diff --git a/vaultpass/__init__.py b/vaultpass/__init__.py index 6c03c08..454c12f 100644 --- a/vaultpass/__init__.py +++ b/vaultpass/__init__.py @@ -232,7 +232,7 @@ class VaultPass(object): lpath = path.split('/') kname = lpath[-1] path = '/'.join(lpath[0:-1]) - self.removeSecretName(kname, path, mount, force = force, destroy = destroy) + self.removeSecretName(kname, path, mount, destroy = destroy) return(handler(**args)) def editSecret(self, path, mount, editor_prog = constants.EDITOR, *args, **kwargs): @@ -458,17 +458,43 @@ class VaultPass(object): exists = self._pathExists(orig_path, mount, is_secret = True) data = {} if exists: + if not force: + _logger.debug('Getting confirmation to update/replace {0} ({1}) on mount {2}'.format(path, + kname, + mount)) + confirmation = self._getConfirm(('Secret name {0} at path {1} on mount {2} exists. ' + 'Overwrite/update? (y/N) ').format(kname, path, mount)) + if not confirmation: + _logger.debug('Confirmation denied; skipping.') + return(None) data = self.getSecret(path, mount, kname = kname) data[kname] = secret self.createSecret(data, path, mount, force = force) return(None) def listSecretNames(self, path, mount, output = None, indent = 4, *args, **kwargs): - pass # TODO + exists = self._pathExists(path, mount) + is_secret = self._pathExists(path, mount, is_secret = True) + if not any((exists, is_secret)): + _logger.error('Invalid path') + _logger.debug('Path {0} on mount {1} is invalid/does not exist.'.format(path, mount)) + raise ValueError('Invalid path') + self.mount.getSecretsTree(path = path, mounts = mount) + outstr = self.mount.printer(path = path, mounts = mount, output = output, indent = indent) + print(outstr) + return(None) - def removeSecretName(self, kname, path, mount, force = False, destroy = False, *args, **kwargs): + def removeSecretName(self, kname, path, mount, destroy = False, *args, **kwargs): # NOTE: this should edit a secret such that it removes a key from the dict at path. - pass # TODO + data = self.getSecret(path, mount) + if kname not in data: + _logger.error('Secret name does not exist') + _logger.debug('Secret name {0} does not exist in {1}:{2}.'.format(kname, mount, path)) + raise ValueError('Secret name does not exist') + del(data[kname]) + # TODO: handle destroy? + self.createSecret(data, path, mount, force = True) + return(data) def searchSecrets(self, pattern, mount, *args, **kwargs): pass # TODO diff --git a/vaultpass/constants.py b/vaultpass/constants.py index 7196ba9..8c042f6 100644 --- a/vaultpass/constants.py +++ b/vaultpass/constants.py @@ -5,6 +5,7 @@ import string NAME = 'VaultPass' VERSION = '0.0.1' SUPPORTED_ENGINES = ('kv1', 'kv2', 'cubbyhole') +# SUPPORTED_OUTPUT_FORMATS = ('pretty', 'yaml', 'json', 'tree') SUPPORTED_OUTPUT_FORMATS = ('pretty', 'yaml', 'json') ALPHA_LOWER_PASS_CHARS = string.ascii_lowercase ALPHA_UPPER_PASS_CHARS = string.ascii_uppercase diff --git a/vaultpass/mounts.py b/vaultpass/mounts.py index 74936a8..a8c0ab8 100644 --- a/vaultpass/mounts.py +++ b/vaultpass/mounts.py @@ -133,21 +133,6 @@ class MountHandler(object): obj = dpath.util.get(self.paths, fullpath, None) return(obj) - def getSecret(self, path, mount, version = None): - if not self.mounts: - self.getSysMounts() - mtype = self.getMountType(mount) - args = {} - handler = None - if mtype == 'cubbyhole': - handler = self.cubbyhandler.read_secret - elif mtype == 'kv1': - handler = self.client.secrets.kv.v1.read_secret - if mtype == 'kv2': - args['version'] = version - data = self.client.secrets - # TODO - def getSecretNames(self, path, mount, version = None): reader = None mtype = self.getMountType(mount) @@ -267,7 +252,7 @@ class MountHandler(object): _logger.debug('Added mountpoint {0} to mounts list with type {1}'.format(mount, mtype)) return(None) - def printer(self, output = None, indent = 4): + def printer(self, path = '/', mounts = None, output = None, indent = 4): # def treePrint(obj, s = 'Password Store\n', level = 0): # prefix = '├──' # leading_prefix = '│' @@ -276,12 +261,10 @@ class MountHandler(object): # return(s) if output: output = output.lower() - if output and output not in ('pretty', 'yaml', 'json'): - # if output and output not in ('pretty', 'yaml', 'json', 'tree'): + if output and output not in constants.SUPPORTED_OUTPUT_FORMATS: _logger.error('Invalid output format') - _logger.debug('The output parameter ("{0}") must be one of: pretty, yaml, json, or None'.format(output)) - # _logger.debug(('The output parameter ("{0}") must be one of: ' - # 'pretty, yaml, json, tree, or None').format(output)) + _logger.debug(('The output parameter ("{0}") must be one of: ' + '{0}, or None').format(output, ', '.join(constants.SUPPORTED_OUTPUT_FORMATS))) raise ValueError('Invalid output format') if output in ('pretty', 'yaml', 'json'): if not any(((indent is None), isinstance(indent, int))): @@ -290,20 +273,25 @@ class MountHandler(object): raise ValueError('indent parameter must be an integer or None') if not self.paths: self.getSecretsTree() + _paths = {} + if not mounts: + mounts = self.mounts.keys() + for m in mounts: + _paths[m] = self.getPath(path, m) if output == 'json': import json - return(json.dumps(self.paths, indent = indent)) + return(json.dumps(_paths, indent = indent)) elif output == 'yaml': import yaml # https://pypi.org/project/PyYAML/ # import pyaml # https://pypi.python.org/pypi/pyaml - return(yaml.dump(self.paths, indent = indent)) + return(yaml.dump(_paths, indent = indent)) elif output == 'pretty': import pprint if indent is None: indent = 1 - return(pprint.pformat(self.paths, indent = indent, width = shutil.get_terminal_size((80, 20)).columns)) + return(pprint.pformat(_paths, indent = indent, width = shutil.get_terminal_size((80, 20)).columns)) # elif output == 'tree': # import tree # TODO? Wayyy later. elif not output: - return(str(self.paths)) + return(str(_paths)) return(None)