Coverage for /home/ken/src/shared/avendesora/avendesora/generator.py : 0%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# Password Generator
# License {{{1 # Copyright (C) 2016 Kenneth S. Kundert # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation, either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program. If not, see http://www.gnu.org/licenses/.
# Imports {{{1 from .account import Account from .config import read_config, get_setting from .dialog import show_list_dialog from .gpg import GnuPG, PythonFile, GPG_EXTENSIONS from .obscure import Hidden from .preferences import ( CONFIG_DEFAULTS, NONCONFIG_SETTINGS, ACCOUNTS_FILE_INITIAL_CONTENTS, CONFIG_FILE_INITIAL_CONTENTS, USER_KEY_FILE_INITIAL_CONTENTS, HASH_FILE_INITIAL_CONTENTS, STEALTH_ACCOUNTS_FILE_INITIAL_CONTENTS, ACCOUNT_LIST_FILE_CONTENTS, ) from .title import Title from .utilities import generate_random_string, validate_componenets from inform import ( debug, Error, notify, log, render, terminate, terminate_if_errors ) from shlib import to_path from pathlib import Path
# PasswordGenerator class{{{1 class PasswordGenerator(object):
# Constructor {{{2 def __init__(self, init=False, gpg_ids=None): # initialize avendesora (these should already be done if called from # main, but it is safe to call them again) read_config() GnuPG.initialize()
# check the integrity of avendesora validate_componenets()
# create the avendesora data directory if init: self.initialize(gpg_ids, init) terminate()
# read the accounts files self.accounts = set() for filename in get_setting('accounts_files', []): try: path = to_path(get_setting('settings_dir'), filename) account_file = PythonFile(path) contents = account_file.run() master_password = contents.get('master_password')
# traverse through all accounts, determine which are new, bind # required information to new accounts, and update account list. for account in Account.all_accounts(): if account not in self.accounts: account.add_fileinfo(master_password, account_file)
# save a copy of account so it is not garbage collected self.accounts.add(account) except Error as err: err.terminate() terminate_if_errors()
# initialize() {{{2 def initialize(self, gpg_ids, filename): # if filename is True, this is being called as part of the Avendesora # 'initialize' command, in which case all missing files should be created. # if filename is a string, this is being called as part of the # Avendesora 'new' command, in which case a single new account file # should be created. def split(s, l=72): # Break long string into a series of adjacent shorter strings if len(s) < l: return '"%s"' % s chunks = [' "%s"' % s[i:i+l] for i in range(0, len(s), l)] return '\n' + '\n'.join(chunks) + '\n'
# Create dictionary of available substitutions for CONTENTS strings fields = {} for key in CONFIG_DEFAULTS: value = get_setting(key, expand=False) value = render(str(value) if isinstance(value, Path) else value) fields.update({key: value}) for key in NONCONFIG_SETTINGS: value = get_setting(key, expand=False) value = render(str(value) if isinstance(value, Path) else value) fields.update({key: value}) gpg_ids = gpg_ids if gpg_ids else get_setting('gpg_ids', []) fields.update({ 'section': '{''{''{''1', 'master_password': split(Hidden.conceal(generate_random_string(72))), 'master_password2': split(Hidden.conceal(generate_random_string(72))), 'user_key': split(Hidden.conceal(generate_random_string(72))), 'gpg_ids': repr(' '.join(gpg_ids)), })
# create the initial versions of the files in the settings directory if filename is True: # Assure that the default initial set of files is present for path, contents in [ (get_setting('config_file'), CONFIG_FILE_INITIAL_CONTENTS), (get_setting('hashes_file'), HASH_FILE_INITIAL_CONTENTS), (get_setting('user_key_file'), USER_KEY_FILE_INITIAL_CONTENTS), (get_setting('default_accounts_file'), ACCOUNTS_FILE_INITIAL_CONTENTS), (get_setting('default_stealth_accounts_file'), STEALTH_ACCOUNTS_FILE_INITIAL_CONTENTS), ]: if path: log('creating initial version.', culprit=path) f = PythonFile(path) f.create(contents.format(**fields), gpg_ids) # create will not overwrite an existing file, instead it # reads the file. else: # Create a new accounts file fields['accounts_files'] = get_setting('accounts_files', []) + [filename] path = to_path(get_setting('settings_dir'), filename) if path.exists(): raise Error('exists.', culprit=path) if path.suffix in GPG_EXTENSIONS and not gpg_ids: raise Error('Must specify GPG IDs.') log('creating accounts file.', culprit=path) f = PythonFile(path) f.create(ACCOUNTS_FILE_INITIAL_CONTENTS.format(**fields), gpg_ids)
# Create a new accounts file path = to_path(get_setting('account_list_file')) if path.suffix in GPG_EXTENSIONS: raise Error('encryption is not supported.', culprit=path) try: log('writing.', culprit=path) path.write_text( ACCOUNT_LIST_FILE_CONTENTS.format(**fields).decode( get_setting('encoding') ) ) except OSError as err: raise Error(os_error(err))
# get_account() {{{2 def get_account(self, name, request_seed=False): if not name: raise Error('no account specified.') for account in self.all_accounts(): if account.matches_exactly(name): account.initialize(request_seed) return account raise Error('not found.', culprit=name)
# discover_account() {{{2 def discover_account(self, title=None, verbose=False): log('Account Discovery ...') if get_setting('verbose'): verbose = True
# get and parse the title data = Title(override=title).get_data()
# sweep through accounts to see if any recognize this title data matches = {} for account in self.all_accounts(): name = account.get_name() if verbose: log('Trying:', name) for key, script in account.recognize(data, verbose): ident = '%s (%s)' % (name, key) if key else name matches[ident] = name, script if verbose: log(' %s matches' % ident)
if not matches: msg = 'cannot find appropriate account.' notify(msg) raise Error(msg) if len(matches) > 1: choice = show_list_dialog(sorted(matches.keys())) if choice: return matches[choice] else: return matches.popitem()[1]
# all_accounts() {{{2 def all_accounts(self): for account in self.accounts: yield account
# find_acounts() {{{2 def find_accounts(self, target): for account in self.all_accounts(): if account.id_contains(target): yield account
# search_acounts() {{{2 def search_accounts(self, target): for account in self.all_accounts(): if account.account_contains(target): yield account
|