Add file resource access explanations
This commit is contained in:
101
tests/test_access_provider.py
Normal file
101
tests/test_access_provider.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from govoplan_access.backend.db.models import Account, Group, User
|
||||
from govoplan_core.core.access import PrincipalRef
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_files.backend.capabilities import FilesAccessService, virtual_folder_resource_id
|
||||
from govoplan_files.backend.db.models import FileAsset, FileFolder, FileShare
|
||||
|
||||
|
||||
TENANT_ID = "tenant-1"
|
||||
USER_ID = "user-1"
|
||||
OTHER_USER_ID = "user-2"
|
||||
GROUP_ID = "group-1"
|
||||
|
||||
|
||||
class FilesAccessProviderTests(unittest.TestCase):
|
||||
def test_file_access_provider_explains_owner_share_admin_and_missing_resources(self) -> None:
|
||||
session = _session()
|
||||
_seed_access_subjects(session)
|
||||
owned = FileAsset(id="file-owned", tenant_id=TENANT_ID, owner_type="user", owner_user_id=USER_ID, display_path="owned.pdf", filename="owned.pdf")
|
||||
shared = FileAsset(id="file-shared", tenant_id=TENANT_ID, owner_type="user", owner_user_id=OTHER_USER_ID, display_path="shared.pdf", filename="shared.pdf")
|
||||
session.add_all([
|
||||
owned,
|
||||
shared,
|
||||
FileShare(id="share-group", tenant_id=TENANT_ID, file_asset_id=shared.id, target_type="group", target_id=GROUP_ID, permission="read"),
|
||||
])
|
||||
session.commit()
|
||||
|
||||
service = FilesAccessService()
|
||||
owned_items = service.explain_resource_provenance(session, _principal(), resource_type="file", resource_id=owned.id, action="files:file:read")
|
||||
shared_items = service.explain_resource_provenance(session, _principal(group_ids={GROUP_ID}), resource_type="file", resource_id=shared.id, action="files:file:read")
|
||||
admin_items = service.explain_resource_provenance(session, _principal(scopes={"files:file:admin"}), resource_type="file", resource_id=shared.id, action="files:file:read")
|
||||
missing_items = service.explain_resource_provenance(session, _principal(), resource_type="file", resource_id="missing-file", action="files:file:read")
|
||||
|
||||
self.assertTrue(any(item.kind == "resource" and item.source == "files.file" and item.id == owned.id for item in owned_items))
|
||||
self.assertTrue(any(item.kind == "owner" and item.id == USER_ID for item in owned_items))
|
||||
self.assertTrue(any(item.kind == "share" and item.id == "share-group" for item in shared_items))
|
||||
self.assertTrue(any(item.kind == "policy" and item.id == "files:file:admin" for item in admin_items))
|
||||
self.assertEqual("files.not_found", missing_items[0].source)
|
||||
self.assertIs(missing_items[0].details["found"], False)
|
||||
|
||||
def test_file_access_provider_explains_persisted_and_virtual_folders(self) -> None:
|
||||
session = _session()
|
||||
_seed_access_subjects(session)
|
||||
persisted = FileFolder(id="folder-persisted", tenant_id=TENANT_ID, owner_type="group", owner_group_id=GROUP_ID, path="records")
|
||||
virtual_child = FileAsset(
|
||||
id="file-in-virtual-folder",
|
||||
tenant_id=TENANT_ID,
|
||||
owner_type="group",
|
||||
owner_group_id=GROUP_ID,
|
||||
display_path="inferred/sub/file.pdf",
|
||||
filename="file.pdf",
|
||||
)
|
||||
session.add_all([persisted, virtual_child])
|
||||
session.commit()
|
||||
|
||||
service = FilesAccessService()
|
||||
principal = _principal(group_ids={GROUP_ID})
|
||||
persisted_items = service.explain_resource_provenance(session, principal, resource_type="folder", resource_id=persisted.id, action="files:file:read")
|
||||
virtual_id = virtual_folder_resource_id(tenant_id=TENANT_ID, owner_type="group", owner_id=GROUP_ID, path="inferred/sub")
|
||||
virtual_items = service.explain_resource_provenance(session, principal, resource_type="folder", resource_id=virtual_id, action="files:file:read")
|
||||
missing_virtual_id = virtual_folder_resource_id(tenant_id=TENANT_ID, owner_type="group", owner_id=GROUP_ID, path="inferred/missing")
|
||||
missing_items = service.explain_resource_provenance(session, principal, resource_type="folder", resource_id=missing_virtual_id, action="files:file:read")
|
||||
|
||||
self.assertTrue(any(item.kind == "resource" and item.source == "files.folder" and item.id == persisted.id for item in persisted_items))
|
||||
self.assertTrue(any(item.kind == "owner" and item.id == GROUP_ID for item in persisted_items))
|
||||
self.assertTrue(any(item.kind == "resource" and item.source == "files.virtual_folder" and item.id == virtual_id for item in virtual_items))
|
||||
self.assertTrue(any(item.kind == "owner" and item.id == GROUP_ID for item in virtual_items))
|
||||
self.assertEqual("files.not_found", missing_items[0].source)
|
||||
|
||||
|
||||
def _session():
|
||||
engine = create_engine("sqlite:///:memory:", future=True)
|
||||
Base.metadata.create_all(bind=engine, tables=[Account.__table__, User.__table__, Group.__table__, FileAsset.__table__, FileFolder.__table__, FileShare.__table__])
|
||||
return sessionmaker(bind=engine, future=True)()
|
||||
|
||||
|
||||
def _seed_access_subjects(session) -> None:
|
||||
session.add_all([
|
||||
Account(id="account-1", email="one@example.test", normalized_email="one@example.test"),
|
||||
Account(id="account-2", email="two@example.test", normalized_email="two@example.test"),
|
||||
User(id=USER_ID, tenant_id=TENANT_ID, account_id="account-1", email="one@example.test"),
|
||||
User(id=OTHER_USER_ID, tenant_id=TENANT_ID, account_id="account-2", email="two@example.test"),
|
||||
Group(id=GROUP_ID, tenant_id=TENANT_ID, slug="group", name="Group"),
|
||||
])
|
||||
session.commit()
|
||||
|
||||
|
||||
def _principal(*, scopes: set[str] | None = None, group_ids: set[str] | None = None) -> PrincipalRef:
|
||||
return PrincipalRef(
|
||||
account_id="account-1",
|
||||
membership_id=USER_ID,
|
||||
tenant_id=TENANT_ID,
|
||||
scopes=frozenset(scopes or {"files:file:read"}),
|
||||
group_ids=frozenset(group_ids or set()),
|
||||
)
|
||||
Reference in New Issue
Block a user