def test_shell(session, users, http_client, base_url, graph): # noqa: F811 user = users["*****@*****.**"] assert not get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) set_user_metadata(session, user.id, USER_METADATA_SHELL_KEY, "/bin/bash") graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) assert body["data"]["user"]["metadata"] != [], "There should be metadata" assert len(body["data"]["user"] ["metadata"]) == 1, "There should only be 1 metadata!" assert (body["data"]["user"]["metadata"][0]["data_key"] == "shell" ), "There should only be 1 metadata!" assert (body["data"]["user"]["metadata"][0]["data_value"] == "/bin/bash" ), "The shell should be set to the correct value" set_user_metadata(session, user.id, USER_METADATA_SHELL_KEY, "/bin/zsh") graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) assert body["data"]["user"]["metadata"] != [], "There should be metadata" assert (body["data"]["user"]["metadata"][0]["data_key"] == "shell" ), "There should only be 1 metadata!" assert (body["data"]["user"]["metadata"][0]["data_value"] == "/bin/zsh" ), "The shell should be set to the correct value" assert len(body["data"]["user"] ["metadata"]) == 1, "There should only be 1 metadata!"
def test_graph_disable( session, graph, users, groups, user_admin_perm_to_auditors, http_client, base_url # noqa: F811 ): graph.update_from_db(session) old_users = graph.users assert sorted(old_users) == sorted(list(users.keys()) + ["*****@*****.**"]) # disable a user username = u"*****@*****.**" fe_url = url(base_url, "/users/{}/disable".format(username)) resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": "******"}, body=urlencode({})) assert resp.code == 200 graph.update_from_db(session) assert len(graph.users) == (len(old_users) - 1), "disabled user removed from graph" assert username not in graph.users
def test_github_username(session, users, http_client, base_url, graph): # noqa: F811 user = users["*****@*****.**"] assert get_user_metadata_by_key(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY) is None set_user_metadata(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY, "zorkian-on-gh") graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) [metadata] = body["data"]["user"]["metadata"] assert metadata["data_key"] == "github_username" assert metadata["data_value"] == "zorkian-on-gh" set_user_metadata(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY, None) graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) assert body["data"]["user"]["metadata"] == []
def test_graph_disable(session, graph, groups, http_client, base_url): # noqa: F811 """ Test that disabled groups work with the graph as expected.""" groupname = u"serving-team" graph.update_from_db(session) old_groups = graph.groups assert sorted(old_groups) == sorted(groups.keys()) assert groupname in graph.permission_metadata # disable a group fe_url = url(base_url, "/groups/{}/disable".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": "******"}, body=urlencode({"name": groupname}), ) assert resp.code == 200 graph.update_from_db(session) assert len(graph.groups) == (len(old_groups) - 1), "disabled group removed from graph" assert groupname not in graph.groups assert groupname not in graph.permission_metadata
def test_graph_desc_to_ances(session, graph, users, groups): # noqa: F811 """ Test adding members where all descendants already exist.""" setup_desc_to_ances(session, users, groups) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set( ["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
def test_shell(session, users, http_client, base_url, graph): # noqa: F811 user = users["*****@*****.**"] assert not get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) set_user_metadata(session, user.id, USER_METADATA_SHELL_KEY, "/bin/bash") graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) assert body["data"]["user"]["metadata"] != [], "There should be metadata" assert len(body["data"]["user"]["metadata"]) == 1, "There should only be 1 metadata!" assert ( body["data"]["user"]["metadata"][0]["data_key"] == "shell" ), "There should only be 1 metadata!" assert ( body["data"]["user"]["metadata"][0]["data_value"] == "/bin/bash" ), "The shell should be set to the correct value" set_user_metadata(session, user.id, USER_METADATA_SHELL_KEY, "/bin/zsh") graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) assert body["data"]["user"]["metadata"] != [], "There should be metadata" assert ( body["data"]["user"]["metadata"][0]["data_key"] == "shell" ), "There should only be 1 metadata!" assert ( body["data"]["user"]["metadata"][0]["data_value"] == "/bin/zsh" ), "The shell should be set to the correct value" assert len(body["data"]["user"]["metadata"]) == 1, "There should only be 1 metadata!"
def test_graph_desc_to_ances(session, graph, users, groups): # noqa: F811 """ Test adding members where all descendants already exist.""" setup_desc_to_ances(session, users, groups) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"] ) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"] ) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
def test_machine_set_plugin(session, standard_graph, graph, http_client, base_url): # noqa: F811 get_plugin_proxy().add_plugin(MachineSetPlugin()) admin = "*****@*****.**" # Edit the metadata of an existing service account. This should fail (although return 200) # including the appropriate error. update = { "description": "some service account", "machine_set": "not valid" } fe_url = url(base_url, "/groups/team-sre/service/[email protected]/edit") resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)) assert resp.code == 200 assert b"[email protected] has invalid machine set" in resp.body graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["machine_set"] == "some machines" # Use a valid machine set, and then this should go through. update["machine_set"] = "is okay" resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["machine_set"] == "is okay"
def test_groups_email(groups, session, graph, http_client, base_url): # noqa: F811 expected_address = "*****@*****.**" sad = groups["sad-team"] sad.email_address = expected_address session.commit() Counter.incr(session, "updates") graph.update_from_db(session) api_url = url(base_url, "/groups/{}".format(sad.name)) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert body["data"]["group"]["contacts"]["email"] == expected_address
def test_groups_email(groups, session, graph, http_client, base_url): # noqa: F811 expected_address = "*****@*****.**" sad = groups["sad-team"] sad.email_address = expected_address session.commit() Counter.incr(session, "updates") graph.update_from_db(session) api_url = url(base_url, "/groups/{}".format(sad.name)) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert body["data"]["group"]["contacts"]["email"] == expected_address
def test_user_enable_disable( session, # noqa: F811 graph, # noqa: F811 users, # noqa: F811 user_admin_perm_to_auditors, user_enable_perm_to_sre, http_client, base_url, ): username = "******" old_groups = sorted(get_groups(graph, username)) headers_admin = {"X-Grouper-User": "******"} headers_enable = {"X-Grouper-User": "******"} body_preserve = urlencode({"preserve_membership": "true"}) body_base = urlencode({}) # disable user fe_url = url(base_url, "/users/{}/disable".format(username)) resp = yield http_client.fetch(fe_url, method="POST", headers=headers_admin, body=body_base) assert resp.code == 200 # Attempt to enable user, preserving groups, as user with `grouper.user.enable`. # Should fail due to lack of admin perm. fe_url = url(base_url, "/users/{}/enable".format(username)) with pytest.raises(HTTPError): resp = yield http_client.fetch( fe_url, method="POST", headers=headers_enable, body=body_preserve ) # enable user, PRESERVE groups, as a user with the correct admin permission fe_url = url(base_url, "/users/{}/enable".format(username)) resp = yield http_client.fetch( fe_url, method="POST", headers=headers_admin, body=body_preserve ) assert resp.code == 200 graph.update_from_db(session) assert old_groups == sorted(get_groups(graph, username)), "nothing should be removed" # disable user again fe_url = url(base_url, "/users/{}/disable".format(username)) resp = yield http_client.fetch(fe_url, method="POST", headers=headers_admin, body=body_base) assert resp.code == 200 # Attempt to enable user, PURGE groups. Should now succeed even with # only the `grouper.user.enable` perm. fe_url = url(base_url, "/users/{}/enable".format(username)) resp = yield http_client.fetch(fe_url, method="POST", headers=headers_enable, body=body_base) assert resp.code == 200 graph.update_from_db(session) assert len(get_groups(graph, username)) == 0, "all group membership should be removed"
def test_user_enable_disable( session, # noqa: F811 graph, # noqa: F811 users, # noqa: F811 user_admin_perm_to_auditors, user_enable_perm_to_sre, http_client, base_url, ): username = u"*****@*****.**" old_groups = sorted(get_groups(graph, username)) headers_admin = {"X-Grouper-User": "******"} headers_enable = {"X-Grouper-User": "******"} body_preserve = urlencode({"preserve_membership": "true"}) body_base = urlencode({}) # disable user fe_url = url(base_url, "/users/{}/disable".format(username)) resp = yield http_client.fetch(fe_url, method="POST", headers=headers_admin, body=body_base) assert resp.code == 200 # Attempt to enable user, preserving groups, as user with `grouper.user.enable`. # Should fail due to lack of admin perm. fe_url = url(base_url, "/users/{}/enable".format(username)) with pytest.raises(HTTPError): resp = yield http_client.fetch( fe_url, method="POST", headers=headers_enable, body=body_preserve ) # enable user, PRESERVE groups, as a user with the correct admin permission fe_url = url(base_url, "/users/{}/enable".format(username)) resp = yield http_client.fetch( fe_url, method="POST", headers=headers_admin, body=body_preserve ) assert resp.code == 200 graph.update_from_db(session) assert old_groups == sorted(get_groups(graph, username)), "nothing should be removed" # disable user again fe_url = url(base_url, "/users/{}/disable".format(username)) resp = yield http_client.fetch(fe_url, method="POST", headers=headers_admin, body=body_base) assert resp.code == 200 # Attempt to enable user, PURGE groups. Should now succeed even with # only the `grouper.user.enable` perm. fe_url = url(base_url, "/users/{}/enable".format(username)) resp = yield http_client.fetch(fe_url, method="POST", headers=headers_enable, body=body_base) assert resp.code == 200 graph.update_from_db(session) assert len(get_groups(graph, username)) == 0, "all group membership should be removed"
def test_graph_edit_role(session, graph, standard_graph, groups, users): # noqa: F811 """Test that membership role changes are refected in the graph.""" username = "******" user_role = graph.get_group_details("tech-ops")["users"][username]["rolename"] assert user_role == "np-owner" groups["tech-ops"].edit_member( users["*****@*****.**"], users[username], "a reason", role="owner" ) graph.update_from_db(session) user_role = graph.get_group_details("tech-ops")["users"][username]["rolename"] assert user_role == "owner"
def test_graph_edit_role( session, graph, standard_graph, groups, users, http_client, base_url # noqa: F811 ): """Test that membership role changes are refected in the graph.""" user_role = graph.get_group_details( "tech-ops")["users"]["*****@*****.**"]["rolename"] assert user_role == "np-owner" # Ensure they are auditors so that they can be owner. add_member(groups["auditors"], users["*****@*****.**"]) session.commit() # np-owner cannot upgrade themselves to owner resp = yield http_client.fetch( url(base_url, "/groups/tech-ops/edit/user/[email protected]"), method="POST", headers={"X-Grouper-User": "******"}, body=urlencode({ "role": "owner", "reason": "testing" }), ) assert resp.code == 200 graph.update_from_db(session) user_role = graph.get_group_details( "tech-ops")["users"]["*****@*****.**"]["rolename"] assert user_role == "np-owner" # but an owner can resp = yield http_client.fetch( url(base_url, "/groups/tech-ops/edit/user/[email protected]"), method="POST", headers={"X-Grouper-User": "******"}, body=urlencode({ "role": "owner", "reason": "testing" }), ) assert resp.code == 200 graph.update_from_db(session) user_role = graph.get_group_details( "tech-ops")["users"]["*****@*****.**"]["rolename"] assert user_role == "owner"
def test_graph_cycle_indirect(session, graph, users, groups): # noqa: F811 """ Test adding a member that will create a cycle. gary zay testuser | | | sre <----- tech-ops <----- team-infra <-- | | | | --------> all-teams -------------------- """ add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) add_member(groups["team-infra"], users["*****@*****.**"]) add_member(groups["team-sre"], groups["tech-ops"]) add_member(groups["tech-ops"], groups["team-infra"]) add_member(groups["team-infra"], groups["all-teams"]) add_member(groups["all-teams"], groups["team-sre"]) session.commit() graph.update_from_db(session) all_users = set(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) all_groups = set(["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_users(graph, "team-sre") == all_users assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "tech-ops") == all_users assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "team-infra") == all_users assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == all_users assert get_users(graph, "all-teams", cutoff=1) == set([]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-infra"])
def test_graph_cycle_indirect(session, graph, users, groups): # noqa: F811 """ Test adding a member that will create a cycle. gary zay testuser | | | sre <----- tech-ops <----- team-infra <-- | | | | --------> all-teams -------------------- """ add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) add_member(groups["team-infra"], users["*****@*****.**"]) add_member(groups["team-sre"], groups["tech-ops"]) add_member(groups["tech-ops"], groups["team-infra"]) add_member(groups["team-infra"], groups["all-teams"]) add_member(groups["all-teams"], groups["team-sre"]) session.commit() graph.update_from_db(session) all_users = set(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) all_groups = set(["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_users(graph, "team-sre") == all_users assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "tech-ops") == all_users assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "team-infra") == all_users assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == all_users assert get_users(graph, "all-teams", cutoff=1) == set([]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"]) assert get_groups(graph, "*****@*****.**") == all_groups assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-infra"])
def test_graph_edit_role(session, graph, standard_graph, groups, users): # noqa: F811 """Test that membership role changes are refected in the graph.""" username = "******" user_role = graph.get_group_details( "tech-ops")["users"][username]["rolename"] assert user_role == "np-owner" groups["tech-ops"].edit_member(users["*****@*****.**"], users[username], "a reason", role="owner") graph.update_from_db(session) user_role = graph.get_group_details( "tech-ops")["users"][username]["rolename"] assert user_role == "owner"
def test_graph_disable( session, graph, users, groups, user_admin_perm_to_auditors, http_client, base_url # noqa: F811 ): graph.update_from_db(session) old_users = graph.users assert sorted(old_users) == sorted(list(users.keys()) + ["*****@*****.**"]) # disable a user username = u"*****@*****.**" fe_url = url(base_url, "/users/{}/disable".format(username)) resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": "******"}, body=urlencode({}) ) assert resp.code == 200 graph.update_from_db(session) assert len(graph.users) == (len(old_users) - 1), "disabled user removed from graph" assert username not in graph.users
def test_passwords_api(session, users, http_client, base_url, graph): # noqa: F811 user = users["*****@*****.**"] TEST_PASSWORD = "******" add_new_user_password(session, "test", TEST_PASSWORD, user.id) assert len(user_passwords(session, user)) == 1, "The user should only have a single password" graph.update_from_db(session) c = Counter.get(session, name="updates") api_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert body["checkpoint"] == c.count, "The API response is not up to date" assert ( body["data"]["user"]["passwords"] != [] ), "The user should not have an empty passwords field" assert ( body["data"]["user"]["passwords"][0]["name"] == "test" ), "The password should have the same name" assert ( body["data"]["user"]["passwords"][0]["func"] == "crypt(3)-$6$" ), "This test does not support any hash functions other than crypt(3)-$6$" assert body["data"]["user"]["passwords"][0]["hash"] == crypt.crypt( TEST_PASSWORD, body["data"]["user"]["passwords"][0]["salt"] ), ( "The hash should be the same as hashing the password and the salt together using the" " hashing function" ) assert body["data"]["user"]["passwords"][0]["hash"] != crypt.crypt( "hello", body["data"]["user"]["passwords"][0]["salt"] ), ( "The hash should not be the same as hashing the wrong password and the salt together" " using the hashing function" ) delete_user_password(session, "test", user.id) c = Counter.get(session, name="updates") graph.update_from_db(session) api_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert body["checkpoint"] == c.count, "The API response is not up to date" assert body["data"]["user"]["passwords"] == [], "The user should not have any passwords"
def test_passwords_api(session, users, http_client, base_url, graph): # noqa: F811 user = users["*****@*****.**"] TEST_PASSWORD = "******" add_new_user_password(session, "test", TEST_PASSWORD, user.id) assert len(user_passwords(session, user)) == 1, "The user should only have a single password" graph.update_from_db(session) c = Counter.get(session, name="updates") api_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert body["checkpoint"] == c.count, "The API response is not up to date" assert ( body["data"]["user"]["passwords"] != [] ), "The user should not have an empty passwords field" assert ( body["data"]["user"]["passwords"][0]["name"] == "test" ), "The password should have the same name" assert ( body["data"]["user"]["passwords"][0]["func"] == "crypt(3)-$6$" ), "This test does not support any hash functions other than crypt(3)-$6$" assert body["data"]["user"]["passwords"][0]["hash"] == crypt.crypt( TEST_PASSWORD, body["data"]["user"]["passwords"][0]["salt"] ), ( "The hash should be the same as hashing the password and the salt together using the" " hashing function" ) assert body["data"]["user"]["passwords"][0]["hash"] != crypt.crypt( "hello", body["data"]["user"]["passwords"][0]["salt"] ), ( "The hash should not be the same as hashing the wrong password and the salt together" " using the hashing function" ) delete_user_password(session, "test", user.id) c = Counter.get(session, name="updates") graph.update_from_db(session) api_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(api_url) body = json.loads(resp.body) assert body["checkpoint"] == c.count, "The API response is not up to date" assert body["data"]["user"]["passwords"] == [], "The user should not have any passwords"
def test_graph_add_member_existing(session, graph, users, groups): # noqa: F811 """ Test adding members to an existing relationship.""" add_member(groups["team-sre"], users["*****@*****.**"], role="owner") add_member(groups["tech-ops"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], groups["team-sre"]) add_member(groups["team-infra"], groups["tech-ops"]) add_member(groups["all-teams"], users["*****@*****.**"], role="owner") add_member(groups["all-teams"], groups["team-infra"]) add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set( ["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
def test_service_account_fe_edit( session, standard_graph, graph, http_client, base_url # noqa: F811 ): owner = "*****@*****.**" plebe = "*****@*****.**" admin = "*****@*****.**" # Unrelated people cannot edit the service account. fe_url = url(base_url, "/groups/team-sre/service/[email protected]/edit") update = {"description": "desc", "machine_set": "machines"} with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode(update)) # A group member can. resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode(update)) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["description"] == "desc" assert metadata["service_account"]["machine_set"] == "machines" # A user admin also can. update["description"] = "done by admin" resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["description"] == "done by admin"
def test_github_username(session, users, http_client, base_url, graph): # noqa: F811 user = users["*****@*****.**"] assert get_user_metadata_by_key(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY) is None set_user_metadata(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY, "zorkian-on-gh") graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) [metadata] = body["data"]["user"]["metadata"] assert metadata["data_key"] == "github_username" assert metadata["data_value"] == "zorkian-on-gh" set_user_metadata(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY, None) graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) assert body["data"]["user"]["metadata"] == []
def test_graph_add_member_existing(session, graph, users, groups): # noqa: F811 """ Test adding members to an existing relationship.""" add_member(groups["team-sre"], users["*****@*****.**"], role="owner") add_member(groups["tech-ops"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], users["*****@*****.**"], role="owner") add_member(groups["team-infra"], groups["team-sre"]) add_member(groups["team-infra"], groups["tech-ops"]) add_member(groups["all-teams"], users["*****@*****.**"], role="owner") add_member(groups["all-teams"], groups["team-infra"]) add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-infra", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "all-teams") == set(["*****@*****.**", "*****@*****.**", "*****@*****.**"]) assert get_users(graph, "all-teams", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"] ) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops", "team-infra"]) assert get_groups(graph, "*****@*****.**") == set( ["team-sre", "all-teams", "tech-ops", "team-infra"] ) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**") == set(["all-teams"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["all-teams"])
def test_graph_cycle_direct(session, graph, users, groups): # noqa: F811 """ Test adding members where all descendants already exist.""" add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) add_member(groups["team-sre"], groups["tech-ops"]) add_member(groups["tech-ops"], groups["team-sre"]) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"])
def test_graph_cycle_direct(session, graph, users, groups): # noqa: F811 """ Test adding members where all descendants already exist.""" add_member(groups["team-sre"], users["*****@*****.**"]) add_member(groups["tech-ops"], users["*****@*****.**"]) add_member(groups["team-sre"], groups["tech-ops"]) add_member(groups["tech-ops"], groups["team-sre"]) session.commit() graph.update_from_db(session) assert get_users(graph, "team-sre") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "team-sre", cutoff=1) == set(["*****@*****.**"]) assert get_users(graph, "tech-ops") == set(["*****@*****.**", "*****@*****.**"]) assert get_users(graph, "tech-ops", cutoff=1) == set(["*****@*****.**"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["team-sre"]) assert get_groups(graph, "*****@*****.**") == set(["team-sre", "tech-ops"]) assert get_groups(graph, "*****@*****.**", cutoff=1) == set(["tech-ops"])
def test_graph_disable(session, graph, groups, http_client, base_url): # noqa: F811 """ Test that disabled groups work with the graph as expected.""" groupname = u"serving-team" graph.update_from_db(session) old_groups = graph.groups assert sorted(old_groups) == sorted(groups.keys()) assert "permissions" in graph.get_group_details(groupname) # disable a group fe_url = url(base_url, "/groups/{}/disable".format(groupname)) resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": "******"}, body=urlencode({"name": groupname}), ) assert resp.code == 200 graph.update_from_db(session) assert len(graph.groups) == (len(old_groups) - 1), "disabled group removed from graph" assert groupname not in graph.groups with pytest.raises(NoSuchGroup): graph.get_group_details(groupname)
def test_disabling_permission( session, groups, standard_graph, graph, http_client, base_url # noqa: F811 ): """ This tests disabling via the front-end route, including checking that the user is authorized to disable permissions. """ perm_name = "sudo" nonpriv_user_name = "*****@*****.**" # user without PERMISSION_ADMIN nonpriv_headers = {"X-Grouper-User": nonpriv_user_name} priv_user_name = "*****@*****.**" # user with PERMISSION_ADMIN priv_headers = {"X-Grouper-User": priv_user_name} disable_url = url(base_url, "/permissions/{}/disable".format(perm_name)) disable_url_non_exist_perm = url(base_url, "/permissions/no.exists/disable") assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") # attempt to disable the permission -> should fail cuz actor # doesn't have PERMISSION_ADMIN with pytest.raises(HTTPError) as exc: yield http_client.fetch(disable_url, method="POST", headers=nonpriv_headers, body="") assert exc.value.code == 403 # check that no change assert get_permission(session, perm_name).enabled graph.update_from_db(session) assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") assert "sudo:shell" in get_user_permissions(graph, "*****@*****.**") # an actor with PERMISSION_ADMIN is allowed to disable the # permission resp = yield http_client.fetch(disable_url, method="POST", headers=priv_headers, body="") assert resp.code == 200 assert not get_permission(session, perm_name).enabled graph.update_from_db(session) assert "sudo:shell" not in get_user_permissions(graph, "*****@*****.**") assert "sudo:shell" not in get_user_permissions(graph, "*****@*****.**") with pytest.raises(HTTPError) as exc: yield http_client.fetch( disable_url_non_exist_perm, method="POST", headers=priv_headers, body="" ) assert exc.value.code == 404 # # make sure that when disabling the permission, all mappings of # it, i.e., with different arguments, are disabled # # the standard_graph grants 'ssh' with args '*' and 'shell' to two # different groups assert "ssh:*" in get_group_permissions(graph, "team-sre") assert "ssh:shell" in get_group_permissions(graph, "tech-ops") # disable the perm disable_url_ssh_pem = url(base_url, "/permissions/ssh/disable") resp = yield http_client.fetch( disable_url_ssh_pem, method="POST", headers=priv_headers, body="" ) assert resp.code == 200 assert not get_permission(session, "ssh").enabled graph.update_from_db(session) assert "ssh:*" not in get_group_permissions(graph, "team-sre") assert "ssh:shell" not in get_group_permissions(graph, "tech-ops")
def test_machine_set_plugin( mocker, session, standard_graph, graph, http_client, base_url # noqa: F811 ): mocker.patch("grouper.service_account.get_plugin_proxy", return_value=PluginProxy([MachineSetPlugin()])) admin = "*****@*****.**" # Edit the metadata of an existing service account. This should fail (although return 200) # including the appropriate error. update = { "description": "some service account", "machine_set": "not valid" } fe_url = url(base_url, "/groups/team-sre/service/[email protected]/edit") resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)) assert resp.code == 200 assert "[email protected] has invalid machine set" in resp.body graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["machine_set"] == "some machines" # Use a valid machine set, and then this should go through. update["machine_set"] = "is okay" resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(update)) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["machine_set"] == "is okay" # Try creating a new service account with an invalid machine set. data = { "name": "*****@*****.**", "description": "some other service account", "machine_set": "not valid", } fe_url = url(base_url, "/groups/team-sre/service/create") resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(data)) assert resp.code == 200 assert "[email protected] has invalid machine set" in resp.body graph.update_from_db(session) assert "*****@*****.**" not in graph.users # But this should go through with a valid machine set. data["machine_set"] = "is okay" resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode(data)) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"][ "description"] == "some other service account" assert metadata["service_account"]["machine_set"] == "is okay"
def test_grant_and_revoke( session, standard_graph, graph, groups, permissions, http_client, base_url # noqa: F811 ): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): # type: (GroupGraph) -> bool return any( [ g["permission"] == permission_name and g["distance"] == 0 for g in graph.get_group_details(group_name)["permissions"] ] ) # make some permission admins grant_permission(groups["security-team"], permissions[PERMISSION_ADMIN]) # grant attempt by non-permission admin fe_url = url(base_url, "/permissions/grant/{}".format(group_name)) with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", body=urlencode({"permission": permission_name, "argument": "specific_arg"}), headers={"X-Grouper-User": "******"}, ) graph.update_from_db(session) assert not _check_graph_for_perm(graph), "no permissions granted" # grant by permission admin resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"permission": permission_name, "argument": "specific_arg"}), headers={"X-Grouper-User": user_name}, ) assert resp.code == 200 graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions granted, successfully" # figure out mapping_id of grant permission_id = get_permission(session, permission_name).id group_id = Group.get(session, name=group_name).id mapping = ( session.query(PermissionMap) .filter(PermissionMap.permission_id == permission_id, PermissionMap.group_id == group_id) .first() ) # revoke permission by non-admin fe_url = url(base_url, "/permissions/{}/revoke/{}".format(permission_name, mapping.id)) with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", body=urlencode({}), headers={"X-Grouper-User": "******"} ) graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions not revoked" # revoke permission for realz resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({}), headers={"X-Grouper-User": user_name} ) assert resp.code == 200 graph.update_from_db(session) assert not _check_graph_for_perm(graph), "permissions revoked successfully"
def test_service_account_fe_perms( session, standard_graph, graph, http_client, base_url # noqa: F811 ): admin = "*****@*****.**" owner = "*****@*****.**" plebe = "*****@*****.**" # Unrelated people cannot create a service account fe_url = url(base_url, "/groups/team-sre/service/create") with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({ "name": "service_account", "description": "*", "machine_set": "*" }), ) # But group members can create service accounts resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({ "name": "service_account", "description": "*", "machine_set": "*" }), ) assert resp.code == 200 # Unrelated people cannot grant a permission. fe_url = url(base_url, "/groups/team-sre/service/[email protected]/grant") with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({ "permission": "team-sre", "argument": "*" }), ) # Even group owners cannot grant an unrelated permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({ "permission": "other-perm", "argument": "*" }), ) assert resp.code == 200 graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") assert metadata["permissions"] == [] # Group owners can delegate a team permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({ "permission": "team-sre", "argument": "*" }), ) assert resp.code == 200 # Global user admins still cannot grant an unrelated permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({ "permission": "other-perm", "argument": "*" }), ) assert resp.code == 200 graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") assert len(metadata["permissions"]) == 1 # But can delegate a team permission. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({ "permission": "ssh", "argument": "*" }), ) assert resp.code == 200 # Check that the permissions are reflected in the graph. graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") perms = [(p["permission"], p["argument"]) for p in metadata["permissions"]] assert sorted(perms) == [("ssh", "*"), ("team-sre", "*")] # Find the mapping IDs of the two permissions. service_account = ServiceAccount.get(session, name="*****@*****.**") perms = service_account_permissions(session, service_account) # Unrelated people cannot revoke a permission. fe_url = url( base_url, "/groups/team-sre/service/[email protected]/revoke/{}".format( perms[0].mapping_id)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({})) # But the group owner and a global admin can. resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({})) assert resp.code == 200 fe_url = url( base_url, "/groups/team-sre/service/[email protected]/revoke/{}".format( perms[1].mapping_id)) resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({})) assert resp.code == 200 # This should have removed all the permissions. graph.update_from_db(session) metadata = graph.get_user_details("*****@*****.**") assert metadata["permissions"] == []
def test_service_accounts( session, standard_graph, graph, users, groups, permissions # noqa: F811 ): # Create a service account. service_account = ServiceAccount.get(session, name="*****@*****.**") assert service_account.description == "some service account" assert service_account.machine_set == "some machines" assert service_account.user.name == "*****@*****.**" assert service_account.user.enabled == True assert service_account.user.is_service_account == True accounts = get_service_accounts(session, groups["team-sre"]) assert len(accounts) == 1 assert accounts[0].user.name == "*****@*****.**" assert is_service_account(session, service_account.user) # Duplicates should raise an exception. with pytest.raises(DuplicateServiceAccount): create_service_account(session, users["*****@*****.**"], "*****@*****.**", "dup", "dup", groups["team-sre"]) # zorkian should be able to manage the account, as should gary, but oliver (not a member of the # group) should not. assert can_manage_service_account(session, service_account, users["*****@*****.**"]) assert can_manage_service_account(session, service_account, users["*****@*****.**"]) assert not can_manage_service_account(session, service_account, users["*****@*****.**"]) # Check that the user appears in the graph. graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["enabled"] assert metadata["service_account"]["description"] == "some service account" assert metadata["service_account"]["machine_set"] == "some machines" assert metadata["service_account"]["owner"] == "team-sre" group_details = graph.get_group_details("team-sre") assert group_details["service_accounts"] == ["*****@*****.**"] # Grant a permission to the service account and check it in the graph. grant_permission_to_service_account(session, service_account, permissions["team-sre"], "*") graph.update_from_db(session) user_details = graph.get_user_details("*****@*****.**") assert user_details["permissions"][0]["permission"] == "team-sre" assert user_details["permissions"][0]["argument"] == "*" # Diabling the service account should remove the link to the group. disable_service_account(session, users["*****@*****.**"], service_account) assert service_account.user.enabled == False assert get_service_accounts(session, groups["team-sre"]) == [] # The user should also be gone from the graph and have its permissions removed. graph.update_from_db(session) group_details = graph.get_group_details("team-sre") assert "service_accounts" not in group_details metadata = graph.user_metadata["*****@*****.**"] assert not metadata["enabled"] assert "owner" not in metadata["service_account"] user_details = graph.get_user_details("*****@*****.**") assert user_details["permissions"] == [] # We can re-enable and attach to a different group. new_group = groups["security-team"] enable_service_account(session, users["*****@*****.**"], service_account, new_group) assert service_account.user.enabled == True assert get_service_accounts(session, groups["team-sre"]) == [] accounts = get_service_accounts(session, new_group) assert len(accounts) == 1 assert accounts[0].user.name == "*****@*****.**" # Check that this is reflected in the graph and the user has no permissions. graph.update_from_db(session) group_details = graph.get_group_details("security-team") assert group_details["service_accounts"] == ["*****@*****.**"] metadata = graph.user_metadata["*****@*****.**"] assert metadata["service_account"]["owner"] == "security-team" user_details = graph.get_user_details("*****@*****.**") assert user_details["permissions"] == []
def test_grant_and_revoke( session, standard_graph, graph, groups, permissions, http_client, base_url # noqa: F811 ): """Test that permission grant and revokes are reflected correctly.""" group_name = "team-sre" permission_name = "sudo" user_name = "*****@*****.**" def _check_graph_for_perm(graph): # type: (GroupGraph) -> bool return any([ g["permission"] == permission_name and g["distance"] == 0 for g in graph.get_group_details(group_name)["permissions"] ]) # make some permission admins grant_permission(groups["security-team"], permissions[PERMISSION_ADMIN]) # grant attempt by non-permission admin fe_url = url(base_url, "/permissions/grant/{}".format(group_name)) with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "specific_arg" }), headers={"X-Grouper-User": "******"}, ) graph.update_from_db(session) assert not _check_graph_for_perm(graph), "no permissions granted" # grant by permission admin resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "permission": permission_name, "argument": "specific_arg" }), headers={"X-Grouper-User": user_name}, ) assert resp.code == 200 graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions granted, successfully" # figure out mapping_id of grant permission_id = get_permission(session, permission_name).id group_id = Group.get(session, name=group_name).id mapping = (session.query(PermissionMap).filter( PermissionMap.permission_id == permission_id, PermissionMap.group_id == group_id).first()) # revoke permission by non-admin fe_url = url( base_url, "/permissions/{}/revoke/{}".format(permission_name, mapping.id)) with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={"X-Grouper-User": "******"}) graph.update_from_db(session) assert _check_graph_for_perm(graph), "permissions not revoked" # revoke permission for realz resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({}), headers={"X-Grouper-User": user_name}) assert resp.code == 200 graph.update_from_db(session) assert not _check_graph_for_perm(graph), "permissions revoked successfully"
def test_service_account_fe_disable( session, standard_graph, graph, http_client, base_url # noqa: F811 ): admin = "*****@*****.**" owner = "*****@*****.**" plebe = "*****@*****.**" # Unrelated people cannot disable the service account. fe_url = url(base_url, "/groups/security-team/service/[email protected]/disable") with pytest.raises(HTTPError): yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": plebe}, body=urlencode({})) # Group members can disable the service account. resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({})) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert not metadata["enabled"] group_details = graph.get_group_details("team-sre") assert "service_accounts" not in group_details # The group owner cannot enable the account, since the group ownership has been lost fe_url = url(base_url, "/service/[email protected]/enable") with pytest.raises(HTTPError): yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": owner}, body=urlencode({"owner": "team-sre"}), ) # A global admin can enable the account. resp = yield http_client.fetch( fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({"owner": "team-sre"}), ) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert metadata["enabled"] assert metadata["service_account"]["owner"] == "team-sre" group_details = graph.get_group_details("team-sre") assert group_details["service_accounts"] == ["*****@*****.**"] # And can also disable the account even though they're not a member of the group. fe_url = url(base_url, "/groups/security-team/service/[email protected]/disable") resp = yield http_client.fetch(fe_url, method="POST", headers={"X-Grouper-User": admin}, body=urlencode({})) assert resp.code == 200 graph.update_from_db(session) metadata = graph.user_metadata["*****@*****.**"] assert not metadata["enabled"]
def test_audit_end_to_end(session, users, groups, http_client, base_url, graph): # noqa: F811 """ Tests an end-to-end audit cycle. """ groupname = "audited-team" gary_id = users["*****@*****.**"].id # make everyone an auditor or global audit will have issues add_member(groups["auditors"], users["*****@*****.**"]) add_member(groups["auditors"], users["*****@*****.**"]) add_member(groups["auditors"], users["*****@*****.**"]) add_member(groups["auditors"], users["*****@*****.**"]) # add some users to test removal add_member(groups[groupname], users["*****@*****.**"]) add_member(groups[groupname], users["*****@*****.**"]) graph.update_from_db(session) # start the audit end_at_str = (datetime.now() + timedelta(days=10)).strftime("%m/%d/%Y") fe_url = url(base_url, "/audits/create") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"ends_at": end_at_str}), headers={"X-Grouper-User": "******"}, ) assert resp.code == 200 open_audits = get_audits(session, only_open=True).all() assert len(open_audits) == 4, "audits created" assert groupname in [x.group.name for x in open_audits ], "group we expect also gets audit" # pull all the info we need to resolve audits, avoids detached sqlalchemy sessions # (DetachedInstanceError) all_group_ids = [x.group.id for x in open_audits] open_audits = [ Audit( x.id, next(iter(x.group.my_owners())), x.group.name, [ MyAuditMemberInfo( ami.audit_member_obj.id, ami.audit_member_obj.edge.member_type, ami.audit_member_obj.edge_id, ) for ami in get_group_audit_members_infos(session, x.group) ], ) for x in open_audits ] # approve everything but the one we added members to for one_audit in open_audits: fe_url = url(base_url, "/audits/{}/complete".format(one_audit.audit_id)) if one_audit.group_name == groupname: continue # blanket approval body = urlencode({ "audit_{}".format(ami.am_id): "approved" for ami in one_audit.audit_members_infos }) resp = yield http_client.fetch( fe_url, method="POST", body=body, headers={"X-Grouper-User": one_audit.owner_name}) assert resp.code == 200 open_audits = get_audits(session, only_open=True).all() assert len(open_audits) == 1, "only our test group remaining" one_audit = open_audits[0] one_audit.id body_dict = {} for ami in get_group_audit_members_infos(session, one_audit.group): if gary_id == ami.member_obj.id: # deny body_dict["audit_{}".format(ami.audit_member_obj.id)] = "remove" else: # approve body_dict["audit_{}".format(ami.audit_member_obj.id)] = "approved" owner_name = next(iter(one_audit.group.my_owners())) fe_url = url(base_url, "/audits/{}/complete".format(one_audit.id)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode(body_dict), headers={"X-Grouper-User": owner_name}) assert resp.code == 200 # check all the logs assert len(AuditLog.get_entries( session, action="start_audit")) == 1, "global start is logged" assert (len(AuditLog.get_entries( session, action="complete_global_audit")) == 1), "global complete is logged" for group_id in all_group_ids: assert (len( AuditLog.get_entries( session, on_group_id=group_id, action="complete_audit", category=AuditLogCategory.audit, )) == 1), "complete entry for each group" assert (len( AuditLog.get_entries(session, on_user_id=gary_id, category=AuditLogCategory.audit)) == 1 ), "removal AuditLog entry on user"
def test_exclude_disabled_permissions( session, standard_graph, graph, users, groups, permissions # noqa: F811 ): """ Ensure that disabled permissions are excluded from various functions/methods that return data from the models. """ perm_ssh = get_permission(session, "ssh") perm_grant = create_permission(session, PERMISSION_GRANT) session.commit() # this user has grouper.permission.grant with argument "ssh/*" grant_permission(groups["group-admins"], perm_grant, argument="ssh/*") graph.update_from_db(session) grant_perms = [ x for x in user_permissions(session, users["*****@*****.**"]) if x.name == PERMISSION_GRANT ] assert "ssh" == filter_grantable_permissions(session, grant_perms)[0][0].name assert "ssh" in (p.name for p in get_all_permissions(session)) assert "ssh" in (p.name for p in get_all_permissions(session, include_disabled=False)) assert "ssh" in (p.name for p in get_all_permissions(session, include_disabled=True)) assert "ssh" in get_grantable_permissions(session, []) assert "team-sre" in [g[0] for g in get_groups_by_permission(session, perm_ssh)] assert get_owner_arg_list(session, perm_ssh, "*") assert "ssh" in get_owners_by_grantable_permission(session) assert "ssh" in (x[0].name for x in user_grantable_permissions(session, users["*****@*****.**"])) assert user_has_permission(session, users["*****@*****.**"], "ssh") assert "ssh" in (p.name for p in user_permissions(session, users["*****@*****.**"])) assert "ssh" in (p["permission"] for p in graph.get_group_details("team-sre")["permissions"]) assert "ssh" in (pt.name for pt in graph.get_permissions()) assert "team-sre" in graph.get_permission_details("ssh")["groups"] assert "ssh" in (p["permission"] for p in graph.get_user_details("*****@*****.**")["permissions"]) # now disable the ssh permission disable_permission(session, "ssh", users["*****@*****.**"].id) graph.update_from_db(session) grant_perms = [ x for x in user_permissions(session, users["*****@*****.**"]) if x.name == PERMISSION_GRANT ] assert not filter_grantable_permissions(session, grant_perms) assert "ssh" not in (p.name for p in get_all_permissions(session)) assert "ssh" not in (p.name for p in get_all_permissions(session, include_disabled=False)) assert "ssh" in (p.name for p in get_all_permissions(session, include_disabled=True)) assert "ssh" not in get_grantable_permissions(session, []) assert not get_groups_by_permission(session, perm_ssh) assert not get_owner_arg_list(session, perm_ssh, "*") assert "ssh" not in get_owners_by_grantable_permission(session) assert "ssh" not in ( x[0].name for x in user_grantable_permissions(session, users["*****@*****.**"]) ) assert not user_has_permission(session, users["*****@*****.**"], "ssh") assert "ssh" not in (p.name for p in user_permissions(session, users["*****@*****.**"])) assert "ssh" not in ( p["permission"] for p in graph.get_group_details("team-sre")["permissions"] ) assert "ssh" not in (pt.name for pt in graph.get_permissions()) assert not graph.get_permission_details("ssh")["groups"] assert "ssh" not in ( p["permission"] for p in graph.get_user_details("*****@*****.**")["permissions"] )
def test_auditor_promotion(mock_nnp, mock_gagn, session, graph, permissions, users): # noqa: F811 """Test automatic promotion of non-auditor approvers We set up our own group/user/permission for testing instead of using the `standard_graph` fixture---retrofitting it to work for us and also not break existing tests is too cumbersome. So here are our groups: very-special-auditors: * user14 group-1: * user11 (o) * user12 * user13 (np-o) * user14 (o, a) group-2: * user13 (np-o) * user21 (o) * user22 group-3: * user22 (o) * user12 (o) group-4: * user21 (np-o) * user41 * user42 (o) * user43 (np-o) o: owner, np-o: no-permission owner, a: auditor group-1 and group-2 have the permission that we will enable auditing. group-4 will be a subgroup of group-1 and thus will inherit the audited permission from group-1. The expected outcome is: user11, user13, user21, user42, and user43 will be added to the auditors group. """ settings = BackgroundSettings() set_global_settings(settings) # # set up our test part of the graph # # create groups AUDITED_GROUP = "audited" AUDITORS_GROUP = mock_gagn.return_value = "very-special-auditors" PERMISSION_NAME = "test-permission" all_groups = { groupname: Group.get_or_create(session, groupname=groupname)[0] for groupname in ("group-1", "group-2", "group-3", "group-4", AUDITORS_GROUP) } # create users users.update({ username + "@a.co": User.get_or_create(session, username=username + "@a.co")[0] for username in ( "user11", "user12", "user13", "user14", "user21", "user22", "user23", "user41", "user42", "user43", ) }) # create permissions permissions.update({ permission: get_or_create_permission( session, permission, description="{} permission".format(permission))[0] for permission in [PERMISSION_NAME] }) # add users to groups for (groupname, username, role) in ( ("group-1", "user11", "owner"), ("group-1", "user12", "member"), ("group-1", "user13", "np-owner"), ("group-1", "user14", "owner"), ("group-2", "user13", "np-owner"), ("group-2", "user21", "owner"), ("group-2", "user22", "member"), ("group-3", "user12", "owner"), ("group-3", "user22", "owner"), ("group-4", "user21", "np-owner"), ("group-4", "user41", "member"), ("group-4", "user42", "owner"), ("group-4", "user43", "np-owner"), ): add_member(all_groups[groupname], users[username + "@a.co"], role=role) # add group-4 as member of group-1 add_member(all_groups["group-1"], all_groups["group-4"]) # add user14 to auditors group add_member(all_groups[AUDITORS_GROUP], users["*****@*****.**"]) # grant permissions to groups # # give the test permission to groups 1 and 2, and group 4 should # also inherit from group 1 grant_permission(all_groups["group-1"], permissions[PERMISSION_NAME]) grant_permission(all_groups["group-2"], permissions[PERMISSION_NAME]) grant_permission(all_groups[AUDITORS_GROUP], permissions[PERMISSION_AUDITOR]) graph.update_from_db(session) # done setting up # now a few pre-op checks assert not graph.get_group_details("group-1").get(AUDITED_GROUP) assert not graph.get_group_details("group-4").get(AUDITED_GROUP) assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"]) assert get_users(graph, "group-3") == set(["*****@*****.**", "*****@*****.**"]) # # run the promotion logic -> nothing should happen because the # test-permission is not yet audited # background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) graph.update_from_db(session) # nothing should have happened assert not graph.get_group_details("group-1").get(AUDITED_GROUP) assert not graph.get_group_details("group-4").get(AUDITED_GROUP) assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"]) assert mock_nnp.call_count == 0 # # now enable auditing for the permission and run the promotion # logic again # enable_permission_auditing(session, PERMISSION_NAME, users["*****@*****.**"].id) graph.update_from_db(session) assert graph.get_group_details("group-1").get(AUDITED_GROUP) assert graph.get_group_details("group-4").get(AUDITED_GROUP) background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) graph.update_from_db(session) # check that stuff happened assert get_users(graph, AUDITORS_GROUP) == set([ "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**" ]) expected_calls = [ call(settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-1"])), call( settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-1", "group-2"]), ), call( settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-2", "group-4"]), ), call(settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-4"])), call(settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-4"])), ] assert mock_nnp.call_count == len(expected_calls) mock_nnp.assert_has_calls(expected_calls, any_order=True) # # run the background promotion logic again, and nothing should # happen # mock_nnp.reset_mock() background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) assert mock_nnp.call_count == 0
def test_audit_end_to_end(session, users, groups, http_client, base_url, graph): # noqa: F811 """ Tests an end-to-end audit cycle. """ groupname = "audited-team" gary_id = users["*****@*****.**"].id # make everyone an auditor or global audit will have issues add_member(groups["auditors"], users["*****@*****.**"]) add_member(groups["auditors"], users["*****@*****.**"]) add_member(groups["auditors"], users["*****@*****.**"]) add_member(groups["auditors"], users["*****@*****.**"]) # add some users to test removal add_member(groups[groupname], users["*****@*****.**"]) add_member(groups[groupname], users["*****@*****.**"]) graph.update_from_db(session) # start the audit end_at_str = (datetime.now() + timedelta(days=10)).strftime("%m/%d/%Y") fe_url = url(base_url, "/audits/create") resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"ends_at": end_at_str}), headers={"X-Grouper-User": "******"}, ) assert resp.code == 200 open_audits = get_audits(session, only_open=True).all() assert len(open_audits) == 4, "audits created" assert groupname in [x.group.name for x in open_audits], "group we expect also gets audit" # pull all the info we need to resolve audits, avoids detached sqlalchemy sessions AuditMember = namedtuple("AuditMember", "am_id, edge_type, edge_id") Audit = namedtuple("Audit", "audit_id, owner_name, group_name, audit_members") all_group_ids = [x.group.id for x in open_audits] open_audits = [ Audit( x.id, next(iter(x.group.my_owners())), x.group.name, [AuditMember(am.id, am.edge.member_type, am.edge_id) for am in x.my_members()], ) for x in open_audits ] # approve everything but the one we added members to for one_audit in open_audits: fe_url = url(base_url, "/audits/{}/complete".format(one_audit.audit_id)) if one_audit.group_name == groupname: continue # blanket approval body = urlencode( {"audit_{}".format(am.am_id): "approved" for am in one_audit.audit_members} ) resp = yield http_client.fetch( fe_url, method="POST", body=body, headers={"X-Grouper-User": one_audit.owner_name} ) assert resp.code == 200 open_audits = get_audits(session, only_open=True).all() assert len(open_audits) == 1, "only our test group remaining" one_audit = open_audits[0] one_audit.id body_dict = {} for am in one_audit.my_members(): if gary_id == am.member.id: # deny body_dict["audit_{}".format(am.id)] = "remove" else: # approve body_dict["audit_{}".format(am.id)] = "approved" owner_name = next(iter(one_audit.group.my_owners())) fe_url = url(base_url, "/audits/{}/complete".format(one_audit.id)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode(body_dict), headers={"X-Grouper-User": owner_name} ) assert resp.code == 200 # check all the logs assert len(AuditLog.get_entries(session, action="start_audit")) == 1, "global start is logged" assert ( len(AuditLog.get_entries(session, action="complete_global_audit")) == 1 ), "global complete is logged" for group_id in all_group_ids: assert ( len( AuditLog.get_entries( session, on_group_id=group_id, action="complete_audit", category=AuditLogCategory.audit, ) ) == 1 ), "complete entry for each group" assert ( len(AuditLog.get_entries(session, on_user_id=gary_id, category=AuditLogCategory.audit)) == 1 ), "removal AuditLog entry on user"
def test_auditor_promotion(mock_nnp, mock_gagn, session, graph, permissions, users): # noqa: F811 """Test automatic promotion of non-auditor approvers We set up our own group/user/permission for testing instead of using the `standard_graph` fixture---retrofitting it to work for us and also not break existing tests is too cumbersome. So here are our groups: very-special-auditors: * user14 group-1: * user11 (o) * user12 * user13 (np-o) * user14 (o, a) group-2: * user13 (np-o) * user21 (o) * user22 group-3: * user22 (o) * user12 (o) group-4: * user21 (np-o) * user41 * user42 (o) * user43 (np-o) o: owner, np-o: no-permission owner, a: auditor group-1 and group-2 have the permission that we will enable auditing. group-4 will be a subgroup of group-1 and thus will inherit the audited permission from group-1. The expected outcome is: user11, user13, user21, user42, and user43 will be added to the auditors group. """ settings = BackgroundSettings() set_global_settings(settings) # # set up our test part of the graph # # create groups AUDITED_GROUP = "audited" AUDITORS_GROUP = mock_gagn.return_value = "very-special-auditors" PERMISSION_NAME = "test-permission" all_groups = { groupname: Group.get_or_create(session, groupname=groupname)[0] for groupname in ("group-1", "group-2", "group-3", "group-4", AUDITORS_GROUP) } # create users users.update( { username + "@a.co": User.get_or_create(session, username=username + "@a.co")[0] for username in ( "user11", "user12", "user13", "user14", "user21", "user22", "user23", "user41", "user42", "user43", ) } ) # create permissions permissions.update( { permission: get_or_create_permission( session, permission, description="{} permission".format(permission) )[0] for permission in [PERMISSION_NAME] } ) # add users to groups for (groupname, username, role) in ( ("group-1", "user11", "owner"), ("group-1", "user12", "member"), ("group-1", "user13", "np-owner"), ("group-1", "user14", "owner"), ("group-2", "user13", "np-owner"), ("group-2", "user21", "owner"), ("group-2", "user22", "member"), ("group-3", "user12", "owner"), ("group-3", "user22", "owner"), ("group-4", "user21", "np-owner"), ("group-4", "user41", "member"), ("group-4", "user42", "owner"), ("group-4", "user43", "np-owner"), ): add_member(all_groups[groupname], users[username + "@a.co"], role=role) # add group-4 as member of group-1 add_member(all_groups["group-1"], all_groups["group-4"]) # add user14 to auditors group add_member(all_groups[AUDITORS_GROUP], users["*****@*****.**"]) # grant permissions to groups # # give the test permission to groups 1 and 2, and group 4 should # also inherit from group 1 grant_permission(all_groups["group-1"], permissions[PERMISSION_NAME]) grant_permission(all_groups["group-2"], permissions[PERMISSION_NAME]) grant_permission(all_groups[AUDITORS_GROUP], permissions[PERMISSION_AUDITOR]) graph.update_from_db(session) # done setting up # now a few pre-op checks assert not graph.get_group_details("group-1").get(AUDITED_GROUP) assert not graph.get_group_details("group-4").get(AUDITED_GROUP) assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"]) assert get_users(graph, "group-3") == set(["*****@*****.**", "*****@*****.**"]) # # run the promotion logic -> nothing should happen because the # test-permission is not yet audited # background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) graph.update_from_db(session) # nothing should have happened assert not graph.get_group_details("group-1").get(AUDITED_GROUP) assert not graph.get_group_details("group-4").get(AUDITED_GROUP) assert get_users(graph, AUDITORS_GROUP) == set(["*****@*****.**"]) assert mock_nnp.call_count == 0 # # now enable auditing for the permission and run the promotion # logic again # enable_permission_auditing(session, PERMISSION_NAME, users["*****@*****.**"].id) graph.update_from_db(session) assert graph.get_group_details("group-1").get(AUDITED_GROUP) assert graph.get_group_details("group-4").get(AUDITED_GROUP) background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) graph.update_from_db(session) # check that stuff happened assert get_users(graph, AUDITORS_GROUP) == set( ["*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**"] ) expected_calls = [ call( settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-1"]) ), call( settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-1", "group-2"]), ), call( settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-2", "group-4"]), ), call( settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-4"]) ), call( settings, session, users["*****@*****.**"], all_groups[AUDITORS_GROUP], set(["group-4"]) ), ] assert mock_nnp.call_count == len(expected_calls) mock_nnp.assert_has_calls(expected_calls, any_order=True) # # run the background promotion logic again, and nothing should # happen # mock_nnp.reset_mock() background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) assert mock_nnp.call_count == 0
def test_promote_nonauditors( mock_gagn, standard_graph, graph, users, groups, session, permissions # noqa: F811 ): """ Test expiration auditing and notification. """ assert graph.get_group_details("audited-team")["audited"] # # Ensure auditors promotion for all approvers # approver_roles = ["owner", "np-owner", "manager"] affected_users = set( ["*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**"]) for idx, role in enumerate(approver_roles): # Add non-auditor as an approver to an audited group add_member(groups["audited-team"], users["*****@*****.**"], role=role) graph.update_from_db(session) assert not affected_users.intersection(get_users(graph, "auditors")) # do the promotion logic background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) # Check that the users now added to auditors group graph.update_from_db(session) assert affected_users.intersection(get_users( graph, "auditors")) == affected_users unsent_emails = _get_unsent_emails_and_send(session) assert any([ 'Subject: Added as member to group "auditors"' in email.body and "To: [email protected]" in email.body for email in unsent_emails ]) assert any([ 'Subject: Added as member to group "auditors"' in email.body and "To: [email protected]" in email.body for email in unsent_emails ]) assert any([ 'Subject: Added as member to group "auditors"' in email.body and "To: [email protected]" in email.body for email in unsent_emails ]) audits = AuditLog.get_entries(session, action="nonauditor_promoted") assert len(audits) == len(affected_users) * (idx + 1) # reset for next iteration revoke_member(groups["audited-team"], users["*****@*****.**"]) for username in affected_users: revoke_member(groups["auditors"], users[username]) # # Ensure nonauditor, nonapprovers in audited groups do not get promoted # # first, run a promotion to get any other promotion that we don't # care about out of the way background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) prev_audit_log_count = len( AuditLog.get_entries(session, action="nonauditor_promoted")) member_roles = ["member"] for idx, role in enumerate(member_roles): # Add non-auditor as a non-approver to an audited group add_member(groups["audited-team"], users["*****@*****.**"], role=role) # do the promotion logic background = BackgroundProcessor(settings, None) background.promote_nonauditors(session) # Check that the user is not added to auditors group graph.update_from_db(session) assert "*****@*****.**" not in get_users(graph, "auditors") assert not any([ 'Subject: Added as member to group "auditors"' in email.body and "To: [email protected]" in email.body for email in _get_unsent_emails_and_send(session) ]) audits = AuditLog.get_entries(session, action="nonauditor_promoted") assert len(audits) == prev_audit_log_count revoke_member(groups["audited-team"], users["*****@*****.**"])
def test_tags(session, http_client, base_url, graph): # noqa: F811 perm = create_permission(session, TAG_EDIT) session.commit() create_permission(session, "it.literally.does.not.matter") session.commit() grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, TAG_EDIT), "*", ) grant_permission( session.query(Group).filter_by(groupname="all-teams").scalar(), get_permission(session, "it.literally.does.not.matter"), "*", ) tag = PublicKeyTag(name="tyler_was_here") tag.add(session) session.commit() tag = PublicKeyTag.get(session, name="tyler_was_here") grant_permission_to_tag(session, tag.id, perm.id, "prod") with pytest.raises(AssertionError): grant_permission_to_tag(session, tag.id, perm.id, "question?") user = session.query(User).filter_by(username="******").scalar() add_public_key(session, user, SSH_KEY_1) key = session.query(PublicKey).filter_by(user_id=user.id).scalar() add_tag_to_public_key(session, key, tag) user = session.query(User).filter_by(username="******").scalar() key = session.query(PublicKey).filter_by(user_id=user.id).scalar() assert ( len(get_public_key_permissions(session, key)) == 1 ), "The SSH Key should have only 1 permission" assert ( get_public_key_permissions(session, key)[0].name == TAG_EDIT ), "The SSH key's permission should be TAG_EDIT" assert ( get_public_key_permissions(session, key)[0].argument == "prod" ), "The SSH key's permission argument should be restricted to the tag's argument" assert len(user_permissions(session, user)) > 1, "The user should have more than 1 permission" graph.update_from_db(session) fe_url = url(base_url, "/users/{}".format(user.username)) resp = yield http_client.fetch(fe_url) assert resp.code == 200 body = json.loads(resp.body) pub_key = body["data"]["user"]["public_keys"][0] assert len(pub_key["tags"]) == 1, "The public key should only have 1 tag" assert pub_key["fingerprint"] == "e9:ae:c5:8f:39:9b:3a:9c:6a:b8:33:6b:cb:6f:ba:35" assert pub_key["fingerprint_sha256"] == "MP9uWaujW96EWxbjDtPdPWheoMDu6BZ8FZj0+CBkVWU" assert pub_key["tags"][0] == "tyler_was_here", "The public key should have the tag we gave it"