just need to bind args to funcs and finish docs. meat is done.
This commit is contained in:
		
							parent
							
								
									018384dfce
								
							
						
					
					
						commit
						606196a022
					
				
							
								
								
									
										5
									
								
								pass.py
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								pass.py
									
									
									
									
									
								
							| @ -15,8 +15,9 @@ def main(): | |||||||
|     if args.oper == 'version': |     if args.oper == 'version': | ||||||
|         print('{0} {1}'.format(vaultpass.constants.NAME, |         print('{0} {1}'.format(vaultpass.constants.NAME, | ||||||
|                                vaultpass.constants.VERSION)) |                                vaultpass.constants.VERSION)) | ||||||
|     import pprint |     args.initialize = (True if args.oper == 'init' else False) | ||||||
|     pprint.pprint(vars(args)) |     args.verify_cfg = (True if args.oper == 'verify' else False) | ||||||
|  |     vp = vaultpass.VaultPass(**vars(args)) | ||||||
|     return(None) |     return(None) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1 +0,0 @@ | |||||||
| ../vaultpass |  | ||||||
| @ -7,10 +7,10 @@ _logger = logging.getLogger() | |||||||
| try: | try: | ||||||
|     import qrcode |     import qrcode | ||||||
|     has_qrcode = True |     has_qrcode = True | ||||||
|     _logger.warning(('Could not import qrcode; ' |  | ||||||
|                      'library required for QR code generation')) |  | ||||||
| except ImportError: | except ImportError: | ||||||
|     has_qrcode = False |     has_qrcode = False | ||||||
|  |     _logger.warning(('Could not import qrcode; ' | ||||||
|  |                      'library required for QR code generation')) | ||||||
| try: | try: | ||||||
|     import qrcode.image.svg |     import qrcode.image.svg | ||||||
|     has_qrcode_svg = True |     has_qrcode_svg = True | ||||||
|  | |||||||
| @ -2,6 +2,8 @@ import getpass | |||||||
| import logging | import logging | ||||||
| import tempfile | import tempfile | ||||||
| import os | import os | ||||||
|  | import pathlib | ||||||
|  | import re | ||||||
| import subprocess | import subprocess | ||||||
| import sys | import sys | ||||||
| import time | import time | ||||||
| @ -19,7 +21,6 @@ from . import constants | |||||||
| from . import editor | from . import editor | ||||||
| from . import gpg_handler | from . import gpg_handler | ||||||
| from . import mounts | from . import mounts | ||||||
| from . import pass_import |  | ||||||
| from . import pwgen | from . import pwgen | ||||||
| from . import QR | from . import QR | ||||||
| 
 | 
 | ||||||
| @ -30,9 +31,22 @@ class VaultPass(object): | |||||||
|     uri = None |     uri = None | ||||||
|     mount = None |     mount = None | ||||||
| 
 | 
 | ||||||
