Add resource access explanation contract
Some checks failed
Dependency Audit / dependency-audit (push) Has been cancelled
Some checks failed
Dependency Audit / dependency-audit (push) Has been cancelled
This commit is contained in:
@@ -32,6 +32,7 @@ from govoplan_core.core.access import (
|
||||
AccessDecisionProvenance,
|
||||
AccessExplanationService,
|
||||
AccessGovernanceMaterializer,
|
||||
ResourceAccessExplanationProvider,
|
||||
AccessSemanticDirectory,
|
||||
AccessSubjectRef,
|
||||
AccountRef,
|
||||
@@ -75,6 +76,7 @@ from govoplan_core.core.campaigns import (
|
||||
CampaignPolicyContextProvider,
|
||||
CampaignRetentionProvider,
|
||||
)
|
||||
from govoplan_core.core.files import CAPABILITY_FILES_ACCESS, FileAccessProvider
|
||||
from govoplan_core.core.modules import ModuleContext, ModuleManifest
|
||||
from govoplan_core.core.idm import CAPABILITY_IDM_DIRECTORY, OrganizationFunctionAssignmentRef
|
||||
from govoplan_core.core.organizations import CAPABILITY_ORGANIZATION_DIRECTORY, OrganizationDirectory as CoreOrganizationDirectory, OrganizationFunctionRef as CoreOrganizationFunctionRef, OrganizationUnitRef as CoreOrganizationUnitRef
|
||||
@@ -239,6 +241,24 @@ class _FakeResourceAccessService:
|
||||
return AccessDecision(allowed=True)
|
||||
|
||||
|
||||
class _FakeResourceAccessExplanationProvider:
|
||||
def explain_resource_provenance(
|
||||
self,
|
||||
session: object,
|
||||
principal: PrincipalRef,
|
||||
*,
|
||||
resource_type: str,
|
||||
resource_id: str,
|
||||
action: str,
|
||||
):
|
||||
del session, principal, resource_type, action
|
||||
return (AccessDecisionProvenance(kind="resource", id=resource_id, label="Demo resource"),)
|
||||
|
||||
|
||||
class _FakeFileAccessProvider(_FakeResourceAccessExplanationProvider):
|
||||
pass
|
||||
|
||||
|
||||
class _FakeAccessExplanationService:
|
||||
provenance = (
|
||||
AccessDecisionProvenance(kind="identity", id="identity-1", label="Demo Person"),
|
||||
@@ -481,6 +501,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
self.assertEqual("access.administration", CAPABILITY_ACCESS_ADMINISTRATION)
|
||||
self.assertEqual("access.governanceMaterializer", CAPABILITY_ACCESS_GOVERNANCE_MATERIALIZER)
|
||||
self.assertEqual("campaigns.access", CAPABILITY_CAMPAIGNS_ACCESS)
|
||||
self.assertEqual("files.access", CAPABILITY_FILES_ACCESS)
|
||||
self.assertEqual("campaigns.deliveryTasks", CAPABILITY_CAMPAIGNS_DELIVERY_TASKS)
|
||||
self.assertEqual("campaigns.mailPolicyContext", CAPABILITY_CAMPAIGNS_MAIL_POLICY_CONTEXT)
|
||||
self.assertEqual("campaigns.policyContext", CAPABILITY_CAMPAIGNS_POLICY_CONTEXT)
|
||||
@@ -584,6 +605,8 @@ class AccessContractTests(unittest.TestCase):
|
||||
self.assertIsInstance(_FakeAccessSemanticDirectory(), AccessSemanticDirectory)
|
||||
self.assertIsInstance(_FakePermissionEvaluator(), PermissionEvaluator)
|
||||
self.assertIsInstance(_FakeResourceAccessService(), ResourceAccessService)
|
||||
self.assertIsInstance(_FakeResourceAccessExplanationProvider(), ResourceAccessExplanationProvider)
|
||||
self.assertIsInstance(_FakeFileAccessProvider(), FileAccessProvider)
|
||||
self.assertIsInstance(_FakeAccessExplanationService(), AccessExplanationService)
|
||||
self.assertIsInstance(_FakeTenantAccessProvisioner(), TenantAccessProvisioner)
|
||||
self.assertIsInstance(_FakeAccessAdministration(), AccessAdministration)
|
||||
@@ -1108,6 +1131,32 @@ class AccessContractTests(unittest.TestCase):
|
||||
idm_directory = FakeIdmDirectory()
|
||||
organization_directory = FakeOrganizationDirectory()
|
||||
|
||||
class FakeFileAccessProvider:
|
||||
def explain_resource_provenance(
|
||||
self,
|
||||
session: object,
|
||||
principal: PrincipalRef,
|
||||
*,
|
||||
resource_type: str,
|
||||
resource_id: str,
|
||||
action: str,
|
||||
):
|
||||
del session, principal, action
|
||||
if resource_type != "file":
|
||||
return ()
|
||||
return (
|
||||
AccessDecisionProvenance(
|
||||
kind="resource",
|
||||
id=resource_id,
|
||||
label="Registry.pdf",
|
||||
tenant_id=tenant_id,
|
||||
source="files.file",
|
||||
details={"resource_type": "file"},
|
||||
),
|
||||
)
|
||||
|
||||
resource_provider = FakeFileAccessProvider()
|
||||
|
||||
with temporary_database(f"sqlite:///{root / 'test.db'}") as database:
|
||||
create_scope_tables(database.engine)
|
||||
Base.metadata.create_all(bind=database.engine)
|
||||
@@ -1165,6 +1214,7 @@ class AccessContractTests(unittest.TestCase):
|
||||
service = SqlAccessExplanationService(
|
||||
idm_directory=idm_directory,
|
||||
organization_directory=organization_directory,
|
||||
resource_explanation_providers=(resource_provider,),
|
||||
)
|
||||
provenance = service.explain_scope_provenance(
|
||||
PrincipalRef(
|
||||
@@ -1178,8 +1228,30 @@ class AccessContractTests(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(any(item.kind == "function" and item.label == "Registry Clerk" for item in provenance))
|
||||
self.assertTrue(any(item.kind == "role" and item.label == "File Reader" for item in provenance))
|
||||
resource_provenance = service.explain_resource_provenance(
|
||||
PrincipalRef(
|
||||
account_id=account_id,
|
||||
membership_id=user_id,
|
||||
tenant_id=tenant_id,
|
||||
identity_id=identity_id,
|
||||
scopes=frozenset({"files:file:read"}),
|
||||
),
|
||||
resource_type="file",
|
||||
resource_id="file-access-explanation",
|
||||
action="files:file:read",
|
||||
)
|
||||
self.assertTrue(any(item.kind == "resource" and item.id == "file-access-explanation" for item in resource_provenance))
|
||||
self.assertTrue(any(item.kind == "right" and item.id == "files:file:read" for item in resource_provenance))
|
||||
|
||||
registry = PlatformRegistry()
|
||||
registry.register(
|
||||
ModuleManifest(
|
||||
id="access",
|
||||
name="Access",
|
||||
version="test",
|
||||
capability_factories={CAPABILITY_ACCESS_EXPLANATION: lambda context: service},
|
||||
)
|
||||
)
|
||||
registry.register(
|
||||
ModuleManifest(
|
||||
id="idm",
|
||||
@@ -1230,6 +1302,18 @@ class AccessContractTests(unittest.TestCase):
|
||||
self.assertIn("files:file:read", payload["user"]["effective_scopes"])
|
||||
self.assertEqual("idm_function_role", payload["role_sources"][0]["source_type"])
|
||||
self.assertEqual("Registry Clerk", payload["function_facts"][0]["function_name"])
|
||||
resource_response = client.get(
|
||||
"/admin/access/resource-explanation",
|
||||
params={
|
||||
"user_id": user_id,
|
||||
"resource_type": "file",
|
||||
"resource_id": "file-access-explanation",
|
||||
"action": "files:file:read",
|
||||
},
|
||||
)
|
||||
self.assertEqual(200, resource_response.status_code, resource_response.text)
|
||||
resource_payload = resource_response.json()
|
||||
self.assertTrue(any(item["kind"] == "resource" and item["id"] == "file-access-explanation" for item in resource_payload["provenance"]))
|
||||
finally:
|
||||
clear_runtime()
|
||||
shutil.rmtree(root, ignore_errors=True)
|
||||
|
||||
Reference in New Issue
Block a user