Skip to content

Commit f39cf12

Browse files
committed
update
Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
1 parent 64f5cf1 commit f39cf12

1 file changed

Lines changed: 60 additions & 30 deletions

File tree

services/core/auth/tests/integration/test_scoped_access_keys.py

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,52 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
import uuid
5+
from collections.abc import Callable
56
from pathlib import Path
7+
from time import monotonic, sleep
68
from unittest.mock import patch
79

10+
import pytest
811
from cryptography.hazmat.primitives import serialization
912
from cryptography.hazmat.primitives.asymmetric import rsa
1013
from fastapi.testclient import TestClient
14+
from httpx import Response
1115
from nmp.common.auth.access_keys import public_jwk_from_private_key_pem, validate_access_key_token
1216
from nmp.common.auth.jwt import TokenClaims
1317
from nmp.common.config import AuthConfig
1418
from nmp.common.config.base import AccessKeyConfig, TokenSigningConfig
1519
from nmp.core.auth.config import AuthServiceConfig
1620
from nmp.testing.client import create_test_client
1721

22+
# Keep this test in its own xdist group while embedded PDP uses process-wide
23+
# wasmtime state. This limits cross-test scheduling noise under --dist loadgroup.
24+
pytestmark = pytest.mark.xdist_group("auth_scoped_access_keys")
25+
1826
ACCESS_KEYS_PATH = "/apis/auth/v2/access-keys"
1927
IAM_ROLE_BINDINGS_PATH = "/apis/auth/v2/iam/role-bindings"
2028
WORKSPACES_PATH = "/apis/entities/v2/workspaces"
2129
SERVICE_HEADERS = {"X-NMP-Principal-Id": "service:integration-test"}
30+
AUTHZ_PROPAGATION_TIMEOUT_SECONDS = 5.0
31+
AUTHZ_PROPAGATION_POLL_INTERVAL_SECONDS = 0.05
32+
33+
34+
def _wait_for_authorization_response(
35+
request: Callable[[], Response],
36+
*,
37+
expected_status_code: int,
38+
) -> Response:
39+
deadline = monotonic() + AUTHZ_PROPAGATION_TIMEOUT_SECONDS
40+
response = request()
41+
while response.status_code != expected_status_code and monotonic() < deadline:
42+
sleep(AUTHZ_PROPAGATION_POLL_INTERVAL_SECONDS)
43+
response = request()
44+
45+
if response.status_code != expected_status_code:
46+
raise AssertionError(
47+
f"Timed out waiting for authorization response {expected_status_code}; "
48+
f"last response {response.status_code}: {response.text}"
49+
)
50+
return response
2251

2352

2453
def _tamper_jwt(token: str) -> str:
@@ -57,8 +86,8 @@ def _auth_configs(private_key_file: str) -> tuple[AuthConfig, AuthServiceConfig]
5786
)
5887
service_config = AuthServiceConfig(
5988
**shared_config.model_dump(),
60-
policy_data_refresh_interval=0.05,
61-
bundle_cache_seconds=0,
89+
policy_data_refresh_interval=0.2,
90+
bundle_cache_seconds=0.1,
6291
admin_email="admin@example.com",
6392
)
6493
return shared_config, service_config
@@ -117,35 +146,37 @@ def test_scoped_access_key_created_by_auth_service_authenticates_platform_reques
117146
async def validate_with_local_jwks(config: AuthConfig, token: str) -> TokenClaims | None:
118147
return await validate_access_key_token(config, token, jwks_override=jwks)
119148

120-
with patch("nmp.common.auth.access_keys.validate_access_key_token", validate_with_local_jwks):
121-
response = client.get(
122-
f"{WORKSPACES_PATH}/{workspace}",
123-
headers={"Authorization": f"Bearer {access_key}"},
124-
)
149+
def get_workspace_with_access_key(token: str) -> Response:
150+
with patch("nmp.common.auth.access_keys.validate_access_key_token", validate_with_local_jwks):
151+
return client.get(
152+
f"{WORKSPACES_PATH}/{workspace}",
153+
headers={"Authorization": f"Bearer {token}"},
154+
)
155+
156+
def authenticate_with_access_key(token: str) -> Response:
157+
with patch("nmp.common.auth.access_keys.validate_access_key_token", validate_with_local_jwks):
158+
return client.get(
159+
"/apis/auth/authenticate",
160+
headers={"Authorization": f"Bearer {token}"},
161+
)
162+
163+
response = _wait_for_authorization_response(
164+
lambda: get_workspace_with_access_key(access_key),
165+
expected_status_code=200,
166+
)
125167

126168
assert response.status_code == 200, response.text
127169
assert response.json()["name"] == workspace
128170

129-
with patch("nmp.common.auth.access_keys.validate_access_key_token", validate_with_local_jwks):
130-
authenticate_response = client.get(
131-
"/apis/auth/authenticate",
132-
headers={"Authorization": f"Bearer {access_key}"},
133-
)
134-
invalid_authenticate_response = client.get(
135-
"/apis/auth/authenticate",
136-
headers={"Authorization": f"Bearer {_tamper_jwt(access_key)}"},
137-
)
171+
authenticate_response = authenticate_with_access_key(access_key)
172+
invalid_authenticate_response = authenticate_with_access_key(_tamper_jwt(access_key))
138173

139174
assert authenticate_response.status_code == 200, authenticate_response.text
140175
assert authenticate_response.json()["principal"] == user
141176
assert authenticate_response.json()["token_kind"] == "access_key"
142177
assert invalid_authenticate_response.status_code == 401, invalid_authenticate_response.text
143178

144-
with patch("nmp.common.auth.access_keys.validate_access_key_token", validate_with_local_jwks):
145-
invalid_workspace_response = client.get(
146-
f"{WORKSPACES_PATH}/{workspace}",
147-
headers={"Authorization": f"Bearer {_tamper_jwt(access_key)}"},
148-
)
179+
invalid_workspace_response = get_workspace_with_access_key(_tamper_jwt(access_key))
149180

150181
assert invalid_workspace_response.status_code == 401, invalid_workspace_response.text
151182

@@ -155,15 +186,14 @@ async def validate_with_local_jwks(config: AuthConfig, token: str) -> TokenClaim
155186
assert len(revoked_keys) == 1
156187
assert revoked_keys[0]["status"] == "REVOKED"
157188

158-
with patch("nmp.common.auth.access_keys.validate_access_key_token", validate_with_local_jwks):
159-
revoked_authenticate_response = client.get(
160-
"/apis/auth/authenticate",
161-
headers={"Authorization": f"Bearer {access_key}"},
162-
)
163-
revoked_workspace_response = client.get(
164-
f"{WORKSPACES_PATH}/{workspace}",
165-
headers={"Authorization": f"Bearer {access_key}"},
166-
)
189+
revoked_authenticate_response = _wait_for_authorization_response(
190+
lambda: authenticate_with_access_key(access_key),
191+
expected_status_code=401,
192+
)
193+
revoked_workspace_response = _wait_for_authorization_response(
194+
lambda: get_workspace_with_access_key(access_key),
195+
expected_status_code=401,
196+
)
167197

168198
assert revoked_authenticate_response.status_code == 401, revoked_authenticate_response.text
169199
assert revoked_workspace_response.status_code == 401, revoked_workspace_response.text

0 commit comments

Comments
 (0)