def _copy_move_path(self, old_path, new_path, force=False, move=False): """Copies or moves a key or directory within the password store. :param str old_path: The current path of the key or directory. :param str new_path: The new path of the key or directory. If `new_path` ends in a trailing '/' it will always be treated as a directory. :param bool force: If ``True`` any existing key or directory at `new_path` will be overwritten. :param bool move: If ``True`` the key or directory will be moved. If ``False`` the key or directory will be copied instead. """ old_path = os.path.normpath(old_path) new_path = os.path.normpath(new_path) old_path_full = os.path.join(self.store_dir, old_path) new_path_full = os.path.join(self.store_dir, new_path) if not os.path.isdir(old_path_full): old_path_full += '.gpg' if not (os.path.isdir(new_path_full) or new_path_full.endswith('/')): new_path_full += '.gpg' new_path_full = copy_move(old_path_full, new_path_full, force, move, self.interactive, self.verbose) if new_path_full is None: return if os.path.exists(new_path_full): reencrypt_path(new_path_full, gpg_bin=self.gpg_bin, gpg_opts=self.gpg_opts) action = 'Copy' if move: action = 'Rename' shutil.rmtree(old_path_full, ignore_errors=True) if not os.path.exists(old_path_full): git_remove_path(self.repo, old_path_full, '', recursive=True, commit=False) git_add_path(self.repo, new_path_full, '{0} {1} to {2}.'.format(action, old_path, new_path), verbose=self.verbose)
def _copy_move_path(self, old_path, new_path, force=False, move=False): """Copies or moves a key or directory within the password store. :param str old_path: The current path of the key or directory. :param str new_path: The new path of the key or directory. If `new_path` ends in a trailing '/' it will always be treated as a directory. :param bool force: If ``True`` any existing key or directory at `new_path` will be overwritten. :param bool move: If ``True`` the key or directory will be moved. If ``False`` the key or directory will be copied instead. """ old_path = os.path.normpath(old_path) new_path = os.path.normpath(new_path) old_path_full = os.path.join(self.store_dir, old_path) new_path_full = os.path.join(self.store_dir, new_path) if not os.path.isdir(old_path_full): old_path_full += '.gpg' if not (os.path.isdir(new_path_full) or new_path_full.endswith('/')): new_path_full += '.gpg' new_path_full = copy_move(old_path_full, new_path_full, force, move, self.interactive, self.verbose) if new_path_full is None: return if os.path.exists(new_path_full): reencrypt_path(new_path_full, gpg_bin=self.gpg_bin, gpg_opts=self.gpg_opts) action = 'Copy' if move: action = 'Rename' shutil.rmtree(old_path_full, ignore_errors=True) if not os.path.exists(old_path_full): git_remove_path(self.repo, old_path_full, '', recursive=True, commit=False) git_add_path(self.repo, new_path_full, '{0} {1} to {2}.' .format(action, old_path, new_path), verbose=self.verbose)
def remove_path(self, path, recursive=False, force=False): """Removes the given key or directory from the store. :param str path: The key or directory to remove. Use '' to delete the whole store. :param bool recursive: (optional) Set to ``True`` if nonempty directories should be removed. :param bool force: (optional) If ``True`` the user will never be prompted for deleting a file or directory, even if :attr:`passpy.store.Store.interactive` is set. """ key_path = os.path.join(self.store_dir, path) key_path = os.path.normpath(key_path) if os.path.isdir(key_path): if self.interactive and not force: answer = input('Really delete {0}? [y/N] '.format(path)) if answer.lower() != 'y': return if recursive: shutil.rmtree(key_path) else: os.rmdir(key_path) else: key_path += '.gpg' if not os.path.isfile(key_path): raise FileNotFoundError( '{0} is not in the password store.'.format(path)) if self.interactive and not force: answer = input('Really delete {0}? [y/N] '.format(path)) if answer.lower() != 'y': return os.remove(key_path) if self.verbose: print('removed {0}'.format(path)) if not os.path.exists(key_path): git_remove_path(self.repo, key_path, 'Remove {0} from store.'.format(path), recursive=recursive, verbose=self.verbose)
def remove_path(self, path, recursive=False, force=False): """Removes the given key or directory from the store. :param str path: The key or directory to remove. Use '' to delete the whole store. :param bool recursive: (optional) Set to ``True`` if nonempty directories should be removed. :param bool force: (optional) If ``True`` the user will never be prompted for deleting a file or directory, even if :attr:`passpy.store.Store.interactive` is set. """ key_path = os.path.join(self.store_dir, path) key_path = os.path.normpath(key_path) if os.path.isdir(key_path): if self.interactive and not force: answer = input('Really delete {0}? [y/N] '.format(path)) if answer.lower() != 'y': return if recursive: shutil.rmtree(key_path) else: os.rmdir(key_path) else: key_path += '.gpg' if not os.path.isfile(key_path): raise FileNotFoundError('{0} is not in the password store.' .format(path)) if self.interactive and not force: answer = input('Really delete {0}? [y/N] '.format(path)) if answer.lower() != 'y': return os.remove(key_path) if self.verbose: print('removed {0}'.format(path)) if not os.path.exists(key_path): git_remove_path(self.repo, key_path, 'Remove {0} from store.'.format(path), recursive=recursive, verbose=self.verbose)
def init_store(self, gpg_ids, path=None): """Initialise the password store or a subdirectory with the gpg ids. :param list gpg_ids: The list of gpg ids to encrypt the password store with. If the list is empty, the current gpg id will be removed from the directory in path or root, if path is None. :param str path: (optional) If given, the gpg ids will only be set for the given directory. The path is relative to :attr:`passpy.store.Store.store_dir`. :raises ValueError: if the there is a problem with `path`. :raises FileExistsError: if :attr:`passpy.store.Store.store_dir` already exists and is a file. :raises FileNotFoundError: if the current gpg id should be deleted, but none exists. :raises OSError: if the directories in path do not exist and can't be created. """ if path is None: path = self.store_dir else: path = os.path.normpath(os.path.join(self.store_dir, path)) if os.path.exists(path): if not os.path.isdir(path): raise FileExistsError( '{0} exists but is not a directory.'.format(path)) # Ensure that gpg_ids is a list so that the later .join does # not accidentally join single letters of a string. if gpg_ids is not None and not isinstance(gpg_ids, list): gpg_ids = [gpg_ids] gpg_id_path = os.path.join(path, '.gpg-id') # Delete current gpg id. if gpg_ids is None or len(gpg_ids) == 0: if not os.path.isfile(gpg_id_path): raise FileNotFoundError( ('{0} does not exist and so' 'cannot be removed.').format(gpg_id_path)) os.remove(gpg_id_path) git_remove_path(self.repo, [gpg_id_path], 'Deinitialize {0}.'.format(gpg_id_path), recursive=True, verbose=self.verbose) # The password store should not contain any empty directories, # so we try to remove as many directories as we can. Any # nonempty ones will throw an error and will not be # removed. shutil.rmtree(path, ignore_errors=True) else: os.makedirs(path) # pass needs the gpg id file to be newline terminated. with open(gpg_id_path, 'w') as gpg_id_file: gpg_id_file.write('\n'.join(gpg_ids)) gpg_id_file.write('\n') git_add_path(self.repo, gpg_id_path, 'Set GPG id to {0}.'.format(', '.join(gpg_ids)), verbose=self.verbose) reencrypt_path(path, gpg_bin=self.gpg_bin, gpg_opts=self.gpg_opts) git_add_path(self.repo, path, 'Reencrypt password store using new GPG id {0}.'.format( ', '.join(gpg_ids)), verbose=self.verbose)
def init_store(self, gpg_ids, path=None): """Initialise the password store or a subdirectory with the gpg ids. :param list gpg_ids: The list of gpg ids to encrypt the password store with. If the list is empty, the current gpg id will be removed from the directory in path or root, if path is None. :param str path: (optional) If given, the gpg ids will only be set for the given directory. The path is relative to :attr:`passpy.store.Store.store_dir`. :raises ValueError: if the there is a problem with `path`. :raises FileExistsError: if :attr:`passpy.store.Store.store_dir` already exists and is a file. :raises FileNotFoundError: if the current gpg id should be deleted, but none exists. :raises OSError: if the directories in path do not exist and can't be created. """ if path is not None: path = os.path.normpath(path) if not os.path.isdir(path) and os.path.exists(path): raise FileExistsError('{0}/{1} exists but is not a directory.' .format(self.store_dir, path)) else: path = self.store_dir # Ensure that gpg_ids is a list so that the later .join does # not accidentally join single letters of a string. if gpg_ids is not None and not isinstance(gpg_ids, list): gpg_ids = [gpg_ids] gpg_id_dir = os.path.join(self.store_dir, path) gpg_id_path = os.path.join(gpg_id_dir, '.gpg-id') # Delete current gpg id. if gpg_ids is None or len(gpg_ids) == 0: if not os.path.isfile(gpg_id_path): raise FileNotFoundError(('{0} does not exist and so' 'cannot be removed.') .format(gpg_id_path)) os.remove(gpg_id_path) git_remove_path(self.repo, [gpg_id_path], 'Deinitialize {0}.'.format(gpg_id_path), recursive=True, verbose=self.verbose) # The password store should not contain any empty directories, # so we try to remove as many directories as we can. Any # nonempty ones will throw an error and will not be # removed. shutil.rmtree(gpg_id_dir, ignore_errors=True) else: os.makedirs(gpg_id_dir) # pass needs the gpg id file to be newline terminated. with open(gpg_id_path, 'w') as gpg_id_file: gpg_id_file.write('\n'.join(gpg_ids)) gpg_id_file.write('\n') git_add_path(self.repo, gpg_id_path, 'Set GPG id to {0}.' .format(', '.join(gpg_ids)), verbose=self.verbose) reencrypt_path(gpg_id_dir, gpg_bin=self.gpg_bin, gpg_opts=self.gpg_opts) git_add_path(self.repo, gpg_id_dir, 'Reencrypt password store using new GPG id {0}.' .format(', '.join(gpg_ids)), verbose=self.verbose)