|     def __init__(self, initialize = False, cfg = '~/.config/vaultpass.xml'): |     def __init__(self, | ||||||
|  |                  initialize = False, | ||||||
|  |                  cfg = '~/.config/vaultpass.xml', | ||||||
|  |                  verify_cfg = True, | ||||||
|  |                  loglevel = constants.DEFAULT_LOGLEVEL, | ||||||
|  |                  *args, | ||||||
|  |                  **kwargs): | ||||||
|  |         rootlogger = logging.getLogger() | ||||||
|  |         if loglevel != constants.DEFAULT_LOGLEVEL: | ||||||
|  |             if not isinstance(loglevel, int): | ||||||
|  |                 # We need to convert it from the name to the int. | ||||||
|  |                 loglevel = getattr(logging, loglevel.upper()) | ||||||
|  |         if loglevel != constants.DEFAULT_LOGLEVEL:  # And again in case we transformed it above. | ||||||
|  |             rootlogger.setLevel(loglevel) | ||||||
|         self.initialize = initialize |         self.initialize = initialize | ||||||
|         self.cfg = config.getConfig(cfg) |         self.cfg = config.getConfig(cfg, validate = verify_cfg) | ||||||
|         self._getURI() |         self._getURI() | ||||||
|         self.getClient() |         self.getClient() | ||||||
|         if not self.initialize: |         if not self.initialize: | ||||||
| @ -140,8 +154,39 @@ class VaultPass(object): | |||||||
|                 force = False, |                 force = False, | ||||||
|                 gpghome = constants.GPG_HOMEDIR, |                 gpghome = constants.GPG_HOMEDIR, | ||||||
|                 pass_dir = constants.PASS_DIR, |                 pass_dir = constants.PASS_DIR, | ||||||
|  |                 flat = False, | ||||||
|                 *args, **kwargs): |                 *args, **kwargs): | ||||||
|         pass  # TODO |         pass_dir = os.path.abspath(os.path.expanduser(pass_dir)) | ||||||
|  |         gpg = gpg_handler.GPG(home = gpghome) | ||||||
|  |         kname_re = re.compile(r'^(?P<kname>[^/]+)\.(gpg|asc)$') | ||||||
|  |         for root, dirs, files in os.walk(pass_dir): | ||||||
|  |             rel_root = pathlib.Path(root).relative_to(pass_dir) | ||||||
|  |             for f in files: | ||||||
|  |                 r = kname_re.search(f) | ||||||
|  |                 if not r: | ||||||
|  |                     continue | ||||||
|  |                 kname = r.groupdict()['kname'] | ||||||
|  |                 dcryptdata = gpg.decrypt(os.path.join(root, f)).decode('utf-8') | ||||||
|  |                 if flat: | ||||||
|  |                     path = os.path.dirname(rel_root) | ||||||
|  |                     data = {kname: dcryptdata} | ||||||
|  |                     self.createSecret(data, path, mount, force = force) | ||||||
|  |                 else: | ||||||
|  |                     data = {} | ||||||
|  |                     k = None | ||||||
|  |                     v = '' | ||||||
|  |                     for line in dcryptdata.splitlines(): | ||||||
|  |                         l = [i.strip() for i in line.split(':', 1) if i.strip() != ''] | ||||||
|  |                         if len(l) == 1: | ||||||
|  |                             v += '\n{0}'.format(l[0]) | ||||||
|  |                         elif len(l) == 0: | ||||||
|  |                             continue | ||||||
|  |                         else: | ||||||
|  |                             data[k] = v | ||||||
|  |                             k = l[0] | ||||||
|  |                             v = l[1] | ||||||
|  |                     self.createSecret(data, path = '/'.join((rel_root, kname)), mount = mount, force = force) | ||||||
|  |         return(None) | ||||||
| 
 | 
 | ||||||
|     def copySecret(self, oldpath, newpath, mount, newmount = None, force = False, remove_old = False, *args, **kwargs): |     def copySecret(self, oldpath, newpath, mount, newmount = None, force = False, remove_old = False, *args, **kwargs): | ||||||
|         mtype = self.mount.getMountType(mount) |         mtype = self.mount.getMountType(mount) | ||||||
| @ -497,7 +542,22 @@ class VaultPass(object): | |||||||
|         return(data) |         return(data) | ||||||
| 
 | 
 | ||||||
|     def searchSecrets(self, pattern, mount, *args, **kwargs): |     def searchSecrets(self, pattern, mount, *args, **kwargs): | ||||||
|         pass  # TODO |         print('This may take a while...') | ||||||
|  |         ptrn = re.compile(pattern) | ||||||
|  |         self.mount.getSecretsTree(mounts = mount) | ||||||
|  |         for p in self.mount.flatpaths: | ||||||
|  |             data = self.getSecret(p, mount) | ||||||
|  |             if data: | ||||||
|  |                 for k, v in data.items(): | ||||||
|  |                     if ptrn.search(v): | ||||||
|  |                         print('/'.join((mount, p, k))) | ||||||
|  |         return(None) | ||||||
| 
 | 
 | ||||||
