diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_gn_auth_auth_flow.py | 120 | ||||
| -rw-r--r-- | tests/test_gn_auth_smoke.py | 1 |
2 files changed, 114 insertions, 7 deletions
diff --git a/tests/test_gn_auth_auth_flow.py b/tests/test_gn_auth_auth_flow.py index cb164cd..c031584 100644 --- a/tests/test_gn_auth_auth_flow.py +++ b/tests/test_gn_auth_auth_flow.py @@ -86,7 +86,7 @@ class TestTokenGrant: class TestTokenGrantRejection: """Password grant with bad credentials must return 401.""" - def test_wrong_password_returns_401( + def test_wrong_password_returns_400( self, gn_auth_url, http, oauth2_credentials): email, _password, client_id, client_secret = oauth2_credentials resp = http.post( @@ -101,11 +101,11 @@ class TestTokenGrantRejection: }, timeout=30, ) - assert resp.status_code == 401, ( - f"Expected 401 for wrong password, got {resp.status_code}: {resp.text}" + assert resp.status_code == 400, ( + f"Expected 400 for wrong password, got {resp.status_code}: {resp.text}" ) - def test_unknown_email_returns_401( + def test_unknown_email_returns_400( self, gn_auth_url, http, oauth2_credentials): _email, password, client_id, client_secret = oauth2_credentials resp = http.post( @@ -120,8 +120,8 @@ class TestTokenGrantRejection: }, timeout=30, ) - assert resp.status_code == 401, ( - f"Expected 401 for unknown email, got {resp.status_code}: {resp.text}" + assert resp.status_code == 400, ( + f"Expected 400 for unknown email, got {resp.status_code}: {resp.text}" ) @@ -191,3 +191,111 @@ class TestUserProfileWithoutToken: f"Expected 401 from /auth/user/ with garbage token, " f"got {resp.status_code}: {resp.text}" ) + + +# --------------------------------------------------------------------------- +# POST /auth/user/masquerade/ — role-based privilege check +# +# The masquerade endpoint requires the "system:user:masquerade" privilege +# which only system-administrators hold. Both admin and basic users can +# obtain a token with "masquerade" scope (the test client supports it), but +# gn-auth's can_masquerade decorator checks the user's roles and raises +# ForbiddenAccess (→ 403) for users without the privilege. +# --------------------------------------------------------------------------- + +@pytest.fixture(scope="session") +def admin_masquerade_token(gn_auth_url, http, oauth2_credentials): + """Admin Bearer token with masquerade scope.""" + email, password, client_id, client_secret = oauth2_credentials + resp = http.post( + f"{gn_auth_url}/auth/token", + json={ + "grant_type": "password", + "username": email, + "password": password, + "scope": "profile group resource user masquerade", + "client_id": client_id, + "client_secret": client_secret, + }, + timeout=30, + ) + assert resp.status_code == 200, f"Admin masquerade token request failed: {resp.text}" + return resp.json()["access_token"] + + +@pytest.fixture(scope="session") +def basic_masquerade_token(gn_auth_url, http, basic_oauth2_credentials): + """Unprivileged user Bearer token with masquerade scope.""" + email, password, client_id, client_secret = basic_oauth2_credentials + resp = http.post( + f"{gn_auth_url}/auth/token", + json={ + "grant_type": "password", + "username": email, + "password": password, + "scope": "profile group resource user masquerade", + "client_id": client_id, + "client_secret": client_secret, + }, + timeout=30, + ) + assert resp.status_code == 200, f"Basic masquerade token request failed: {resp.text}" + return resp.json()["access_token"] + + +@pytest.fixture(scope="session") +def basic_user_id(gn_auth_url, http, basic_access_token): + """User ID of the unprivileged test user, fetched via GET /auth/user/.""" + resp = http.get( + f"{gn_auth_url}/auth/user/", + headers={"Authorization": f"Bearer {basic_access_token}"}, + timeout=30, + ) + assert resp.status_code == 200, f"Could not fetch basic user profile: {resp.text}" + return resp.json()["user_id"] + + +class TestMasqueradePrivilege: + """POST /auth/user/masquerade/ enforces the system:user:masquerade privilege.""" + + def test_basic_user_cannot_masquerade_returns_403( + self, gn_auth_url, http, basic_masquerade_token, basic_user_id, + access_token): + # Basic user tries to masquerade as the admin; the admin's user_id is + # obtained via the profile endpoint. + admin_profile = http.get( + f"{gn_auth_url}/auth/user/", + headers={"Authorization": f"Bearer {access_token}"}, + timeout=30, + ) + assert admin_profile.status_code == 200 + admin_id = admin_profile.json()["user_id"] + + resp = http.post( + f"{gn_auth_url}/auth/user/masquerade/", + json={"masquerade_as": admin_id}, + headers={"Authorization": f"Bearer {basic_masquerade_token}"}, + timeout=30, + ) + assert resp.status_code == 403, ( + f"Expected 403 when unprivileged user attempts masquerade, " + f"got {resp.status_code}: {resp.text}" + ) + + def test_admin_can_masquerade_as_basic_user( + self, gn_auth_url, http, admin_masquerade_token, basic_user_id): + resp = http.post( + f"{gn_auth_url}/auth/user/masquerade/", + json={"masquerade_as": basic_user_id}, + headers={"Authorization": f"Bearer {admin_masquerade_token}"}, + timeout=30, + ) + assert resp.status_code == 200, ( + f"Expected 200 when admin masquerades as basic user, " + f"got {resp.status_code}: {resp.text}" + ) + data = resp.json() + assert "masquerade_as" in data, f"Missing 'masquerade_as' in response: {data}" + assert data["masquerade_as"]["user"]["user_id"] == basic_user_id, ( + "Masquerade response user_id does not match target user" + ) diff --git a/tests/test_gn_auth_smoke.py b/tests/test_gn_auth_smoke.py index 7dd9645..4ff067a 100644 --- a/tests/test_gn_auth_smoke.py +++ b/tests/test_gn_auth_smoke.py @@ -194,7 +194,6 @@ class TestDataAuthorisation: # GET /auth/system/roles (with valid token — Phase 2) # --------------------------------------------------------------------------- -@pytest.mark.auth_flow class TestSystemRolesAuthenticated: def test_returns_200_with_token(self, gn_auth_url, http, access_token): resp = http.get( |
