75 lines
2.8 KiB
Python
75 lines
2.8 KiB
Python
"""Role mapping is deny-by-default: unmapped groups grant nothing."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime, timedelta
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
from ragcore.application.authorization import AuthorizationService
|
|
from ragcore.domain.auth import OidcClaims, Session, map_roles
|
|
from ragcore.domain.authorization import Permission, PermissionDenied, Principal, PrincipalType
|
|
from ragcore.domain.control import Role
|
|
|
|
|
|
def test_recognized_groups_map_to_their_configured_role() -> None:
|
|
roles = map_roles(("ragcore-admins",), {"ragcore-admins": Role.PLATFORM_ADMIN})
|
|
assert roles == frozenset({Role.PLATFORM_ADMIN})
|
|
|
|
|
|
def test_unrecognized_groups_grant_nothing() -> None:
|
|
roles = map_roles(("some-other-app-group",), {"ragcore-admins": Role.PLATFORM_ADMIN})
|
|
assert roles == frozenset()
|
|
|
|
|
|
def test_no_groups_at_all_grants_nothing() -> None:
|
|
roles = map_roles((), {"ragcore-admins": Role.PLATFORM_ADMIN})
|
|
assert roles == frozenset()
|
|
|
|
|
|
def test_mixed_recognized_and_unrecognized_groups_only_map_the_known_ones() -> None:
|
|
roles = map_roles(
|
|
("ragcore-editors", "some-unrelated-group", "ragcore-viewers"),
|
|
{"ragcore-editors": Role.EDITOR, "ragcore-viewers": Role.VIEWER},
|
|
)
|
|
assert roles == frozenset({Role.EDITOR, Role.VIEWER})
|
|
|
|
|
|
def test_oidc_claims_bounds_are_enforced() -> None:
|
|
with pytest.raises(ValueError, match="subject"):
|
|
OidcClaims(subject="", email=None, groups=())
|
|
with pytest.raises(ValueError, match="groups"):
|
|
OidcClaims(subject="alice", email=None, groups=tuple(f"g{i}" for i in range(101)))
|
|
|
|
|
|
def test_a_session_with_no_mapped_roles_is_denied_everywhere() -> None:
|
|
"""Ties role mapping's deny-by-default guarantee to the real, already-
|
|
established authorization system: an authenticated identity with an
|
|
empty roles set (because none of its groups were recognized) is denied
|
|
exactly like any other roleless principal -- there is no separate
|
|
"authenticated but unmapped" bypass anywhere in the authorization path.
|
|
"""
|
|
|
|
workspace_id = uuid4()
|
|
claims = OidcClaims(subject="alice", email="alice@example.test", groups=("unmapped-group",))
|
|
roles = map_roles(claims.groups, {"ragcore-admins": Role.PLATFORM_ADMIN})
|
|
now = datetime.now(UTC)
|
|
session = Session(
|
|
id=uuid4(),
|
|
subject=claims.subject,
|
|
workspace_id=workspace_id,
|
|
issued_at=now,
|
|
expires_at=now + timedelta(hours=1),
|
|
csrf_token="a" * 32,
|
|
roles=roles,
|
|
)
|
|
|
|
assert session.roles == frozenset()
|
|
|
|
principal = Principal(uuid4(), workspace_id, PrincipalType.HUMAN, session.roles)
|
|
with pytest.raises(PermissionDenied):
|
|
AuthorizationService().authorize(
|
|
principal, Permission.SEARCH, resource_workspace_id=workspace_id
|
|
)
|