test_trusted_delivery_remediation.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. from __future__ import annotations
  2. from copy import deepcopy
  3. from datetime import UTC, datetime, timedelta
  4. import pytest
  5. OWNER = "01900000-0000-7000-8000-000000096001"
  6. APPROVER = "01900000-0000-7000-8000-000000096002"
  7. USER = "01900000-0000-7000-8000-000000096003"
  8. DOMAIN = "01900000-0000-7000-8000-000000096101"
  9. ASSET = "01900000-0000-7000-8000-000000096201"
  10. class MemoryRepository:
  11. def __init__(self):
  12. self.users = {OWNER, APPROVER, USER}
  13. self.policies = {}
  14. self.holds = {}
  15. self.events = []
  16. self.grants = {}
  17. self.receipts = {}
  18. def users_available(self, values):
  19. return set(values) & self.users
  20. def create_policy_version(self, value):
  21. self.policies[value["uid"]] = deepcopy(value)
  22. return deepcopy(value)
  23. def active_policies(self):
  24. return [deepcopy(value) for value in self.policies.values() if value["status"] == "active"]
  25. def add_evidence(self, value):
  26. self.events.append(deepcopy(value))
  27. def active_hold_for_asset(self, asset_uid):
  28. return None
  29. def create_legal_hold(self, value):
  30. self.holds[value["uid"]] = deepcopy(value)
  31. return deepcopy(value)
  32. def enqueue_grant(self, value):
  33. existing = next((item for item in self.grants.values() if item["idempotency_key"] == value["idempotency_key"]), None)
  34. if existing:
  35. if existing["request_digest"] != value["request_digest"]:
  36. raise RuntimeError("trusted delivery idempotency conflict")
  37. return {"uid": existing["uid"]}
  38. self.grants[value["uid"]] = deepcopy(value)
  39. return {"uid": value["uid"]}
  40. def get_grant(self, uid):
  41. value = self.grants.get(uid)
  42. return deepcopy(value) if value else None
  43. def get_provision_receipt(self, grant_uid, key):
  44. return deepcopy(self.receipts.get((grant_uid, key)))
  45. def record_provision_receipt(self, grant_uid, key, _request_digest, receipt):
  46. self.receipts[(grant_uid, key)] = deepcopy(receipt)
  47. return deepcopy(receipt)
  48. class Provider:
  49. def __init__(self):
  50. self.envelopes = []
  51. def apply(self, envelope):
  52. self.envelopes.append(deepcopy(envelope))
  53. return {"status": "applied", "receipt_code": "TEST", "response_digest": "a" * 64}
  54. def policy(now):
  55. return {
  56. "code": "REMEDIATION_READ",
  57. "version": "1.0.0",
  58. "selector": {
  59. "subjects": [USER], "roles": ["operator"], "business_domains": [DOMAIN],
  60. "assets": [ASSET], "purposes": ["quality_review"], "environments": ["test"],
  61. "actions": ["read"], "classifications": ["sensitive"],
  62. "expires_at": (now + timedelta(days=1)).isoformat(),
  63. },
  64. "resource_rules": {
  65. "row_filter": {"op": "eq", "field": "region", "value": "CN-31"},
  66. "field_rules": [{"field": "code", "effect": "allow", "mask_ref": "display-code-v1"}],
  67. },
  68. }
  69. def service():
  70. from app.core.system.trusted_delivery import (
  71. ClosedProviderRegistry,
  72. TrustedDeliveryService,
  73. )
  74. now = datetime(2026, 8, 11, 8, 0, tzinfo=UTC)
  75. ids = iter(f"01900000-0000-7000-8000-{value:012d}" for value in range(9600, 9800))
  76. provider = Provider()
  77. return (
  78. TrustedDeliveryService(MemoryRepository(), provider_registry=ClosedProviderRegistry.for_tests({"database": provider}), uid_factory=lambda: next(ids), now_factory=lambda: now),
  79. provider,
  80. now,
  81. )
  82. def test_first_policy_stays_draft_until_approved_activation():
  83. trusted, _provider, now = service()
  84. created = trusted.create_policy_version(policy(now), actor_uid=OWNER)
  85. assert created["status"] == "draft"
  86. assert trusted.evaluate_gateway({
  87. "subject_uid": USER, "roles": ["operator"], "business_domain_uid": DOMAIN, "asset_uid": ASSET,
  88. "purpose": "quality_review", "environment": "test", "action": "read", "classification": "sensitive", "requested_fields": ["code"],
  89. }) == {"decision": "deny", "reason_code": "default_deny"}
  90. def test_provision_recomputes_policy_projection_and_rejects_caller_scope_expansion():
  91. trusted, provider, now = service()
  92. created = trusted.create_policy_version(policy(now), actor_uid=OWNER)
  93. trusted.activate_policy_version({"code": created["code"], "version": created["version"], "approval_ref": "approval:activate", "approval_digest": "b" * 64, "idempotency_key": "activate-1"}, actor_uid=APPROVER)
  94. base = {
  95. "policy_uid": created["uid"], "approval_ref": "approval:provision", "approval_digest": "c" * 64,
  96. "subject_uid": USER, "asset_uid": ASSET, "business_domain_uid": DOMAIN, "purpose": "quality_review", "environment": "test", "action": "read", "classification": "sensitive", "requested_fields": ["code"],
  97. "expires_at": (now + timedelta(minutes=5)).isoformat(), "target_capability": {"actions": ["read"], "fields": ["code"]}, "provider": "database", "idempotency_key": "provision-1",
  98. }
  99. with pytest.raises(PermissionError):
  100. trusted.provision({**base, "target_capability": {"actions": ["export"], "fields": ["code"]}}, actor_uid=OWNER)
  101. result = trusted.provision(base, actor_uid=OWNER)
  102. assert result["status"] == "applied"
  103. envelope = provider.envelopes[-1]
  104. assert envelope["projection"] == {"fields": ["code"], "row_predicate": {"op": "eq", "field": "region", "value": "CN-31"}, "masking": {"code": "display-code-v1"}}
  105. assert "approval_ref" not in envelope
  106. assert "target_capability" not in envelope
  107. def test_hold_persists_two_distinct_approval_columns_for_sql_repository():
  108. trusted, _provider, _now = service()
  109. hold = trusted.create_legal_hold({
  110. "asset_uid": ASSET, "operation": "freeze", "approver_refs": ["approval:one", "approval:two"], "evidence_digest": "d" * 64,
  111. }, actor_uid=OWNER)
  112. assert hold["approver_ref_one"] == "approval:one"
  113. assert hold["approver_ref_two"] == "approval:two"