1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
"""Tests for admin role-assignment HTTP endpoints."""
import pytest
from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.authorisation.users.admin.models import grant_sysadmin_role
from tests.unit.auth import conftest
from tests.unit.auth.fixtures.resource_fixtures import SYSTEM_RESOURCE
# Body used in all role-assign tests — assigning system-administrator on the
# system resource is a real, migrations-seeded combination.
_ASSIGN_BODY = {
"role_name": "system-administrator",
"resource_id": str(SYSTEM_RESOURCE.resource_id)
}
# Target user for assignment: unaff@iliated.user (no roles initially)
_TARGET_USER = conftest.TEST_USERS[3]
def _setup_admin_mock(conn, clients, mocker):
"""Grant sysadmin role and mock the token for sys@admin.user."""
admin = conftest.TEST_USERS[4]
with db.cursor(conn) as cursor:
grant_sysadmin_role(cursor, admin)
mocker.patch(
"gn_auth.auth.authorisation.users.views.require_oauth.acquire",
conftest.get_tokeniser(
admin,
tuple(c for c in clients if c.user == admin)[0]))
return admin
@pytest.mark.unit_test
def test_assign_role_no_token_returns_401(fxtr_app):
"""
GIVEN: no Authorization header
WHEN: POST /auth/user/<uid>/roles/assign
THEN: 401 is returned
"""
with fxtr_app.test_client() as http:
res = http.post(
f"/auth/user/{_TARGET_USER.user_id}/roles/assign",
json=_ASSIGN_BODY)
assert res.status_code == 401
@pytest.mark.unit_test
def test_assign_role_non_admin_returns_403(fxtr_app, mocker, fxtr_oauth2_clients):
"""
GIVEN: a valid token belonging to a non-admin user
WHEN: POST /auth/user/<uid>/roles/assign
THEN: 403 is returned
"""
_conn, clients = fxtr_oauth2_clients
user = conftest.TEST_USERS[3] # unaff@iliated.user — no privileges
mocker.patch(
"gn_auth.auth.authorisation.users.views.require_oauth.acquire",
conftest.get_tokeniser(
user,
tuple(c for c in clients if c.user == user)[0]))
with fxtr_app.test_client() as http:
res = http.post(
f"/auth/user/{_TARGET_USER.user_id}/roles/assign",
json=_ASSIGN_BODY,
headers={"Authorization": "Bearer some-mocked-token"})
assert res.status_code == 403
def _revoke_assigned_role(conn):
"""Remove the role row written by the success tests.
Keeps the DB in the state the fixtures expect — no user_roles entry for
_TARGET_USER — so teardown and any subsequent queries are not surprised.
"""
with db.cursor(conn) as cursor:
cursor.execute(
"DELETE FROM user_roles "
"WHERE user_id=? "
"AND role_id=(SELECT role_id FROM roles WHERE role_name=?) "
"AND resource_id=?",
(str(_TARGET_USER.user_id),
_ASSIGN_BODY["role_name"],
_ASSIGN_BODY["resource_id"]))
@pytest.mark.unit_test
def test_assign_role_admin_returns_200(fxtr_app, mocker, fxtr_oauth2_clients):
"""
GIVEN: a valid system-admin token and a valid role/resource body
WHEN: POST /auth/user/<uid>/roles/assign
THEN: 200 is returned
"""
conn, clients = fxtr_oauth2_clients
_setup_admin_mock(conn, clients, mocker)
try:
with fxtr_app.test_client() as http:
res = http.post(
f"/auth/user/{_TARGET_USER.user_id}/roles/assign",
json=_ASSIGN_BODY,
headers={"Authorization": "Bearer some-mocked-token"})
assert res.status_code == 200
finally:
_revoke_assigned_role(conn)
@pytest.mark.unit_test
def test_assign_role_persists_to_db(fxtr_app, mocker, fxtr_oauth2_clients):
"""
GIVEN: a valid system-admin token and a valid role/resource body
WHEN: POST /auth/user/<uid>/roles/assign
THEN: the user_roles row is present in the DB for that user/role/resource
"""
conn, clients = fxtr_oauth2_clients
_setup_admin_mock(conn, clients, mocker)
try:
with fxtr_app.test_client() as http:
http.post(
f"/auth/user/{_TARGET_USER.user_id}/roles/assign",
json=_ASSIGN_BODY,
headers={"Authorization": "Bearer some-mocked-token"})
with db.cursor(conn) as cursor:
cursor.execute(
"SELECT COUNT(*) AS cnt FROM user_roles "
"INNER JOIN roles ON user_roles.role_id=roles.role_id "
"WHERE user_roles.user_id=? "
"AND roles.role_name=? "
"AND user_roles.resource_id=?",
(str(_TARGET_USER.user_id),
_ASSIGN_BODY["role_name"],
_ASSIGN_BODY["resource_id"]))
assert cursor.fetchone()["cnt"] == 1
finally:
_revoke_assigned_role(conn)
|