|     def searchSecretNames(self, pattern, mount, *args, **kwargs): |     def searchSecretNames(self, pattern, mount, *args, **kwargs): | ||||||
|         pass  # TODO |         ptrn = re.compile(pattern) | ||||||
|  |         self.mount.getSecretsTree(mounts = mount) | ||||||
|  |         for p in self.mount.flatpaths: | ||||||
|  |             n = p.split('/')[-1] | ||||||
|  |             if ptrn.search(n): | ||||||
|  |                 print(p) | ||||||
|  |         return(None) | ||||||
|  | |||||||
| @ -14,12 +14,25 @@ def parseArgs(): | |||||||
|                       action = 'version', |                       action = 'version', | ||||||
|                       version = '{0} {1}'.format(constants.NAME, constants.VERSION)) |                       version = '{0} {1}'.format(constants.NAME, constants.VERSION)) | ||||||
|     args.add_argument('-c', '--config', |     args.add_argument('-c', '--config', | ||||||
|  |                       dest = 'cfg', | ||||||
|                       default = '~/.config/vaultpass.xml', |                       default = '~/.config/vaultpass.xml', | ||||||
|                       help = ('The path to your configuration file. Default: ~/.config/vaultpass.xml')) |                       help = ('The path to your configuration file. Default: ~/.config/vaultpass.xml')) | ||||||
|  |     args.add_argument('-l', '--loglevel', | ||||||
|  |                       dest = 'loglevel', | ||||||
|  |                       default = constants.DEFAULT_LOGLEVEL, | ||||||
|  |                       help = ('The log level. Default: {0}').format(constants.DEFAULT_LOGLEVEL_NAME)) | ||||||
|  |     # I can't get this to change in the logger root. TODO. | ||||||
|  |     # args.add_argument('-L', '--logfile', | ||||||
|  |     #                   dest = 'logfile', | ||||||
|  |     #                   default = constants.DEFAULT_LOGFILE, | ||||||
|  |     #                   help = ('The file to use for logging. ' | ||||||
|  |     #                           'Default: {0}').format(constants.DEFAULT_LOGFILE)) | ||||||
|     args.add_argument('-m', '--mount', |     args.add_argument('-m', '--mount', | ||||||
|                       dest = 'mount', |                       dest = 'mount', | ||||||
|                       default = 'secret', |                       default = constants.SELECTED_DEFAULT_MOUNT, | ||||||
|                       help = ('The mount to use in OPERATION. If not specified, assume a mount named "secret"')) |                       help = (('The mount to use in OPERATION. ' | ||||||
|  |                                'If not specified, assume a mount named ' | ||||||
|  |                                '"{0}"').format(constants.SELECTED_DEFAULT_MOUNT))) | ||||||
|     # I wish argparse supported default subcommands. It doesn't as of python 3.8. |     # I wish argparse supported default subcommands. It doesn't as of python 3.8. | ||||||
|     subparser = args.add_subparsers(help = ('Operation to perform'), |     subparser = args.add_subparsers(help = ('Operation to perform'), | ||||||
|                                     metavar = 'OPERATION', |                                     metavar = 'OPERATION', | ||||||
| @ -79,6 +92,9 @@ def parseArgs(): | |||||||
|     importvault = subparser.add_parser('import', |     importvault = subparser.add_parser('import', | ||||||
|                                        description = ('Import your existing Pass into Vault'), |                                        description = ('Import your existing Pass into Vault'), | ||||||
|                                        help = ('Import your existing Pass into Vault')) |                                        help = ('Import your existing Pass into Vault')) | ||||||
|  |     verify = subparser.add_parser('verify', | ||||||
|  |                                   description = ('Verify the validity and syntax of your configuration file'), | ||||||
|  |                                   help = ('Verify the validity and syntax of your configuration file')) | ||||||
|     # CP/COPY |     # CP/COPY | ||||||
|     # vp.copySecret() |     # vp.copySecret() | ||||||
|     cp.add_argument('-f', '--force', |     cp.add_argument('-f', '--force', | ||||||
| @ -460,10 +476,16 @@ def parseArgs(): | |||||||
|     importvault.add_argument('-f', '--force', |     importvault.add_argument('-f', '--force', | ||||||
|                              dest = 'force', |                              dest = 'force', | ||||||
|                              action = 'store_true', |                              action = 'store_true', | ||||||
|                              help = ('If specified, overwrite the destination in Vault.')) |                              help = ('If specified, overwrite the destination in Vault')) | ||||||
|  |     importvault.add_argument('-F', '--flat', | ||||||
|  |                              action = 'store_true', | ||||||
|  |                              help = ('Being that this is already a very tenuous process, this allows a bit more ' | ||||||
|  |                                      'flexibility - passing -F/--flat indicates that the content itself rather than ' | ||||||
|  |                                      'the path is significant (see the README for more information)')) | ||||||
|     importvault.add_argument('mount', |     importvault.add_argument('mount', | ||||||
|                              metavar = 'MOUNT_NAME', |                              metavar = 'MOUNT_NAME', | ||||||
|                              help = 'The mount name in Vault to import into (Pass\' hierarchy will be recreated). ' |                              help = 'The mount name in Vault to import into (Pass\' hierarchy will be recreated). ' | ||||||
|                                     'This mount MUST exist first and MUST be KV2 if auth is provided that does not ' |                                     'This mount MUST exist first and MUST be KV2 if auth is provided that does not ' | ||||||
|                                     'have CREATE access on /sys/mounts!') |                                     'have CREATE access on /sys/mounts!') | ||||||
|  |     # VERIFY has no args. | ||||||
|     return(args) |     return(args) | ||||||
|  | |||||||
| @ -345,5 +345,5 @@ def getConfig(cfg_ref, validate = True, populate_defaults = True, xsd_path = Non | |||||||
|                 break |                 break | ||||||
|     if cfgobj: |     if cfgobj: | ||||||
|         _logger.info('Parsing configuration.') |         _logger.info('Parsing configuration.') | ||||||
|         cfgobj.main(validate = validate, populate_defaults = populate_defaults) |         cfgobj.main(validate = validate, populate_defaults = (populate_defaults if validate else False)) | ||||||
|     return(cfgobj) |     return(cfgobj) | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import os | import os | ||||||
|  | import logging | ||||||
| import string | import string | ||||||
| 
 | 
 | ||||||
| # These are static. | # These are static. | ||||||
| @ -7,6 +8,10 @@ VERSION = '0.0.1' | |||||||
| SUPPORTED_ENGINES = ('kv1', 'kv2', 'cubbyhole') | SUPPORTED_ENGINES = ('kv1', 'kv2', 'cubbyhole') | ||||||
| # SUPPORTED_OUTPUT_FORMATS = ('pretty', 'yaml', 'json', 'tree') | # SUPPORTED_OUTPUT_FORMATS = ('pretty', 'yaml', 'json', 'tree') | ||||||
| SUPPORTED_OUTPUT_FORMATS = ('pretty', 'yaml', 'json') | SUPPORTED_OUTPUT_FORMATS = ('pretty', 'yaml', 'json') | ||||||
|  | DEFAULT_LOGFILE = os.path.abspath(os.path.expanduser('~/.cache/vaultpass/vaultpass.log')) | ||||||
|  | DEFAULT_LOGLEVEL_NAME = 'WARNING' | ||||||
|  | DEFAULT_LOGLEVEL = getattr(logging, DEFAULT_LOGLEVEL_NAME) | ||||||
|  | DEFAULT_MOUNT = 'secret' | ||||||
| ALPHA_LOWER_PASS_CHARS = string.ascii_lowercase | ALPHA_LOWER_PASS_CHARS = string.ascii_lowercase | ||||||
| ALPHA_UPPER_PASS_CHARS = string.ascii_uppercase | ALPHA_UPPER_PASS_CHARS = string.ascii_uppercase | ||||||
| ALPHA_PASS_CHARS = ALPHA_LOWER_PASS_CHARS + ALPHA_UPPER_PASS_CHARS | ALPHA_PASS_CHARS = ALPHA_LOWER_PASS_CHARS + ALPHA_UPPER_PASS_CHARS | ||||||
| @ -39,6 +44,7 @@ if not os.environ.get('NO_VAULTPASS_ENVS'): | |||||||
|     EDITOR = os.environ.get('EDITOR', EDITOR) |     EDITOR = os.environ.get('EDITOR', EDITOR) | ||||||
|     SELECTED_GPG_HOMEDIR = os.environ.get('GNUPGHOME', GPG_HOMEDIR) |     SELECTED_GPG_HOMEDIR = os.environ.get('GNUPGHOME', GPG_HOMEDIR) | ||||||
|     PASS_DIR = os.environ.get('PASSWORD_STORE_DIR', PASS_DIR) |     PASS_DIR = os.environ.get('PASSWORD_STORE_DIR', PASS_DIR) | ||||||
|  |     SELECTED_DEFAULT_MOUNT = os.environ.get('VAULTPASS_DEFMNT', DEFAULT_MOUNT) | ||||||
| 
 | 
 | ||||||
| # These are made more sane. | # These are made more sane. | ||||||
| PASS_DIR = os.path.abspath(os.path.expanduser(PASS_DIR)) | PASS_DIR = os.path.abspath(os.path.expanduser(PASS_DIR)) | ||||||
|  | |||||||
| @ -2,6 +2,8 @@ import logging | |||||||
| import logging.handlers | import logging.handlers | ||||||
| import os | import os | ||||||
| ## | ## | ||||||
|  | from . import constants | ||||||
|  | ## | ||||||
| try: | try: | ||||||
|     # https://www.freedesktop.org/software/systemd/python-systemd/journal.html#journalhandler-class |     # https://www.freedesktop.org/software/systemd/python-systemd/journal.html#journalhandler-class | ||||||
|     from systemd import journal |     from systemd import journal | ||||||
| @ -10,23 +12,20 @@ except ImportError: | |||||||
|     _has_journald = False |     _has_journald = False | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| logfile = os.path.abspath(os.path.expanduser('~/.cache/vaultpass/vaultpass.log')) | def prepLogfile(path = constants.DEFAULT_LOGFILE): | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def prepLogfile(path = logfile): |  | ||||||
|     path = os.path.abspath(os.path.expanduser(path)) |     path = os.path.abspath(os.path.expanduser(path)) | ||||||
|     # Set up the permissions beforehand. |     # Set up the permissions beforehand. | ||||||
|     os.makedirs(os.path.dirname(logfile), exist_ok = True, mode = 0o0700) |     os.makedirs(os.path.dirname(path), exist_ok = True, mode = 0o0700) | ||||||
|     if not os.path.isfile(path): |     if not os.path.isfile(path): | ||||||
|         # "Touch" it so the next command doesn't fail. |         # "Touch" it so the next command doesn't fail. | ||||||
|         with open(path, 'w') as fh: |         with open(path, 'w') as fh: | ||||||
|             fh.write('') |             fh.write('') | ||||||
|     os.chmod(logfile, 0o0600) |     os.chmod(path, 0o0600) | ||||||
|     return(path) |     return(path) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| _cfg_args = {'handlers': [], | _cfg_args = {'handlers': [], | ||||||
|              'level': logging.DEBUG}  # TEMPORARY FOR TESTING |              'level': constants.DEFAULT_LOGLEVEL} | ||||||
| if _has_journald: | if _has_journald: | ||||||
|     # There were some weird changes somewhere along the line. |     # There were some weird changes somewhere along the line. | ||||||
|     try: |     try: | ||||||
|  | |||||||
| @ -81,6 +81,7 @@ class MountHandler(object): | |||||||
|         self.xml = mounts_xml |         self.xml = mounts_xml | ||||||
|         self.mounts = {} |         self.mounts = {} | ||||||
|         self.paths = {} |         self.paths = {} | ||||||
|  |         self.flatpaths = set() | ||||||
|         self.getSysMounts() |         self.getSysMounts() | ||||||
| 
 | 
 | ||||||
|     def createMount(self, mount_name, mount_type = 'kv2'): |     def createMount(self, mount_name, mount_type = 'kv2'): | ||||||
| @ -184,6 +185,9 @@ class MountHandler(object): | |||||||
|                     _logger.debug('The version parameter ({0}) must be an integer or None'.format(version)) |                     _logger.debug('The version parameter ({0}) must be an integer or None'.format(version)) | ||||||
|                     raise ValueError('version parameter must be an integer or None') |                     raise ValueError('version parameter must be an integer or None') | ||||||
|                 handler = self.client.secrets.kv.v2 |                 handler = self.client.secrets.kv.v2 | ||||||
|  |             self.flatpaths.add(mount) | ||||||
|  |             flatpath = path.rstrip('/') | ||||||
|  |             self.flatpaths.add('/'.join((mount, flatpath))) | ||||||
|             if mount not in self.paths.keys(): |             if mount not in self.paths.keys(): | ||||||
|                 self.paths[mount] = {} |                 self.paths[mount] = {} | ||||||
|             try: |             try: | ||||||
|  | |||||||
| @ -1 +0,0 @@ | |||||||
| import os |  | ||||||
		Reference in New Issue
	
	Block a user