from os import environ from os.path import expanduser from bundlewrap.exceptions import FaultUnavailable from bundlewrap.utils import Fault from bundlewrap.utils.dicts import merge_dict from bundlewrap.utils.text import bold, yellow from bundlewrap.utils.ui import io from pykeepass import PyKeePass from pykeepass.exceptions import CredentialsError DUMMY_MODE = environ.get('BW_KEEPASS_DUMMY_MODE', '0') == '1' KEEPASS_FILE = environ.get('BW_KEEPASS_FILE', None) KEEPASS_PASS = environ.get('BW_KEEPASS_PASSWORD', None) cache = {} try: with open(expanduser('~/.bw_keepass.cfg'), 'r') as f: cfg = f.read().splitlines() if not KEEPASS_FILE: KEEPASS_FILE = cfg[0].strip() if not KEEPASS_PASS and len(cfg) > 1: KEEPASS_PASS = cfg[1] except (FileNotFoundError, IndexError): pass def _get_contents_from_keepass(path): if isinstance(path, list): list_path = path string_path = '|'.join(path) else: list_path = path.split('|') string_path = path try: return cache[string_path] except KeyError: # Not yet fetched from keepass pass with io.job('{p} looking up "{path}" in "{file}"'.format( p=bold('KeePass'), path=list_path, file=KEEPASS_FILE, )): if not KEEPASS_FILE or not KEEPASS_PASS: raise FaultUnavailable('BW_KEEPASS_FILE and/or BW_KEEPASS_PASSWORD missing') try: keepass = PyKeePass(expanduser(KEEPASS_FILE), KEEPASS_PASS) result = keepass.find_entries_by_path(list_path, first=True) except CredentialsError: raise FaultUnavailable('Your specified BW_KEEPASS_PASSWORD is invalid for use with {}'.format(KEEPASS_FILE)) except Exception as e: raise FaultUnavailable('Exception while trying to get path "{}" from file "{}": {}'.format( list_path, KEEPASS_FILE, repr(e) )) if not result: raise FaultUnavailable('Could not find any entries for path "{}" in file "{}"'.format( list_path, KEEPASS_FILE, )) cache[string_path] = result return cache[string_path] def _password(path): if DUMMY_MODE: return 'KEEPASS DUMMY PASSWORD' else: secret = _get_contents_from_keepass(path) return secret.password def password(path): return Fault( 'bwkeepass password', _password, path=path, ) def _username(path): if DUMMY_MODE: return 'KEEPASS DUMMY USERNAME' else: secret = _get_contents_from_keepass(path) return secret.username def username(path): return Fault( 'bwkeepass username', _username, path=path, ) def _url(path): if DUMMY_MODE: return 'http://KEEPASS.DUMMY.URL' else: secret = _get_contents_from_keepass(path) return secret.url def url(path): return Fault( 'bwkeepass url', _url, path=path, ) def _notes(path): if DUMMY_MODE: return 'KEEPASS DUMMY NOTES' else: secret = _get_contents_from_keepass(path) return secret.notes def notes(path): return Fault( 'bwkeepass notes', _notes, path=path, )