Exemple #1
0
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"
Exemple #2
0
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"
Exemple #3
0
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
Exemple #4
0
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):
    # 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"
Exemple #7
0
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"
Exemple #9
0
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
Exemple #10
0
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"
Exemple #11
0
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"
Exemple #12
0
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