feat: strengthen module contracts and shared WebUI runtime
This commit is contained in:
@@ -25,6 +25,7 @@ from govoplan_core.core.access import (
|
||||
CAPABILITY_AUDIT_RECORDER,
|
||||
CAPABILITY_AUDIT_RETENTION,
|
||||
CAPABILITY_AUDIT_SINK,
|
||||
CAPABILITY_AUTH_API_PRINCIPAL_PROVIDER,
|
||||
CAPABILITY_TENANCY_TENANT_RESOLVER,
|
||||
AccessDecision,
|
||||
AccessAdministration,
|
||||
@@ -1082,16 +1083,62 @@ class AccessContractTests(unittest.TestCase):
|
||||
def get_organization_function_assignment(self, requested_assignment_id: str):
|
||||
return assignment if requested_assignment_id == assignment_id else None
|
||||
|
||||
def organization_function_assignments_for_identity(self, requested_identity_id: str, *, tenant_id: str | None = None):
|
||||
def organization_function_assignments_for_identity(
|
||||
self,
|
||||
requested_identity_id: str,
|
||||
*,
|
||||
tenant_id: str | None = None,
|
||||
effective_at=None,
|
||||
):
|
||||
del effective_at
|
||||
if requested_identity_id == identity_id and tenant_id in (None, assignment.tenant_id):
|
||||
return (assignment,)
|
||||
return ()
|
||||
|
||||
def organization_function_assignments_for_account(self, requested_account_id: str, *, tenant_id: str | None = None):
|
||||
def organization_function_assignments_for_account(
|
||||
self,
|
||||
requested_account_id: str,
|
||||
*,
|
||||
tenant_id: str | None = None,
|
||||
effective_at=None,
|
||||
):
|
||||
del effective_at
|
||||
if requested_account_id == account_id and tenant_id in (None, assignment.tenant_id):
|
||||
return (assignment,)
|
||||
return ()
|
||||
|
||||
def organization_function_assignments_for_identities(
|
||||
self,
|
||||
identity_ids,
|
||||
*,
|
||||
tenant_id: str | None = None,
|
||||
effective_at=None,
|
||||
):
|
||||
return {
|
||||
item_id: self.organization_function_assignments_for_identity(
|
||||
item_id,
|
||||
tenant_id=tenant_id,
|
||||
effective_at=effective_at,
|
||||
)
|
||||
for item_id in identity_ids
|
||||
}
|
||||
|
||||
def organization_function_assignments_for_accounts(
|
||||
self,
|
||||
account_ids,
|
||||
*,
|
||||
tenant_id: str | None = None,
|
||||
effective_at=None,
|
||||
):
|
||||
return {
|
||||
item_id: self.organization_function_assignments_for_account(
|
||||
item_id,
|
||||
tenant_id=tenant_id,
|
||||
effective_at=effective_at,
|
||||
)
|
||||
for item_id in account_ids
|
||||
}
|
||||
|
||||
class FakeOrganizationDirectory(CoreOrganizationDirectory):
|
||||
def get_organization_unit(self, requested_organization_unit_id: str):
|
||||
if requested_organization_unit_id != organization_unit_id:
|
||||
@@ -1317,7 +1364,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
clear_runtime()
|
||||
shutil.rmtree(root, ignore_errors=True)
|
||||
|
||||
def test_api_principal_dependency_uses_access_resolver_capability(self) -> None:
|
||||
def test_api_principal_dependency_uses_auth_provider_capability(self) -> None:
|
||||
root = Path(tempfile.mkdtemp(prefix="govoplan-access-contract-"))
|
||||
try:
|
||||
account_id = "account-capability"
|
||||
@@ -1346,6 +1393,31 @@ class AccessContractTests(unittest.TestCase):
|
||||
def explain_scope(self, principal: PrincipalRef, required_scope: str) -> AccessDecision:
|
||||
return AccessDecision(allowed=self.has_scope(principal, required_scope), requirements=(required_scope,))
|
||||
|
||||
class CapabilityApiPrincipalProvider:
|
||||
def resolve_api_principal(
|
||||
self,
|
||||
request,
|
||||
session,
|
||||
*,
|
||||
authorization=None,
|
||||
x_api_key=None,
|
||||
):
|
||||
del authorization, x_api_key
|
||||
principal_ref = CapabilityPrincipalResolver().resolve_request(
|
||||
request,
|
||||
session=session,
|
||||
)
|
||||
account = session.get(Account, principal_ref.account_id)
|
||||
user = session.get(User, principal_ref.membership_id)
|
||||
assert account is not None
|
||||
assert user is not None
|
||||
return ApiPrincipal(
|
||||
principal=principal_ref,
|
||||
account=account,
|
||||
user=user,
|
||||
permission_evaluator=CapabilityPermissionEvaluator(),
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/capability-principal")
|
||||
@@ -1365,6 +1437,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
capability_factories={
|
||||
CAPABILITY_ACCESS_PRINCIPAL_RESOLVER: lambda context: CapabilityPrincipalResolver(),
|
||||
CAPABILITY_ACCESS_PERMISSION_EVALUATOR: lambda context: CapabilityPermissionEvaluator(),
|
||||
CAPABILITY_AUTH_API_PRINCIPAL_PROVIDER: lambda context: CapabilityApiPrincipalProvider(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1372,7 +1445,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
title="access contract test",
|
||||
version="test",
|
||||
settings=SimpleNamespace(database_url=f"sqlite:///{root / 'test.db'}"),
|
||||
enabled_modules=(),
|
||||
enabled_modules=(ACCESS_MODULE_ID,),
|
||||
manifest_factories=(access_manifest,),
|
||||
base_routers=(router,),
|
||||
post_module_routers=(),
|
||||
|
||||
Reference in New Issue
Block a user