about summary refs log tree commit diff
path: root/tests/test_gn_auth_auth_flow.py
blob: ab26f10d2bd9af05c9868fe95104695df35a0141 (plain)
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
"""
Auth-flow integration tests for gn-auth.

Require live credentials set up by the CI test-session lifecycle
(create-test-users / create-test-oauth2-client).  Run with:

    pytest -m "gn_auth and auth_flow"

Environment variables:

    GN_TEST_EMAIL            admin test-user email
    GN_TEST_PASSWORD         admin test-user password
    GN_TEST_BASIC_EMAIL      unprivileged test-user email
    GN_TEST_BASIC_PASSWORD   unprivileged test-user password
    GN_OAUTH2_CLIENT_ID      OAuth2 client UUID
    GN_OAUTH2_CLIENT_SECRET  OAuth2 client secret
"""

import pytest

pytestmark = [pytest.mark.gn_auth, pytest.mark.auth_flow]


# ---------------------------------------------------------------------------
# POST /auth/token — password grant with valid credentials
# ---------------------------------------------------------------------------

class TestTokenGrant:
    """Password grant with valid admin credentials issues a usable token."""

    def _post_token(self, gn_auth_url, http, oauth2_credentials,
                    scope="profile group resource"):
        email, password, client_id, client_secret = oauth2_credentials
        return http.post(
            f"{gn_auth_url}/auth/token",
            json={
                "grant_type": "password",
                "username": email,
                "password": password,
                "scope": scope,
                "client_id": client_id,
                "client_secret": client_secret,
            },
            timeout=30,
        )

    def test_valid_credentials_return_200(
            self, gn_auth_url, http, oauth2_credentials):
        resp = self._post_token(gn_auth_url, http, oauth2_credentials)
        assert resp.status_code == 200, (
            f"Expected 200 from token endpoint, got {resp.status_code}: {resp.text}"
        )

    def test_response_contains_access_token(
            self, gn_auth_url, http, oauth2_credentials):
        resp = self._post_token(gn_auth_url, http, oauth2_credentials)
        data = resp.json()
        assert "access_token" in data, (
            f"Token response missing 'access_token': {data}"
        )

    def test_token_type_is_bearer(
            self, gn_auth_url, http, oauth2_credentials):
        resp = self._post_token(gn_auth_url, http, oauth2_credentials)
        token_type = resp.json().get("token_type", "")
        assert token_type.lower() == "bearer", (
            f"Expected token_type 'bearer', got: {token_type!r}"
        )

    def test_granted_scope_covers_requested(
            self, gn_auth_url, http, oauth2_credentials):
        requested = {"profile", "group", "resource"}
        resp = self._post_token(gn_auth_url, http, oauth2_credentials)
        data = resp.json()
        assert "scope" in data, f"Token response missing 'scope': {data}"
        granted = set(data["scope"].split())
        assert requested <= granted, (
            f"Requested scopes {requested} not all in granted scopes {granted}"
        )