def test_fe_password_delete(session, users, http_client, base_url): user = users["*****@*****.**"] fe_url = url(base_url, "/users/{}/passwords/add".format(user.username)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"name": "test", "password": TEST_PASSWORD}), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "The user should have a password now" assert user_passwords(session, user)[0].name == "test", "The password should have the name given" assert ( user_passwords(session, user)[0].password_hash != TEST_PASSWORD ), "The password should not be available as plain text" user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url(base_url, "/users/{}/passwords/add".format(user.username)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({"name": "test", "password": TEST_PASSWORD}), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert ( len(user_passwords(session, user)) == 1 ), "The user should have a password now (duplicate names are permitted for distinct users)" assert user_passwords(session, user)[0].name == "test", "The password should have the name given" assert ( user_passwords(session, user)[0].password_hash != TEST_PASSWORD ), "The password should not be available as plain text" with pytest.raises(HTTPError): user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url( base_url, "/users/{}/passwords/{}/delete".format(user.username, user_passwords(session, user)[0].id) ) resp = yield http_client.fetch(fe_url, method="POST", body="", headers={"X-Grouper-User": "******"}) user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url(base_url, "/users/{}/passwords/{}/delete".format(user.username, user_passwords(session, user)[0].id)) resp = yield http_client.fetch(fe_url, method="POST", body="", headers={"X-Grouper-User": user.username}) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 0, "The password should have been deleted" user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "Other user's passwords should not have been deleted"
def test_passwords_api(session, users, http_client, base_url, graph): 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 get_user_view_template_vars(session, actor, user, graph): # type: (Session, User, User, GroupGraph) -> Dict[str, Any] # TODO(cbguder): get around circular dependencies from grouper.fe.handlers.user_disable import UserDisable from grouper.fe.handlers.user_enable import UserEnable ret = {} # type: Dict[str, Any] if user.is_service_account: ret["can_control"] = can_manage_service_account( session, user.service_account, actor ) or user_is_user_admin(session, actor) ret["can_disable"] = ret["can_control"] ret["can_enable"] = user_is_user_admin(session, actor) ret["can_enable_preserving_membership"] = user_is_user_admin(session, actor) ret["account"] = user.service_account else: ret["can_control"] = user.name == actor.name or user_is_user_admin(session, actor) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable_preserving_membership"] = UserEnable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access_without_membership(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate(session, actor).count() _, ret["num_pending_perm_requests"] = get_requests( session, status="pending", limit=1, offset=0, owner=actor ) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell_metadata = get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) ret["shell"] = shell_metadata.data_value if shell_metadata else "No shell configured" github_username = get_user_metadata_by_key(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY) ret["github_username"] = github_username.data_value if github_username else "(Unset)" ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [ {"name": g.name, "type": "Group", "role": ge._role} for g, ge in group_edge_list ] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens if user.is_service_account: service_account = user.service_account ret["permissions"] = service_account_permissions(session, service_account) else: ret["permissions"] = user_md.get("permissions", []) for permission in ret["permissions"]: permission["granted_on"] = datetime.fromtimestamp(permission["granted_on"]) return ret
def get_user_view_template_vars(session, actor, user, graph): # TODO(cbguder): get around circular dependencies from grouper.fe.handlers.user_disable import UserDisable from grouper.fe.handlers.user_enable import UserEnable ret = {} if user.is_service_account: ret["can_control"] = ( can_manage_service_account(session, user.service_account, actor) or user_is_user_admin(session, actor) ) ret["can_disable"] = ret["can_control"] ret["can_enable"] = user_is_user_admin(session, actor) ret["account"] = user.service_account else: ret["can_control"] = (user.name == actor.name or user_is_user_admin(session, actor)) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate(session, actor).count() _, ret["num_pending_perm_requests"] = get_requests_by_owner(session, actor, status='pending', limit=1, offset=0) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell = (get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY).data_value if get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) else "No shell configured") ret["shell"] = shell ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [{'name': g.name, 'type': 'Group', 'role': ge._role} for g, ge in group_edge_list] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) for key in ret["public_keys"]: key.tags = get_public_key_tags(session, key) key.pretty_permissions = ["{} ({})".format(perm.name, perm.argument if perm.argument else "unargumented") for perm in get_public_key_permissions(session, key)] ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens if user.is_service_account: service_account = user.service_account ret["permissions"] = service_account_permissions(session, service_account) else: ret["permissions"] = user_md.get('permissions', []) return ret
def test_fe_password_delete(session, users, http_client, base_url): user = users['*****@*****.**'] fe_url = url(base_url, '/users/{}/passwords/add'.format(user.username)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': "test", "password": TEST_PASSWORD}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "The user should have a password now" assert user_passwords(session, user)[0].name == "test", "The password should have the name given" assert user_passwords(session, user)[0].password_hash != TEST_PASSWORD, "The password should not be available as plain text" user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url(base_url, '/users/{}/passwords/add'.format(user.username)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': "test", "password": TEST_PASSWORD}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "The user should have a password now (duplicate names are permitted for distinct users)" assert user_passwords(session, user)[0].name == "test", "The password should have the name given" assert user_passwords(session, user)[0].password_hash != TEST_PASSWORD, "The password should not be available as plain text" with pytest.raises(HTTPError): user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url(base_url, '/users/{}/passwords/{}/delete'.format(user.username, user_passwords(session, user)[0].id)) resp = yield http_client.fetch(fe_url, method="POST", body="", headers={'X-Grouper-User': "******"}) user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url(base_url, '/users/{}/passwords/{}/delete'.format(user.username, user_passwords(session, user)[0].id)) resp = yield http_client.fetch(fe_url, method="POST", body="", headers={'X-Grouper-User': user.username}) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 0, "The password should have been deleted" user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "Other user's passwords should not have been deleted"
def test_passwords(session, users): user = users["*****@*****.**"] 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" password = user_passwords(session, user)[0] assert password.name == "test", "The password should have the name we gave it" assert password.password_hash != TEST_PASSWORD, "The password should not be what is passed in" assert password.check_password(TEST_PASSWORD), "The password should validate when given the same password" assert not password.check_password("sadfjhsdf"), "Incorrect passwords should fail" add_new_user_password(session, "test2", TEST_PASSWORD, user.id) assert len(user_passwords(session, user)) == 2, "The user should have 2 passwords" password2 = user_passwords(session, user)[1] assert password2.name == "test2", "The password should have the name we gave it" assert password2.password_hash != TEST_PASSWORD, "The password should not be what is passed in" assert password2.check_password(TEST_PASSWORD), "The password should validate when given the same password" assert not password2.check_password("sadfjhsdf"), "Incorrect passwords should fail" with pytest.raises(PasswordAlreadyExists): add_new_user_password(session, "test", TEST_PASSWORD, user.id) session.rollback() # Technically there's a very very very small O(1/2^160) chance that this will fail for a correct implementation assert ( password.password_hash != password2.password_hash ), "2 passwords that are identical should hash differently because of the salts" delete_user_password(session, "test", user.id) assert len(user_passwords(session, user)) == 1, "The user should only have a single password" assert user_passwords(session, user)[0].name == "test2", "The password named test should have been deleted"
def test_passwords(session, users): user = users['*****@*****.**'] 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" password = user_passwords(session, user)[0] assert password.name == "test", "The password should have the name we gave it" assert password.password_hash != TEST_PASSWORD, "The password should not be what is passed in" assert password.check_password(TEST_PASSWORD), "The password should validate when given the same password" assert not password.check_password("sadfjhsdf"), "Incorrect passwords should fail" add_new_user_password(session, "test2", TEST_PASSWORD, user.id) assert len(user_passwords(session, user)) == 2, "The user should have 2 passwords" password2 = user_passwords(session, user)[1] assert password2.name == "test2", "The password should have the name we gave it" assert password2.password_hash != TEST_PASSWORD, "The password should not be what is passed in" assert password2.check_password(TEST_PASSWORD), "The password should validate when given the same password" assert not password2.check_password("sadfjhsdf"), "Incorrect passwords should fail" with pytest.raises(PasswordAlreadyExists): add_new_user_password(session, "test", TEST_PASSWORD, user.id) session.rollback() # Technically there's a very very very small O(1/2^160) chance that this will fail for a correct implementation assert password.password_hash != password2.password_hash, "2 passwords that are identical should hash differently because of the salts" delete_user_password(session, "test", user.id) assert len(user_passwords(session, user)) == 1, "The user should only have a single password" assert user_passwords(session, user)[0].name == "test2", "The password named test should have been deleted"
def get_user_view_template_vars(session, actor, user, graph): ret = {} ret["can_control"] = (user.name == actor.name or user_is_user_admin(session, actor)) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate( session, actor).count() _, ret["num_pending_perm_requests"] = get_requests_by_owner( session, actor, status='pending', limit=1, offset=0) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell = (get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY).data_value if get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) else "No shell configured") ret["shell"] = shell ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [{ 'name': g.name, 'type': 'Group', 'role': ge._role } for g, ge in group_edge_list] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) for key in ret["public_keys"]: key.tags = get_public_key_tags(session, key) key.pretty_permissions = [ "{} ({})".format( perm.name, perm.argument if perm.argument else "unargumented") for perm in get_public_key_permissions(session, key) ] ret["permissions"] = user_md.get('permissions', []) ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens return ret
def test_fe_password_add(session, users, http_client, base_url): user = users['*****@*****.**'] fe_url = url(base_url, '/users/{}/passwords/add'.format(user.username)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': "test", "password": TEST_PASSWORD}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "The user should have a password now" assert user_passwords(session, user)[0].name == "test", "The password should have the name given" assert user_passwords(session, user)[0].password_hash != TEST_PASSWORD, "The password should not be available as plain text" with pytest.raises(HTTPError): fe_url = url(base_url, '/users/{}/passwords/add'.format(user.username)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': "test", "password": TEST_PASSWORD}), headers={'X-Grouper-User': "******"}) fe_url = url(base_url, '/users/{}/passwords/add'.format(user.username)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': "test", "password": TEST_PASSWORD}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "Adding a password with the same name should fail" user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url(base_url, '/users/{}/passwords/add'.format(user.username)) resp = yield http_client.fetch(fe_url, method="POST", body=urlencode({'name': "test", "password": TEST_PASSWORD}), headers={'X-Grouper-User': user.username}) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords(session, user)) == 1, "The user should have a password now (duplicate names are permitted for distinct users)" assert user_passwords(session, user)[0].name == "test", "The password should have the name given" assert user_passwords(session, user)[0].password_hash != TEST_PASSWORD, "The password should not be available as plain text"
def test_fe_password_add(session, users, http_client, base_url): # noqa: F811 user = users["*****@*****.**"] fe_url = url(base_url, "/users/{}/passwords/add".format(user.username)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "name": "test", "password": TEST_PASSWORD }), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert len(user_passwords( session, user)) == 1, "The user should have a password now" assert (user_passwords( session, user)[0].name == "test"), "The password should have the name given" assert (user_passwords(session, user)[0].password_hash != TEST_PASSWORD ), "The password should not be available as plain text" with pytest.raises(HTTPError): fe_url = url(base_url, "/users/{}/passwords/add".format(user.username)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "name": "test", "password": TEST_PASSWORD }), headers={"X-Grouper-User": "******"}, ) fe_url = url(base_url, "/users/{}/passwords/add".format(user.username)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "name": "test", "password": TEST_PASSWORD }), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert (len(user_passwords( session, user)) == 1), "Adding a password with the same name should fail" user = session.query(User).filter_by(name="*****@*****.**").scalar() fe_url = url(base_url, "/users/{}/passwords/add".format(user.username)) resp = yield http_client.fetch( fe_url, method="POST", body=urlencode({ "name": "test", "password": TEST_PASSWORD }), headers={"X-Grouper-User": user.username}, ) assert resp.code == 200 user = session.query(User).filter_by(name="*****@*****.**").scalar() assert ( len(user_passwords(session, user)) == 1 ), "The user should have a password now (duplicate names are permitted for distinct users)" assert (user_passwords( session, user)[0].name == "test"), "The password should have the name given" assert (user_passwords(session, user)[0].password_hash != TEST_PASSWORD ), "The password should not be available as plain text"
def get_user_view_template_vars(session, actor, user, graph): # type: (Session, User, User, GroupGraph) -> Dict[str, Any] # TODO(cbguder): get around circular dependencies from grouper.fe.handlers.user_disable import UserDisable from grouper.fe.handlers.user_enable import UserEnable ret = {} # type: Dict[str, Any] if user.is_service_account: ret["can_control"] = can_manage_service_account( session, user.service_account, actor ) or user_is_user_admin(session, actor) ret["can_disable"] = ret["can_control"] ret["can_enable"] = user_is_user_admin(session, actor) ret["can_enable_preserving_membership"] = user_is_user_admin(session, actor) ret["account"] = user.service_account else: ret["can_control"] = user.name == actor.name or user_is_user_admin(session, actor) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable_preserving_membership"] = UserEnable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access_without_membership(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate(session, actor).count() _, ret["num_pending_perm_requests"] = get_requests( session, status="pending", limit=1, offset=0, owner=actor ) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell = ( get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY).data_value if get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) else "No shell configured" ) ret["shell"] = shell github_username = get_user_metadata_by_key(session, user.id, USER_METADATA_GITHUB_USERNAME_KEY) ret["github_username"] = github_username.data_value if github_username else "(Unset)" ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [ {"name": g.name, "type": "Group", "role": ge._role} for g, ge in group_edge_list ] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens if user.is_service_account: service_account = user.service_account ret["permissions"] = service_account_permissions(session, service_account) else: ret["permissions"] = user_md.get("permissions", []) for permission in ret["permissions"]: permission["granted_on"] = datetime.fromtimestamp(permission["granted_on"]) return ret
def get_user_view_template_vars(session, actor, user, graph): # TODO(cbguder): get around circular dependencies from grouper.fe.handlers.user_disable import UserDisable from grouper.fe.handlers.user_enable import UserEnable ret = {} if user.is_service_account: ret["can_control"] = can_manage_service_account( session, user.service_account, actor ) or user_is_user_admin(session, actor) ret["can_disable"] = ret["can_control"] ret["can_enable"] = user_is_user_admin(session, actor) ret["can_enable_preserving_membership"] = user_is_user_admin(session, actor) ret["account"] = user.service_account else: ret["can_control"] = user.name == actor.name or user_is_user_admin(session, actor) ret["can_disable"] = UserDisable.check_access(session, actor, user) ret["can_enable_preserving_membership"] = UserEnable.check_access(session, actor, user) ret["can_enable"] = UserEnable.check_access_without_membership(session, actor, user) if user.id == actor.id: ret["num_pending_group_requests"] = user_requests_aggregate(session, actor).count() _, ret["num_pending_perm_requests"] = get_requests( session, status="pending", limit=1, offset=0, owner=actor ) else: ret["num_pending_group_requests"] = None ret["num_pending_perm_requests"] = None try: user_md = graph.get_user_details(user.name) except NoSuchUser: # Either user is probably very new, so they have no metadata yet, or # they're disabled, so we've excluded them from the in-memory graph. user_md = {} shell = ( get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY).data_value if get_user_metadata_by_key(session, user.id, USER_METADATA_SHELL_KEY) else "No shell configured" ) ret["shell"] = shell ret["open_audits"] = user_open_audits(session, user) group_edge_list = get_groups_by_user(session, user) if user.enabled else [] ret["groups"] = [ {"name": g.name, "type": "Group", "role": ge._role} for g, ge in group_edge_list ] ret["passwords"] = user_passwords(session, user) ret["public_keys"] = get_public_keys_of_user(session, user.id) for key in ret["public_keys"]: key.tags = get_public_key_tags(session, key) key.pretty_permissions = [ "{} ({})".format(perm.name, perm.argument if perm.argument else "unargumented") for perm in get_public_key_permissions(session, key) ] ret["log_entries"] = get_log_entries_by_user(session, user) ret["user_tokens"] = user.tokens if user.is_service_account: service_account = user.service_account ret["permissions"] = service_account_permissions(session, service_account) else: ret["permissions"] = user_md.get("permissions", []) return ret