Commit 45fcda79 authored by George Papathanail's avatar George Papathanail
Browse files

test: cover require_role/require_three_legged and the enforcement-disabled path

parent f817d9a8
Loading
Loading
Loading
Loading
Loading
+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