def test_list_credentials(tmpdir, capsys): with patch("hop.auth.load_auth", MagicMock(return_value=[])): auth.list_credentials() captured = capsys.readouterr() assert len(captured.out) == 0 # lie about output going to a TTY to check more user-friendly message used then with patch("hop.auth.load_auth", MagicMock(return_value=[])), \ patch("os.isatty", MagicMock(return_value=True)): auth.list_credentials() captured = capsys.readouterr() assert "No credentials" in captured.out short_cred = auth.Auth("user1", "pass1") long_cred = auth.Auth("user2", "pass2", "host2") with patch("hop.auth.load_auth", MagicMock(return_value=[short_cred])): auth.list_credentials() captured = capsys.readouterr() assert short_cred.username in captured.out assert short_cred.password not in captured.out assert "for" not in captured.out with patch("hop.auth.load_auth", MagicMock(return_value=[short_cred, long_cred])): auth.list_credentials() captured = capsys.readouterr() assert short_cred.username in captured.out assert long_cred.username in captured.out assert long_cred.password not in captured.out assert long_cred.hostname in captured.out
def test_auth_ca_location(): a = auth.Auth("foo", "bar", ssl=False) assert not a.ssl assert a.ssl_ca_location is None a = auth.Auth("foo", "bar", ssl_ca_location="foo/bar") assert a.ssl assert a.ssl_ca_location == "foo/bar"
def test_auth_protocol(): a = auth.Auth("foo", "bar") # use default security assert a.ssl assert a.protocol == "SASL_SSL" a = auth.Auth("foo", "bar", ssl=False) assert not a.ssl assert a.protocol == "SASL_PLAINTEXT"
def test_delete_credential_ambiguous_creds_without_hosts(tmpdir): creds = [auth.Auth("user1", "pass1"), auth.Auth("user1", "pass2")] with temp_environ(HOME=str(tmpdir)), \ patch("hop.auth.load_auth", MagicMock(return_value=creds)), \ pytest.raises(RuntimeError) as err: auth.delete_credential("user1") assert "Ambiguous credentials found" in err.value.args[0] assert creds[0].username in err.value.args[0]
def test_add_credential_to_nonempty(auth_config, tmpdir): old_cred = auth.Auth("username", "password") new_cred = auth.Auth("other_user", "other_pass") with temp_config(tmpdir, auth_config) as config_dir, temp_environ(HOME=config_dir), \ patch("hop.auth.read_new_credential", MagicMock(return_value=new_cred)): args = MagicMock() args.cred_file = None args.force = False auth.add_credential(args) check_credential_file(configure.get_config_path("auth"), old_cred) check_credential_file(configure.get_config_path("auth"), new_cred)
def test_add_credential_to_nonempty_hostname_no_conflict(tmpdir): # unfortunately, we must permit duplicate usernames if one has a hostname and the other does not old_cred = auth.Auth("username", "password") new_cred = auth.Auth("username", "other_pass", "example.com") with temp_environ(HOME=str(tmpdir)), \ patch("hop.auth.load_auth", MagicMock(return_value=[old_cred])), \ patch("hop.auth.read_new_credential", MagicMock(return_value=new_cred)): args = MagicMock() args.cred_file = None args.force = False auth.add_credential(args) check_credential_file(configure.get_config_path("auth"), old_cred) check_credential_file(configure.get_config_path("auth"), new_cred)
def test_add_credential_conflict_no_host(tmpdir, caplog): old_cred = auth.Auth("username", "password") new_cred = auth.Auth("username", "other_pass") with temp_environ(HOME=str(tmpdir)), \ patch("hop.auth.read_new_credential", MagicMock(return_value=new_cred)): auth.write_auth_data(configure.get_config_path("auth"), [old_cred]) args = MagicMock() args.cred_file = None args.force = False auth.add_credential(args) # without the force option, the old credential should not be overwritten check_credential_file(configure.get_config_path("auth"), old_cred) assert "Credential already exists; overwrite with --force" in caplog.text args.force = True auth.add_credential(args) # with the force option, the old credential should be overwritten check_credential_file(configure.get_config_path("auth"), new_cred)
def test_add_credential_to_empty(tmpdir): new_cred = auth.Auth("user", "pass") with temp_environ(HOME=str(tmpdir)), \ patch("hop.auth.read_new_credential", MagicMock(return_value=new_cred)): args = MagicMock() args.cred_file = None args.force = False auth.add_credential(args) check_credential_file(configure.get_config_path("auth"), new_cred)
def test_write_config_data(tmpdir): config_file = tmpdir + "/config" username = "******" password = "******" auth.write_auth_data(config_file, [auth.Auth(username, password)]) check_credential_file(config_file, auth.Auth(username, password)) credential_write_read_round_trip(auth.Auth(username, password), config_file) credential_write_read_round_trip( auth.Auth(username, password, host="example.com"), config_file) credential_write_read_round_trip(auth.Auth(username, password, ssl=False), config_file) credential_write_read_round_trip( auth.Auth(username, password, method=auth.SASLMethod.SCRAM_SHA_256), config_file) credential_write_read_round_trip( auth.Auth(username, password, ssl_ca_location="ca.cert"), config_file)
def test_auth_mechanism(): a = auth.Auth("foo", "bar") # use default mechanism assert a.mechanism == str(auth.SASLMethod.SCRAM_SHA_512) a = auth.Auth("foo", "bar", method=auth.SASLMethod.SCRAM_SHA_256) assert a.mechanism == str(auth.SASLMethod.SCRAM_SHA_256)
def test_auth_hostname(): a = auth.Auth("foo", "bar") # use default hostname assert a.hostname == "" a = auth.Auth("foo", "bar", "example.com") assert a.hostname == "example.com"
def test_select_auth_ambiguity(): too_many = "Ambiguous credentials found" two_vague_creds = [ auth.Auth("user1", "pass1"), auth.Auth("user2", "pass2"), ] # given two credetials with no associated hostnames, any lookup which does not specify the # username will be ambiguous with pytest.raises(RuntimeError) as err: selected = auth.select_matching_auth(two_vague_creds, "example.com") assert f"{too_many} for hostname 'example.com' with no username specified" in err.value.args[ 0] assert "user1 which has no associated hostname" in err.value.args[0] assert "user2 which has no associated hostname" in err.value.args[0] # specifying a valid username should resolve the ambiguity for username in ["user1", "user2"]: selected = auth.select_matching_auth(two_vague_creds, "example.com", username) assert selected.username == username two_vague_creds = [ auth.Auth("user1", "pass1"), auth.Auth("user3", "pass3", host="example.com"), ] # given a credential with no hostname and one with, a request which matches the one with a # hostname should _not_ be ambiguous selected = auth.select_matching_auth(two_vague_creds, "example.com") assert selected.username == "user3" two_specific_creds = [ auth.Auth("user3", "pass3", host="example.com"), auth.Auth("user4", "pass4", host="example.com"), ] # given two credentials which both exactly match the requested hostname there should again be # an ambiguity with pytest.raises(RuntimeError) as err: selected = auth.select_matching_auth(two_specific_creds, "example.com") assert f"{too_many} for hostname 'example.com'" in err.value.args[0] assert "user3 for example.com" in err.value.args[0] assert "user4 for example.com" in err.value.args[0] # specifying a valid username should again resolve the ambiguity for username in ["user3", "user4"]: selected = auth.select_matching_auth(two_specific_creds, "example.com", username) assert selected.username == username # No single source of credentials should issue two credentials with the same username, however, # two separate issuers could issue ones with the same name, and if neither specifies a hostname # they will be ambiguous. # (Duplicate usernames with matching hostnames should be impossible, as there should not be two # issuers for the same hostname) duplicate_users = [ auth.Auth("user", "pass5"), auth.Auth("user", "pass6"), ] with pytest.raises(RuntimeError) as err: selected = auth.select_matching_auth(duplicate_users, "example.com") assert f"{too_many} for hostname 'example.com'" in err.value.args[0] assert "user which has no associated hostname" in err.value.args[0] assert "user which has no associated hostname" in err.value.args[0] # specifying a username woun't help, since they are duplicates with pytest.raises(RuntimeError) as err: selected = auth.select_matching_auth(duplicate_users, "example.com", "user") assert f"{too_many} for hostname 'example.com'" in err.value.args[0] assert "user which has no associated hostname" in err.value.args[0] assert "user which has no associated hostname" in err.value.args[0]
def test_auth_password(): a = auth.Auth("foo", "bar") assert a.password == "bar"
def test_auth_username(): a = auth.Auth("foo", "bar") assert a.username == "foo"
auth.add_credential(args) # without the force option, the old credential should not be overwritten check_credential_file(configure.get_config_path("auth"), old_cred) assert "Credential already exists; overwrite with --force" in caplog.text args.force = True auth.add_credential(args) # with the force option, the old credential should be overwritten check_credential_file(configure.get_config_path("auth"), new_cred) # also check that unrelated credentials haven't been removed creds = auth.load_auth(configure.get_config_path("auth")) assert unrelated_cred in creds delete_input_creds = [ auth.Auth("user1", "pass1"), auth.Auth("user2", "pass2"), auth.Auth("user3", "pass3", "example.com"), auth.Auth("user3", "pass3-alt", "example.net"), ] def check_credential_deletion(config_file, removed_index): """Check that a particular credential is not in a config file after removal, and that all other are still present. Args: config_file: Path to the file to check. removed_index: Index of the credential in delete_input_creds which should not be present. """ observed = auth.load_auth(config_file)