Loading tests/unit/test_authorization.py 0 → 100644 +104 −0 Original line number Diff line number Diff line """Unit tests for core/authorization.py: require_role/require_three_legged enforce OPERATION_REQUIREMENTS directly against a synthetic Request + CallerContext, and fail closed on any operation missing from that table. Complements test_core_auth.py, which only covers JWT validation itself, not what happens with the roles/flow it produces.""" import pytest from fastapi import Request from open_exposure_gateway.core.auth.tokens import TokenFlow from open_exposure_gateway.core.authorization import require_role, require_three_legged from open_exposure_gateway.core.config import KeycloakSettings, Settings from open_exposure_gateway.core.exceptions import ForbiddenException from open_exposure_gateway.dependencies import CallerContext _TWO_LEGGED_ONLY_ROUTE = ("POST", "/qod/v0/sessions") _THREE_LEGGED_ROUTE = ("POST", "/location-retrieval/v0.5/retrieve") def _request(method: str, path: str) -> Request: return Request( scope={ "type": "http", "method": method, "path": path, "root_path": "", "query_string": b"", "headers": [], "scheme": "http", "server": ("test", 80), } ) def _caller(roles: list[str], flow: TokenFlow) -> CallerContext: return CallerContext( x_correlator=None, tenant_id="test_tenant", app_provider_id="test_app_provider", roles=roles, flow=flow, subject_id="test_subject" if flow == TokenFlow.THREE_LEGGED else None, ) def _settings(enforcement_enabled: bool = True) -> Settings: return Settings(keycloak_settings=KeycloakSettings(enforcement_enabled=enforcement_enabled)) class TestRequireRole: def test_missing_role_is_rejected(self) -> None: request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=[], flow=TokenFlow.TWO_LEGGED) with pytest.raises(ForbiddenException): require_role(request, caller, _settings()) def test_present_role_is_allowed(self) -> None: request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) assert require_role(request, caller, _settings()) is caller def test_unregistered_route_fails_closed(self) -> None: request = _request("GET", "/not-a-real-camara-route") caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) with pytest.raises(RuntimeError): require_role(request, caller, _settings()) def test_missing_role_is_logged_not_rejected_when_enforcement_disabled(self) -> None: request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=[], flow=TokenFlow.TWO_LEGGED) result = require_role(request, caller, _settings(enforcement_enabled=False)) assert result is caller class TestRequireThreeLegged: def test_two_legged_caller_is_rejected_on_a_three_legged_route(self) -> None: request = _request(*_THREE_LEGGED_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) # Role-correct is not enough: role and consent are checked independently. with pytest.raises(ForbiddenException): require_three_legged(request, caller, _settings()) def test_three_legged_caller_is_allowed_on_a_three_legged_route(self) -> None: request = _request(*_THREE_LEGGED_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.THREE_LEGGED) assert require_three_legged(request, caller, _settings()) is caller def test_three_legged_caller_is_allowed_on_a_two_legged_only_route(self) -> None: # A three-legged token satisfies a two-legged-only requirement too -- # only the reverse (two-legged on a three-legged route) is rejected. request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.THREE_LEGGED) assert require_three_legged(request, caller, _settings()) is caller def test_unregistered_route_fails_closed(self) -> None: request = _request("GET", "/not-a-real-camara-route") caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) with pytest.raises(RuntimeError): require_three_legged(request, caller, _settings()) def test_two_legged_caller_is_logged_not_rejected_when_enforcement_disabled(self) -> None: request = _request(*_THREE_LEGGED_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) result = require_three_legged(request, caller, _settings(enforcement_enabled=False)) assert result is caller Loading
tests/unit/test_authorization.py 0 → 100644 +104 −0 Original line number Diff line number Diff line """Unit tests for core/authorization.py: require_role/require_three_legged enforce OPERATION_REQUIREMENTS directly against a synthetic Request + CallerContext, and fail closed on any operation missing from that table. Complements test_core_auth.py, which only covers JWT validation itself, not what happens with the roles/flow it produces.""" import pytest from fastapi import Request from open_exposure_gateway.core.auth.tokens import TokenFlow from open_exposure_gateway.core.authorization import require_role, require_three_legged from open_exposure_gateway.core.config import KeycloakSettings, Settings from open_exposure_gateway.core.exceptions import ForbiddenException from open_exposure_gateway.dependencies import CallerContext _TWO_LEGGED_ONLY_ROUTE = ("POST", "/qod/v0/sessions") _THREE_LEGGED_ROUTE = ("POST", "/location-retrieval/v0.5/retrieve") def _request(method: str, path: str) -> Request: return Request( scope={ "type": "http", "method": method, "path": path, "root_path": "", "query_string": b"", "headers": [], "scheme": "http", "server": ("test", 80), } ) def _caller(roles: list[str], flow: TokenFlow) -> CallerContext: return CallerContext( x_correlator=None, tenant_id="test_tenant", app_provider_id="test_app_provider", roles=roles, flow=flow, subject_id="test_subject" if flow == TokenFlow.THREE_LEGGED else None, ) def _settings(enforcement_enabled: bool = True) -> Settings: return Settings(keycloak_settings=KeycloakSettings(enforcement_enabled=enforcement_enabled)) class TestRequireRole: def test_missing_role_is_rejected(self) -> None: request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=[], flow=TokenFlow.TWO_LEGGED) with pytest.raises(ForbiddenException): require_role(request, caller, _settings()) def test_present_role_is_allowed(self) -> None: request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) assert require_role(request, caller, _settings()) is caller def test_unregistered_route_fails_closed(self) -> None: request = _request("GET", "/not-a-real-camara-route") caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) with pytest.raises(RuntimeError): require_role(request, caller, _settings()) def test_missing_role_is_logged_not_rejected_when_enforcement_disabled(self) -> None: request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=[], flow=TokenFlow.TWO_LEGGED) result = require_role(request, caller, _settings(enforcement_enabled=False)) assert result is caller class TestRequireThreeLegged: def test_two_legged_caller_is_rejected_on_a_three_legged_route(self) -> None: request = _request(*_THREE_LEGGED_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) # Role-correct is not enough: role and consent are checked independently. with pytest.raises(ForbiddenException): require_three_legged(request, caller, _settings()) def test_three_legged_caller_is_allowed_on_a_three_legged_route(self) -> None: request = _request(*_THREE_LEGGED_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.THREE_LEGGED) assert require_three_legged(request, caller, _settings()) is caller def test_three_legged_caller_is_allowed_on_a_two_legged_only_route(self) -> None: # A three-legged token satisfies a two-legged-only requirement too -- # only the reverse (two-legged on a three-legged route) is rejected. request = _request(*_TWO_LEGGED_ONLY_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.THREE_LEGGED) assert require_three_legged(request, caller, _settings()) is caller def test_unregistered_route_fails_closed(self) -> None: request = _request("GET", "/not-a-real-camara-route") caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) with pytest.raises(RuntimeError): require_three_legged(request, caller, _settings()) def test_two_legged_caller_is_logged_not_rejected_when_enforcement_disabled(self) -> None: request = _request(*_THREE_LEGGED_ROUTE) caller = _caller(roles=["app-provider"], flow=TokenFlow.TWO_LEGGED) result = require_three_legged(request, caller, _settings(enforcement_enabled=False)) assert result is caller