feat: define governed tenant erasure contracts
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
@@ -344,6 +344,18 @@ class ModuleSystemTests(unittest.TestCase):
|
||||
self.assertTrue(scopes_grant_compatible(["access:membership:read"], "admin:users:read"))
|
||||
self.assertTrue(scopes_grant_compatible(["admin:users:read"], "access:membership:read"))
|
||||
self.assertTrue(scopes_grant_compatible(["access:tenant:read"], "system:tenants:read"))
|
||||
self.assertTrue(
|
||||
scopes_grant_compatible(
|
||||
["access:tenant:erase"],
|
||||
"system:tenants:erase",
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
scopes_grant_compatible(
|
||||
["system:tenants:write"],
|
||||
"system:tenants:erase",
|
||||
)
|
||||
)
|
||||
self.assertTrue(scopes_grant_compatible(["system:*"], "access:tenant:read"))
|
||||
self.assertTrue(
|
||||
scopes_grant_compatible(
|
||||
@@ -1015,8 +1027,10 @@ finally:
|
||||
json={"mode": "destroy", "reason": "not supported"},
|
||||
)
|
||||
self.assertEqual(409, destructive.status_code, destructive.text)
|
||||
issue_codes = {item["code"] for item in destructive.json()["detail"]["plan"]["issues"]}
|
||||
self.assertIn("tenant_data_present", issue_codes)
|
||||
self.assertIn(
|
||||
"Direct destructive deletion is disabled",
|
||||
destructive.json()["detail"]["message"],
|
||||
)
|
||||
|
||||
with database.session() as session:
|
||||
empty_tenant = Tenant(
|
||||
@@ -1029,15 +1043,43 @@ finally:
|
||||
session.commit()
|
||||
empty_tenant_id = empty_tenant.id
|
||||
|
||||
destroyed = client.request(
|
||||
"DELETE",
|
||||
f"/api/v1/admin/tenants/{empty_tenant_id}",
|
||||
erasure_policy = client.patch(
|
||||
"/api/v1/admin/tenant-erasure-policy",
|
||||
headers=headers,
|
||||
json={"mode": "destroy", "reason": "empty tenant cleanup"},
|
||||
json={
|
||||
"production_profile": False,
|
||||
"required_approvals": 1,
|
||||
"preview_ttl_seconds": 900,
|
||||
"recent_authentication_seconds": 900,
|
||||
},
|
||||
)
|
||||
self.assertEqual(200, destroyed.status_code, destroyed.text)
|
||||
self.assertEqual("destroy", destroyed.json()["plan"]["action"])
|
||||
self.assertTrue(destroyed.json()["plan"]["destructive_supported"])
|
||||
self.assertEqual(200, erasure_policy.status_code, erasure_policy.text)
|
||||
erasure_preview = client.post(
|
||||
f"/api/v1/admin/tenants/{empty_tenant_id}/erasure-operations",
|
||||
headers=headers,
|
||||
json={
|
||||
"idempotency_key": f"empty-destroy-{name}",
|
||||
"reason": "empty tenant cleanup",
|
||||
},
|
||||
)
|
||||
self.assertEqual(201, erasure_preview.status_code, erasure_preview.text)
|
||||
self.assertTrue(erasure_preview.json()["preview"]["allowed"])
|
||||
operation_id = erasure_preview.json()["id"]
|
||||
approved_erasure = client.post(
|
||||
f"/api/v1/admin/tenants/{empty_tenant_id}/erasure-operations/{operation_id}/approve",
|
||||
headers=headers,
|
||||
json={"confirmation": f"empty-destroy-{name}"},
|
||||
)
|
||||
self.assertEqual(200, approved_erasure.status_code, approved_erasure.text)
|
||||
self.assertEqual("ready", approved_erasure.json()["state"])
|
||||
executed_erasure = client.post(
|
||||
f"/api/v1/admin/tenants/{empty_tenant_id}/erasure-operations/{operation_id}/execute",
|
||||
headers=headers,
|
||||
json={"confirmation": f"empty-destroy-{name}"},
|
||||
)
|
||||
self.assertEqual(200, executed_erasure.status_code, executed_erasure.text)
|
||||
self.assertEqual("completed", executed_erasure.json()["state"])
|
||||
self.assertIsNone(executed_erasure.json()["reason"])
|
||||
|
||||
retired = client.request(
|
||||
"DELETE",
|
||||
|
||||
Reference in New Issue
Block a user