113 lines
3.2 KiB
Python
113 lines
3.2 KiB
Python
from fastapi import Depends, FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from services.shared.core import Role
|
|
from services.shared.security import get_actor, issue_app_token, require_roles
|
|
|
|
|
|
def build_app() -> FastAPI:
|
|
app = FastAPI()
|
|
|
|
@app.get('/actor')
|
|
def actor_view(actor: dict = Depends(get_actor)) -> dict:
|
|
return actor
|
|
|
|
@app.get('/admin')
|
|
def admin_view(_: dict = Depends(require_roles(Role.ADMIN))) -> dict:
|
|
return {'ok': True}
|
|
|
|
return app
|
|
|
|
|
|
def test_valid_bearer_token_resolves_actor(monkeypatch):
|
|
monkeypatch.setenv('APP_TOKEN_SECRET', 'track1-secret')
|
|
client = TestClient(build_app())
|
|
token = issue_app_token(
|
|
subject='usr_1',
|
|
username='alice',
|
|
role='admin',
|
|
auth_source='oidc',
|
|
provider='keycloak',
|
|
)
|
|
|
|
response = client.get('/actor', headers={'Authorization': f'Bearer {token}'})
|
|
|
|
assert response.status_code == 200
|
|
body = response.json()
|
|
assert body['user'] == 'alice'
|
|
assert body['role'] == 'admin'
|
|
assert body['auth_source'] == 'oidc'
|
|
|
|
|
|
def test_invalid_bearer_token_returns_401(monkeypatch):
|
|
monkeypatch.setenv('APP_TOKEN_SECRET', 'track1-secret')
|
|
client = TestClient(build_app())
|
|
|
|
response = client.get('/actor', headers={'Authorization': 'Bearer broken.token.value'})
|
|
|
|
assert response.status_code == 401
|
|
|
|
|
|
def test_legacy_header_auth_can_be_disabled(monkeypatch):
|
|
monkeypatch.setenv('ALLOW_LEGACY_HEADER_AUTH', '0')
|
|
client = TestClient(build_app())
|
|
|
|
response = client.get('/admin', headers={'X-User': 'admin', 'X-Role': 'admin'})
|
|
|
|
assert response.status_code == 403
|
|
|
|
|
|
def test_bearer_token_can_use_tenant_header_when_legacy_auth_disabled(monkeypatch):
|
|
monkeypatch.setenv('APP_TOKEN_SECRET', 'track1-secret')
|
|
monkeypatch.setenv('ALLOW_LEGACY_HEADER_AUTH', '0')
|
|
client = TestClient(build_app())
|
|
token = issue_app_token(
|
|
subject='usr_1',
|
|
username='alice',
|
|
role='admin',
|
|
auth_source='local',
|
|
)
|
|
|
|
response = client.get(
|
|
'/actor',
|
|
headers={'Authorization': f'Bearer {token}', 'X-Tenant-ID': 'default'},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
body = response.json()
|
|
assert body['user'] == 'alice'
|
|
assert body['role'] == 'admin'
|
|
assert body['tenant_id'] == 'default'
|
|
assert body['tenant_source'] == 'header'
|
|
|
|
|
|
def test_bearer_token_tenant_takes_precedence_over_header(monkeypatch):
|
|
monkeypatch.setenv('APP_TOKEN_SECRET', 'track1-secret')
|
|
client = TestClient(build_app())
|
|
token = issue_app_token(
|
|
subject='usr_1',
|
|
username='alice',
|
|
role='admin',
|
|
auth_source='oidc',
|
|
tenant_id='token-tenant',
|
|
)
|
|
|
|
response = client.get(
|
|
'/actor',
|
|
headers={'Authorization': f'Bearer {token}', 'X-Tenant-ID': 'header-tenant'},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
body = response.json()
|
|
assert body['tenant_id'] == 'token-tenant'
|
|
assert body['tenant_source'] == 'token'
|
|
|
|
|
|
def test_legacy_header_auth_still_works_when_enabled(monkeypatch):
|
|
monkeypatch.setenv('ALLOW_LEGACY_HEADER_AUTH', '1')
|
|
client = TestClient(build_app())
|
|
|
|
response = client.get('/admin', headers={'X-User': 'admin', 'X-Role': 'admin'})
|
|
|
|
assert response.status_code == 200
|