Skip to content

Commit

Permalink
Merge pull request #1050 from ScilifelabDataCentre/list-users-in-unit
Browse files Browse the repository at this point in the history
Add new endpoint for listing unit users
  • Loading branch information
i-oden authored Mar 14, 2022
2 parents d3fb38d + 66dd210 commit ccca27f
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ Please add a _short_ line describing the PR you make, if the PR implements a spe
- Introduce a separate error message if someone tried to add an unit user to projects individually. ([#1039](https://github.com/ScilifelabDataCentre/dds_web/pull/1039))
- Catch KeyNotFoundError when user tries to give access to a project they themselves do not have access to ([#1045](https://github.com/ScilifelabDataCentre/dds_web/pull/1045))
- Display an error message when the user makes too many authentication requests. ([#1034](https://github.com/ScilifelabDataCentre/dds_web/pull/1034))
- New endpoint for Unit Personnel and Admins to list the other Unit Personnel / Admins within their project ([#1050](https://github.com/ScilifelabDataCentre/dds_web/pull/1050))
1 change: 1 addition & 0 deletions dds_web/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def output_json(data, code, headers=None):
api.add_resource(user.DeleteUserSelf, "/user/delete_self", endpoint="delete_user_self")
api.add_resource(user.RemoveUserAssociation, "/user/access/revoke", endpoint="revoke_from_project")
api.add_resource(user.UserActivation, "/user/activation", endpoint="user_activation")
api.add_resource(user.UnitUsers, "/unit/users", endpoint="unit_users")

# Invoicing ############################################################################ Invoicing #
api.add_resource(user.ShowUsage, "/usage", endpoint="usage")
32 changes: 32 additions & 0 deletions dds_web/api/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -930,3 +930,35 @@ def get(self):
},
"project_usage": usage,
}


class UnitUsers(flask_restful.Resource):
"""List unit users."""

@auth.login_required(role=["Unit Admin", "Unit Personnel"])
@logging_bind_request
def get(self):
"""Get and return unit users within the unit the current user is connected to."""
unit_users = {}

if not auth.current_user().is_active:
raise ddserr.AccessDeniedError(
message=(
"Your account has been deactivated. "
"You cannot list the users within your unit."
)
)

keys = ["Name", "Username", "Email", "Role", "Active"]
unit_users = [
{
"Name": user.name,
"Username": user.username,
"Email": user.primary_email,
"Role": user.role,
"Active": user.is_active,
}
for user in auth.current_user().unit.users
]

return {"users": unit_users, "keys": keys, "unit": auth.current_user().unit.name}
3 changes: 3 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class DDSEndpoint:
USER_DELETE_SELF = BASE_ENDPOINT + "/user/delete_self"
USER_CONFIRM_DELETE = "/confirm_deletion/"

# List users
LIST_UNIT_USERS = BASE_ENDPOINT + "/unit/users"

# Authentication - user and project
ENCRYPTED_TOKEN = BASE_ENDPOINT + "/user/encrypted_token"
SECOND_FACTOR = BASE_ENDPOINT + "/user/second_factor"
Expand Down
90 changes: 90 additions & 0 deletions tests/test_users_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# IMPORTS ################################################################################ IMPORTS #

# Standard library
import http

# Own
from dds_web import db
from dds_web.api import user
from dds_web.database import models
import tests

# CONFIG ################################################################################## CONFIG #

users = {
"Researcher": "researchuser",
"Unit Personnel": "unituser",
"Unit Admin": "unitadmin",
"Super Admin": "superadmin",
}

# TESTS #################################################################################### TESTS #


def get_token(username, client):
return tests.UserAuth(tests.USER_CREDENTIALS[username]).token(client)


def test_list_unitusers_with_researcher(client):
"""Researchers cannot list unit users."""
token = get_token(username=users["Researcher"], client=client)
response = client.get(tests.DDSEndpoint.LIST_UNIT_USERS, headers=token)
assert response.status_code == http.HTTPStatus.FORBIDDEN


def test_list_unitusers_with_super_admin(client):
"""Super admins will be able to list unit users, but not right now."""
token = get_token(username=users["Super Admin"], client=client)
response = client.get(tests.DDSEndpoint.LIST_UNIT_USERS, headers=token)
assert response.status_code == http.HTTPStatus.FORBIDDEN


def test_list_unitusers_with_unit_personnel_and_admin_deactivated(client):
"""Unit Personnel should be able to list the users within a unit."""
# Deactivate user
for u in ["Unit Personnel", "Unit Admin"]:
# Get token
token = get_token(username=users[u], client=client)

user = models.User.query.get(users[u])
user.active = False
db.session.commit()

# Try to list users - should only work if active - not now
response = client.get(tests.DDSEndpoint.LIST_UNIT_USERS, headers=token)

# Unauth and not forbidden because the user object is not returned from the token
assert response.status_code == http.HTTPStatus.UNAUTHORIZED


def test_list_unitusers_with_unit_personnel_and_admin_ok(client):
# Active unit users should be able to list unit users
for u in ["Unit Personnel", "Unit Admin"]:
# Get token
token = get_token(username=users[u], client=client)

# Get users
response = client.get(tests.DDSEndpoint.LIST_UNIT_USERS, headers=token)
assert response.status_code == http.HTTPStatus.OK

keys_in_response = response.json["keys"]
unit_in_response = response.json["unit"]
users_in_response = response.json["users"]

assert keys_in_response

user_object = models.User.query.get(users[u])
assert user_object.unit.name == unit_in_response

all_users = user_object.unit.users

# ["Name", "Username", "Email", "Role", "Active"]
for dbrow in user_object.unit.users:
expected = {
"Name": dbrow.name,
"Username": dbrow.username,
"Email": dbrow.primary_email,
"Role": dbrow.role,
"Active": dbrow.is_active,
}
assert expected in users_in_response

0 comments on commit ccca27f

Please sign in to comment.