from os import environ from os.path import expanduser from subprocess import check_output, CalledProcessError from bundlewrap.exceptions import FaultUnavailable from bundlewrap.utils import Fault from bundlewrap.utils.text import bold, yellow from bundlewrap.utils.ui import io PASSWORD_STORE_DIR = expanduser(environ.get('BW_PASS_DIR', '~/.password-store')) DUMMY_MODE = environ.get('BW_PASS_DUMMY_MODE', '0') == '1' cache = {} def _get_contents_from_pass(identifier: str): try: return cache[identifier] except KeyError: # Not yet fetched from pass pass with io.job('{p} fetching {identifier}'.format( p=bold('pass'), identifier=identifier, )): try: pass_output = check_output( ['pass', 'show', identifier], env={ 'PASSWORD_STORE_DIR': PASSWORD_STORE_DIR, } ).decode('UTF-8').splitlines() except FileNotFoundError: raise FaultUnavailable('pass not found') except CalledProcessError as e: raise FaultUnavailable('pass exited {} when trying to get secret "{}"'.format( e.returncode, identifier, )) cache[identifier] = {} cache[identifier]['password'] = pass_output[0] # TODO import all other options set in pass return cache[identifier] def _password(identifier): if DUMMY_MODE: return 'PASS DUMMY PASSWORD' else: secret = _get_contents_from_pass(identifier) return secret['password'] def password(identifier): return Fault( 'bwpass password', _password, identifier=identifier, )