def test_select_entry(self): """Test password selection.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, 'foo.gpg')) touch(os.path.join(directory, 'bar.gpg')) touch(os.path.join(directory, 'baz.gpg')) program = PasswordStore(directory=directory) # Substring search. entry = program.select_entry('fo') assert entry.name == 'foo' # Fuzzy search. entry = program.select_entry('bz') assert entry.name == 'baz'
def test_select_entry_interactive(self): """Test interactive password selection.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, 'foo.gpg')) touch(os.path.join(directory, 'bar.gpg')) touch(os.path.join(directory, 'baz.gpg')) # Select entries using the command line filter 'a' and then use # interactive selection to narrow the choice down to 'baz' by # specifying the unique substring 'z'. program = PasswordStore(directory=directory) with CaptureOutput(input='z'): entry = program.select_entry('a') assert entry.name == 'baz'
def test_select_entry(self): """Test password selection.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, "foo.gpg")) touch(os.path.join(directory, "bar.gpg")) touch(os.path.join(directory, "baz.gpg")) program = PasswordStore(directory=directory) # Substring search. entry = program.select_entry("fo") assert entry.name == "foo" # Fuzzy search. entry = program.select_entry("bz") assert entry.name == "baz"
def test_smart_search(self): """Test smart searching.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, 'abcdef.gpg')) touch(os.path.join(directory, 'aabbccddeeff.gpg')) touch(os.path.join(directory, 'Google.gpg')) program = PasswordStore(directory=directory) # Test a substring match that avoids fuzzy matching. matches = program.smart_search('abc') assert len(matches) == 1 assert matches[0].name == 'abcdef' # Test a fuzzy match to confirm that the fall back works. matches = program.smart_search('gg') assert len(matches) == 1 assert matches[0].name == 'Google'
def test_no_matching_password_error(self): """Test the NoMatchingPasswordError exception.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, 'Whatever.gpg')) program = PasswordStore(directory=directory) self.assertRaises(NoMatchingPasswordError, program.smart_search, 'x')
def test_missing_password_store_error(self): """Test the MissingPasswordStoreError exception.""" with TemporaryDirectory() as directory: missing = os.path.join(directory, 'missing') program = PasswordStore(directory=missing) self.assertRaises(MissingPasswordStoreError, program.ensure_directory_exists)
def main(): """Command line interface for the ``qpass`` program.""" # Initialize logging to the terminal. coloredlogs.install() # Prepare for command line argument parsing. action = show_matching_entry program_opts = dict(exclude_list=[]) show_opts = dict(filters=[], use_clipboard=is_clipboard_supported()) verbosity = 0 # Parse the command line arguments. try: options, arguments = getopt.gnu_getopt( sys.argv[1:], "elnp:f:x:vqh", ["edit", "list", "no-clipboard", "password-store=", "filter=", "exclude=", "verbose", "quiet", "help"], ) for option, value in options: if option in ("-e", "--edit"): action = edit_matching_entry elif option in ("-l", "--list"): action = list_matching_entries elif option in ("-n", "--no-clipboard"): show_opts["use_clipboard"] = False elif option in ("-p", "--password-store"): stores = program_opts.setdefault("stores", []) stores.append(PasswordStore(directory=value)) elif option in ("-f", "--filter"): show_opts["filters"].append(value) elif option in ("-x", "--exclude"): program_opts["exclude_list"].append(value) elif option in ("-v", "--verbose"): coloredlogs.increase_verbosity() verbosity += 1 elif option in ("-q", "--quiet"): coloredlogs.decrease_verbosity() verbosity -= 1 elif option in ("-h", "--help"): usage(__doc__) return else: raise Exception("Unhandled option! (programming error)") if not (arguments or action == list_matching_entries): usage(__doc__) return except Exception as e: warning("Error: %s", e) sys.exit(1) # Execute the requested action. try: show_opts["quiet"] = verbosity < 0 kw = show_opts if action == show_matching_entry else {} action(QuickPass(**program_opts), arguments, **kw) except PasswordStoreError as e: # Known issues don't get a traceback. logger.error("%s", e) sys.exit(1) except KeyboardInterrupt: # If the user interrupted an interactive prompt they most likely did so # intentionally, so there's no point in generating more output here. sys.exit(1)
def test_simple_search(self): """Test simple substring searching.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, 'foo.gpg')) touch(os.path.join(directory, 'bar.gpg')) touch(os.path.join(directory, 'baz.gpg')) program = PasswordStore(directory=directory) matches = program.simple_search('fo') assert len(matches) == 1 assert matches[0].name == 'foo' matches = program.simple_search('a') assert len(matches) == 2 assert matches[0].name == 'bar' assert matches[1].name == 'baz' matches = program.simple_search('b', 'z') assert len(matches) == 1 assert matches[0].name == 'baz'
def test_simple_search(self): """Test simple substring searching.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, "foo.gpg")) touch(os.path.join(directory, "bar.gpg")) touch(os.path.join(directory, "baz.gpg")) program = PasswordStore(directory=directory) matches = program.simple_search("fo") assert len(matches) == 1 assert matches[0].name == "foo" matches = program.simple_search("a") assert len(matches) == 2 assert matches[0].name == "bar" assert matches[1].name == "baz" matches = program.simple_search("b", "z") assert len(matches) == 1 assert matches[0].name == "baz"
def main(): """Command line interface for the ``qpass`` program.""" # Initialize logging to the terminal. coloredlogs.install() # Prepare for command line argument parsing. action = show_matching_entry program_opts = dict() show_opts = dict(use_clipboard=is_clipboard_supported()) # Parse the command line arguments. try: options, arguments = getopt.gnu_getopt(sys.argv[1:], 'elnp:vqh', [ 'edit', 'list', 'no-clipboard', 'password-store=', 'verbose', 'quiet', 'help', ]) for option, value in options: if option in ('-e', '--edit'): action = edit_matching_entry elif option in ('-l', '--list'): action = list_matching_entries elif option in ('-n', '--no-clipboard'): show_opts['use_clipboard'] = False elif option in ('-p', '--password-store'): stores = program_opts.setdefault('stores', []) stores.append(PasswordStore(directory=value)) elif option in ('-v', '--verbose'): coloredlogs.increase_verbosity() elif option in ('-q', '--quiet'): coloredlogs.decrease_verbosity() elif option in ('-h', '--help'): usage(__doc__) return else: raise Exception("Unhandled option! (programming error)") if not (arguments or action == list_matching_entries): usage(__doc__) return except Exception as e: warning("Error: %s", e) sys.exit(1) # Execute the requested action. try: action(QuickPass(**program_opts), arguments, **(show_opts if action == show_matching_entry else {})) except PasswordStoreError as e: # Known issues don't get a traceback. logger.error("%s", e) sys.exit(1) except KeyboardInterrupt: # If the user interrupted an interactive prompt they most likely did so # intentionally, so there's no point in generating more output here. sys.exit(1)
def get_secret_from_store(name, directory=None): """ Use :mod:`qpass` to get a secret from ``~/.password-store``. :param name: The name of a password or a search pattern that matches a single entry in the password store (a string). :param directory: The directory to use (a string, defaults to ``~/.password-store``). :returns: The secret (a string). :raises: :exc:`exceptions.ValueError` when the given `name` doesn't match any entries or matches multiple entries in the password store. """ kw = dict(directory=directory) if directory else {} store = PasswordStore(**kw) matches = store.smart_search(name) if len(matches) != 1: msg = "Expected exactly one match in password database! (input: %s)" raise ValueError(format(msg, name)) return matches[0].password
def test_fuzzy_search(self): """Test fuzzy searching.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, 'Personal/Zabbix.gpg')) touch(os.path.join(directory, 'Work/Zabbix.gpg')) touch(os.path.join(directory, 'Something else.gpg')) program = PasswordStore(directory=directory) # Test a fuzzy search with multiple matches. matches = program.fuzzy_search('zbx') assert len(matches) == 2 assert any(entry.name == 'Personal/Zabbix' for entry in matches) assert any(entry.name == 'Work/Zabbix' for entry in matches) # Test a fuzzy search with a single match. matches = program.fuzzy_search('p/z') assert len(matches) == 1 assert matches[0].name == 'Personal/Zabbix' # Test a fuzzy search with `the other' match. matches = program.fuzzy_search('w/z') assert len(matches) == 1 assert matches[0].name == 'Work/Zabbix'
def test_password_discovery(self): """Test password discovery.""" with TemporaryDirectory() as directory: touch(os.path.join(directory, 'foo.gpg')) touch(os.path.join(directory, 'foo/bar.gpg')) touch(os.path.join(directory, 'foo/bar/baz.gpg')) touch(os.path.join(directory, 'Also with spaces.gpg')) program = PasswordStore(directory=directory) assert len(program.entries) == 4 assert program.entries[0].name == 'Also with spaces' assert program.entries[1].name == 'foo' assert program.entries[2].name == 'foo/bar' assert program.entries[3].name == 'foo/bar/baz'
def test_directory_variable(self): """Test support for ``$PASSWORD_STORE_DIR``.""" with TemporaryDirectory() as directory: with PatchedItem(os.environ, DIRECTORY_VARIABLE, directory): program = PasswordStore() assert program.directory == directory
def test_empty_password_store_error(self): """Test the EmptyPasswordStoreError exception.""" with TemporaryDirectory() as directory: program = PasswordStore(directory=directory) self.assertRaises(EmptyPasswordStoreError, program.smart_search)