96 lines
4.9 KiB
Python
96 lines
4.9 KiB
Python
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_campaign.backend.capabilities import CampaignAccessService
|
|
from govoplan_campaign.backend.db.models import Campaign, CampaignShare
|
|
from govoplan_core.core.access import PrincipalRef
|
|
from govoplan_core.db.base import Base
|
|
|
|
|
|
TENANT_ID = "tenant-1"
|
|
USER_ID = "user-1"
|
|
OTHER_USER_ID = "user-2"
|
|
GROUP_ID = "group-1"
|
|
|
|
|
|
class CampaignAccessProviderTests(unittest.TestCase):
|
|
def test_campaign_access_provider_explains_owner_share_admin_and_missing_resources(self) -> None:
|
|
session = _session()
|
|
_seed_access_subjects(session)
|
|
owned = Campaign(id="campaign-owned", tenant_id=TENANT_ID, owner_user_id=USER_ID, external_id="owned", name="Owned campaign")
|
|
shared = Campaign(id="campaign-shared", tenant_id=TENANT_ID, owner_user_id=OTHER_USER_ID, external_id="shared", name="Shared campaign")
|
|
session.add_all([
|
|
owned,
|
|
shared,
|
|
CampaignShare(id="share-group", tenant_id=TENANT_ID, campaign_id=shared.id, target_type="group", target_id=GROUP_ID, permission="read"),
|
|
])
|
|
session.commit()
|
|
|
|
service = CampaignAccessService()
|
|
owned_items = service.explain_resource_provenance(session, _principal(), resource_type="campaign", resource_id=owned.id, action="campaigns:campaign:read")
|
|
shared_items = service.explain_resource_provenance(session, _principal(group_ids={GROUP_ID}), resource_type="campaign", resource_id=shared.id, action="campaigns:campaign:read")
|
|
admin_items = service.explain_resource_provenance(session, _principal(scopes={"tenant:*"}), resource_type="campaign", resource_id=shared.id, action="campaigns:campaign:read")
|
|
missing_items = service.explain_resource_provenance(session, _principal(), resource_type="campaign", resource_id="missing-campaign", action="campaigns:campaign:read")
|
|
|
|
self.assertTrue(any(item.kind == "resource" and item.source == "campaigns.campaign" 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 == "tenant:*" for item in admin_items))
|
|
self.assertEqual("campaigns.not_found", missing_items[0].source)
|
|
self.assertIs(missing_items[0].details["found"], False)
|
|
|
|
def test_campaign_access_provider_requires_write_share_for_write_actions(self) -> None:
|
|
session = _session()
|
|
_seed_access_subjects(session)
|
|
campaign = Campaign(id="campaign-write", tenant_id=TENANT_ID, owner_user_id=OTHER_USER_ID, external_id="write", name="Write campaign")
|
|
session.add_all([
|
|
campaign,
|
|
CampaignShare(id="share-read", tenant_id=TENANT_ID, campaign_id=campaign.id, target_type="group", target_id=GROUP_ID, permission="read"),
|
|
CampaignShare(id="share-write", tenant_id=TENANT_ID, campaign_id=campaign.id, target_type="user", target_id=USER_ID, permission="write"),
|
|
])
|
|
session.commit()
|
|
|
|
service = CampaignAccessService()
|
|
items = service.explain_resource_provenance(
|
|
session,
|
|
_principal(group_ids={GROUP_ID}),
|
|
resource_type="campaign",
|
|
resource_id=campaign.id,
|
|
action="campaigns:campaign:update",
|
|
)
|
|
|
|
self.assertTrue(any(item.kind == "share" and item.id == "share-write" for item in items))
|
|
self.assertFalse(any(item.kind == "share" and item.id == "share-read" for item in items))
|
|
|
|
|
|
def _session():
|
|
engine = create_engine("sqlite:///:memory:", future=True)
|
|
Base.metadata.create_all(bind=engine, tables=[Account.__table__, User.__table__, Group.__table__, Campaign.__table__, CampaignShare.__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 {"campaigns:campaign:read"}),
|
|
group_ids=frozenset(group_ids or set()),
|
|
)
|