diff options
Diffstat (limited to 'tests/unit/auth/test_admin_users.py')
| -rw-r--r-- | tests/unit/auth/test_admin_users.py | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/tests/unit/auth/test_admin_users.py b/tests/unit/auth/test_admin_users.py new file mode 100644 index 0000000..eaa5bcd --- /dev/null +++ b/tests/unit/auth/test_admin_users.py @@ -0,0 +1,190 @@ +"""Tests for admin user-management: model functions and HTTP endpoints.""" +import pytest + +from gn_auth.auth.db import sqlite3 as db +from gn_auth.auth.authorisation.users.admin.models import ( + create_verified_user, + grant_sysadmin_role) + +from tests.unit.auth import conftest + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _credential_count(conn, user_id: str) -> int: + with db.cursor(conn) as cursor: + cursor.execute( + "SELECT COUNT(*) AS cnt FROM user_credentials WHERE user_id=?", + (user_id,)) + return cursor.fetchone()["cnt"] + + +@pytest.mark.unit_test +def test_create_verified_user_sets_verified_flag(conn_after_auth_migrations): + """ + GIVEN: a database with migrations applied + WHEN: create_verified_user is called with valid email, name and password + THEN: the returned user has verified=True and the flag is persisted in the DB + """ + conn = conn_after_auth_migrations + user = create_verified_user(conn, "new@example.org", "New User", "s3cr3t") + assert user.verified is True + with db.cursor(conn) as cursor: + cursor.execute( + "SELECT verified FROM users WHERE user_id=?", (str(user.user_id),)) + row = cursor.fetchone() + assert row is not None + assert bool(row["verified"]) is True + + +@pytest.mark.unit_test +def test_create_verified_user_has_no_roles(conn_after_auth_migrations): + """ + GIVEN: a database with migrations applied + WHEN: create_verified_user is called with valid email, name and password + THEN: the new user is assigned no roles + """ + conn = conn_after_auth_migrations + user = create_verified_user(conn, "noroles@example.org", "No Roles User", "s3cr3t") + with db.cursor(conn) as cursor: + cursor.execute( + "SELECT COUNT(*) AS cnt FROM user_roles WHERE user_id=?", + (str(user.user_id),)) + row = cursor.fetchone() + assert row["cnt"] == 0 + + +@pytest.mark.unit_test +def test_create_verified_user_stores_credentials(conn_after_auth_migrations): + """ + GIVEN: a database with migrations applied + WHEN: create_verified_user is called with valid email, name and password + THEN: a password credential row is stored for the new user + """ + conn = conn_after_auth_migrations + user = create_verified_user(conn, "creds@example.org", "Creds User", "s3cr3t") + assert _credential_count(conn, str(user.user_id)) == 1 + + +@pytest.mark.unit_test +def test_create_verified_user_raises_on_duplicate_email(conn_after_auth_migrations): + """ + GIVEN: a user already exists with a given email + WHEN: create_verified_user is called with the same email + THEN: an exception is raised + """ + conn = conn_after_auth_migrations + create_verified_user(conn, "dupe@example.org", "First User", "s3cr3t") + with pytest.raises(Exception): + create_verified_user(conn, "dupe@example.org", "Second User", "s3cr3t") + + +# --------------------------------------------------------------------------- +# HTTP endpoint tests: POST /auth/system/administration/users/create +# --------------------------------------------------------------------------- + +_NEW_USER_BODY = {"email": "newbie@example.org", "name": "Newbie", "password": "s3cr3t"} +_CREATE_URL = "/auth/system/administration/users/create" + + +@pytest.mark.unit_test +def test_create_user_endpoint_no_token_returns_401(fxtr_app): + """ + GIVEN: no Authorization header + WHEN: POST /auth/system/administration/users/create + THEN: 401 is returned + """ + with fxtr_app.test_client() as http: + res = http.post(_CREATE_URL, json=_NEW_USER_BODY) + assert res.status_code == 401 + + +@pytest.mark.unit_test +def test_create_user_endpoint_non_admin_returns_403(fxtr_app, mocker, fxtr_oauth2_clients): + """ + GIVEN: a valid token belonging to a non-admin user + WHEN: POST /auth/system/administration/users/create + THEN: 403 is returned + """ + _conn, clients = fxtr_oauth2_clients + user = conftest.TEST_USERS[3] # unaff@iliated.user — no privileges + mocker.patch( + "gn_auth.auth.system.admin.users.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( + _CREATE_URL, + json=_NEW_USER_BODY, + headers={"Authorization": "Bearer some-mocked-token"}) + assert res.status_code == 403 + + +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.system.admin.users.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_create_user_endpoint_admin_returns_201(fxtr_app, mocker, fxtr_oauth2_clients): + """ + GIVEN: a valid system-admin token and a valid request body + WHEN: POST /auth/system/administration/users/create + THEN: 201 is returned + """ + conn, clients = fxtr_oauth2_clients + _setup_admin_mock(conn, clients, mocker) + with fxtr_app.test_client() as http: + res = http.post( + _CREATE_URL, + json={**_NEW_USER_BODY, "password": "s3cr3tP4ssw0rd"}, + headers={"Authorization": "Bearer some-mocked-token"}) + assert res.status_code == 201 + + +@pytest.mark.unit_test +def test_create_user_endpoint_admin_returns_new_user(fxtr_app, mocker, fxtr_oauth2_clients): + """ + GIVEN: a valid system-admin token and a valid request body + WHEN: POST /auth/system/administration/users/create + THEN: the response body contains the new user's email and name + """ + conn, clients = fxtr_oauth2_clients + _setup_admin_mock(conn, clients, mocker) + with fxtr_app.test_client() as http: + res = http.post( + _CREATE_URL, + json={**_NEW_USER_BODY, "password": "s3cr3tP4ssw0rd"}, + headers={"Authorization": "Bearer some-mocked-token"}) + data = res.get_json() + assert data.get("email") == _NEW_USER_BODY["email"] + assert data.get("name") == _NEW_USER_BODY["name"] + assert res.status_code == 201 + + +@pytest.mark.unit_test +def test_create_user_endpoint_short_password_returns_400( + fxtr_app, mocker, fxtr_oauth2_clients): + """ + GIVEN: a valid system-admin token and a request body with a short password + WHEN: POST /auth/system/administration/users/create + THEN: 400 is returned (password must be at least 8 characters) + """ + conn, clients = fxtr_oauth2_clients + _setup_admin_mock(conn, clients, mocker) + with fxtr_app.test_client() as http: + res = http.post( + _CREATE_URL, + json={**_NEW_USER_BODY, "password": "short"}, + headers={"Authorization": "Bearer some-mocked-token"}) + assert res.status_code == 400 |
