Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
    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)