Ejemplo n.º 1
0
    def set_key(self, path, key_data, force=False):
        """Add a key to the store or update an existing one.

        :param str path: The key to write.

        :param str key_data: The data of the key.

        :param bool foce: (optional) If ``True`` path will be
            overwritten if it exists.

        :raises FileExistsError: if a key already exists for path and
            overwrite is ``False``.

        """
        if path is None or path == '':
            return
        path = os.path.normpath(path)

        key_path = os.path.join(self.store_dir, path + '.gpg')
        key_dir = os.path.dirname(key_path)
        if os.path.exists(key_path) and not force:
            raise FileExistsError('An entry already exists for {0}.'
                                  .format(path))

        os.makedirs(os.path.join(self.store_dir, key_dir), exist_ok=True)
        write_key(key_path, key_data, self.gpg_bin, self.gpg_opts)

        git_add_path(self.repo, key_path,
                     'Add given password for {0} to store.'.format(path),
                     verbose=self.verbose)
Ejemplo n.º 2
0
    def set_key(self, path, key_data, force=False):
        """Add a key to the store or update an existing one.

        :param str path: The key to write.

        :param str key_data: The data of the key.

        :param bool foce: (optional) If ``True`` path will be
            overwritten if it exists.

        :raises FileExistsError: if a key already exists for path and
            overwrite is ``False``.

        """
        if path is None or path == '':
            return
        path = os.path.normpath(path)

        key_path = os.path.join(self.store_dir, path + '.gpg')
        key_dir = os.path.dirname(key_path)
        if os.path.exists(key_path) and not force:
            raise FileExistsError(
                'An entry already exists for {0}.'.format(path))

        os.makedirs(os.path.join(self.store_dir, key_dir), exist_ok=True)
        write_key(key_path, key_data, self.gpg_bin, self.gpg_opts)

        git_add_path(self.repo,
                     key_path,
                     'Add given password for {0} to store.'.format(path),
                     verbose=self.verbose)
Ejemplo n.º 3
0
    def gen_key(self, path, length, symbols=True, force=False, inplace=False):
        """Generate a new password for a key.

        :param str path: The path of the key.

        :param int length: The length of the new password.

        :param bool symbols: (optional) If ``True`` non alphanumeric
            characters will also be used in the new password.

        :param bool force: (optional) If ``True`` an existing key at
            `path` will be overwritten.

        :param bool inplace: (optional) If ``True`` only the first
            line of an existing key at `path` will be overwritten with
            the new password.

        """
        if path is None or path == '':
            return None
        path = os.path.normpath(path)
        key_path = os.path.join(self.store_dir, path + '.gpg')
        key_dir = os.path.dirname(key_path)
        if os.path.exists(key_path) and not (force or inplace):
            if self.interactive:
                answer = input('An entry already exists for {0}. '
                               'Overwrite it? [y/N] '.format(path))
                if answer.lower() != 'y':
                    return None
            else:
                raise FileExistsError(
                    'An entry already exists for {0}.'.format(path))

        os.makedirs(os.path.join(self.store_dir, key_dir), exist_ok=True)

        password = gen_password(length, symbols=symbols)
        action = 'Add'
        if not inplace:
            write_key(key_path, password, self.gpg_bin, self.gpg_opts)
            action = 'Add'
        else:
            action = 'Replace'
            key_data = read_key(key_path,
                                gpg_bin=self.gpg_bin,
                                gpg_opts=self.gpg_opts)
            lines = key_data.split('\n')
            lines[0] = password
            write_key(key_path,
                      '\n'.join(lines),
                      gpg_bin=self.gpg_bin,
                      gpg_opts=self.gpg_opts)

        git_add_path(self.repo,
                     key_path,
                     '{0} generated password for {1}.'.format(action, path),
                     verbose=self.verbose)
        return password
Ejemplo n.º 4
0
    def gen_key(self, path, length, symbols=True, force=False,
                inplace=False):
        """Generate a new password for a key.

        :param str path: The path of the key.

        :param int length: The length of the new password.

        :param bool symbols: (optional) If ``True`` non alphanumeric
            characters will also be used in the new password.

        :param bool force: (optional) If ``True`` an existing key at
            `path` will be overwritten.

        :param bool inplace: (optional) If ``True`` only the first
            line of an existing key at `path` will be overwritten with
            the new password.

        """
        if path is None or path == '':
            return None
        path = os.path.normpath(path)
        key_path = os.path.join(self.store_dir, path + '.gpg')
        key_dir = os.path.dirname(key_path)
        if os.path.exists(key_path) and not (force or inplace):
            if self.interactive:
                answer = input('An entry already exists for {0}. '
                               'Overwrite it? [y/N] '.format(path))
                if answer.lower() != 'y':
                    return None
            else:
                raise FileExistsError('An entry already exists for {0}.'
                                      .format(path))

        os.makedirs(os.path.join(self.store_dir, key_dir), exist_ok=True)

        password = gen_password(length, symbols=symbols)
        action = 'Add'
        if not inplace:
            write_key(key_path, password, self.gpg_bin, self.gpg_opts)
            action = 'Add'
        else:
            action = 'Replace'
            key_data = read_key(key_path, gpg_bin=self.gpg_bin,
                                gpg_opts=self.gpg_opts)
            lines = key_data.split('\n')
            lines[0] = password
            write_key(key_path, '\n'.join(lines), gpg_bin=self.gpg_bin,
                      gpg_opts=self.gpg_opts)

        git_add_path(self.repo, key_path,
                     '{0} generated password for {1}.'.format(action, path),
                     verbose=self.verbose)
        return password
Ejemplo n.º 5
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)
Ejemplo n.º 6
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)
Ejemplo n.º 7
0
    def init_git(self):
        """Initialise git for the password store.

        Silently fails if :attr:`passpy.store.Store.repo` is not
        ``None``.

        """
        if self.repo is not None:
            return
        self.repo = git_init(self.store_dir)
        git_add_path(self.repo, self.store_dir,
                     'Add current contents of password store.',
                     verbose=self.verbose)
        attributes_path = os.path.join(self.store_dir, '.gitattributes')
        with open(attributes_path, 'w') as attributes_file:
            attributes_file.write('*.gpg diff=gpg\n')
        git_add_path(self.repo, attributes_path,
                     'Configure git repository for gpg file diff.',
                     verbose=self.verbose)
        git_config(self.repo, '--local', 'diff.gpg.binary', 'true')
        git_config(self.repo, '--local', 'diff.gpg.textconf',
                   '"' + self.gpg_bin + ' -d ' + ' '.join(self.gpg_opts) + '"')
Ejemplo n.º 8
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)
Ejemplo n.º 9
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)