20260811_481_trusted_delivery.py 6.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. """Persist P3-WP06 trusted-delivery grants without external credentials or raw data."""
  2. from alembic import op
  3. revision = "20260811_481"
  4. down_revision = "20260811_480"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. op.execute(
  9. """
  10. CREATE TABLE public.trusted_delivery_policy_versions (
  11. uid UUID PRIMARY KEY, code VARCHAR(120) NOT NULL, version VARCHAR(30) NOT NULL,
  12. status VARCHAR(20) NOT NULL CHECK(status IN ('draft','active','retired')),
  13. selector JSONB NOT NULL, resource_rules JSONB NOT NULL,
  14. created_by UUID NOT NULL REFERENCES public.users(id), current_version INTEGER NOT NULL DEFAULT 1 CHECK(current_version>0),
  15. created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
  16. UNIQUE(code,version), CHECK(jsonb_typeof(selector)='object'), CHECK(jsonb_typeof(resource_rules)='object')
  17. );
  18. CREATE UNIQUE INDEX uq_trusted_delivery_active_policy ON public.trusted_delivery_policy_versions(code) WHERE status='active';
  19. CREATE TABLE public.trusted_delivery_grants (
  20. uid UUID PRIMARY KEY, policy_uid UUID NOT NULL REFERENCES public.trusted_delivery_policy_versions(uid),
  21. subject_uid UUID NOT NULL REFERENCES public.users(id), asset_uid UUID NOT NULL,
  22. provider VARCHAR(20) NOT NULL CHECK(provider IN ('database','api','file','iam','kms','dlp','siem')),
  23. idempotency_key VARCHAR(160) NOT NULL UNIQUE, request_digest CHAR(64) NOT NULL CHECK(request_digest ~ '^[0-9a-f]{64}$'),
  24. expires_at TIMESTAMPTZ NOT NULL, status VARCHAR(20) NOT NULL CHECK(status IN ('active','revoked','failed','held')),
  25. reason_code VARCHAR(80) NOT NULL, current_version INTEGER NOT NULL DEFAULT 1 CHECK(current_version>0),
  26. created_by UUID NOT NULL REFERENCES public.users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), updated_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp()
  27. );
  28. CREATE INDEX ix_trusted_delivery_grants_expiry ON public.trusted_delivery_grants(status,expires_at);
  29. CREATE TABLE public.trusted_delivery_deliveries (
  30. uid UUID PRIMARY KEY, grant_uid UUID NOT NULL REFERENCES public.trusted_delivery_grants(uid),
  31. idempotency_key VARCHAR(160) NOT NULL UNIQUE, request_digest CHAR(64) NOT NULL CHECK(request_digest ~ '^[0-9a-f]{64}$'),
  32. status VARCHAR(20) NOT NULL CHECK(status IN ('pending','processing','applied','dead_letter')),
  33. attempt_count INTEGER NOT NULL DEFAULT 0 CHECK(attempt_count BETWEEN 0 AND 3), next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
  34. lease_owner VARCHAR(80), lease_fence BIGINT NOT NULL DEFAULT 0 CHECK(lease_fence>=0), lease_expires_at TIMESTAMPTZ,
  35. created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), updated_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
  36. CHECK((status='processing')=(lease_owner IS NOT NULL AND lease_expires_at IS NOT NULL))
  37. );
  38. CREATE INDEX ix_trusted_delivery_delivery_claim ON public.trusted_delivery_deliveries(status,next_attempt_at);
  39. CREATE TABLE public.trusted_delivery_receipts (
  40. uid UUID PRIMARY KEY, delivery_uid UUID NOT NULL UNIQUE REFERENCES public.trusted_delivery_deliveries(uid),
  41. grant_uid UUID NOT NULL REFERENCES public.trusted_delivery_grants(uid), status VARCHAR(20) NOT NULL CHECK(status IN ('applied','failed')),
  42. receipt_code VARCHAR(120) NOT NULL, request_digest CHAR(64) NOT NULL CHECK(request_digest ~ '^[0-9a-f]{64}$'),
  43. response_digest CHAR(64) NOT NULL CHECK(response_digest ~ '^[0-9a-f]{64}$'), diff_digest CHAR(64) NOT NULL CHECK(diff_digest ~ '^[0-9a-f]{64}$'),
  44. created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp()
  45. );
  46. CREATE OR REPLACE FUNCTION public.protect_trusted_delivery_receipt() RETURNS trigger LANGUAGE plpgsql AS $$
  47. BEGIN RAISE EXCEPTION 'trusted delivery receipt is append-only'; END $$;
  48. CREATE TRIGGER trg_trusted_delivery_receipt_append_only BEFORE UPDATE OR DELETE OR TRUNCATE ON public.trusted_delivery_receipts FOR EACH STATEMENT EXECUTE FUNCTION public.protect_trusted_delivery_receipt();
  49. CREATE TABLE public.trusted_delivery_legal_holds (
  50. uid UUID PRIMARY KEY, asset_uid UUID NOT NULL, operation VARCHAR(30) NOT NULL CHECK(operation IN ('freeze','export_review','destruction_review')),
  51. status VARCHAR(20) NOT NULL CHECK(status IN ('pending','active','released')),
  52. approver_ref_one VARCHAR(200) NOT NULL, approver_ref_two VARCHAR(200) NOT NULL, evidence_digest CHAR(64) NOT NULL CHECK(evidence_digest ~ '^[0-9a-f]{64}$'),
  53. created_by UUID NOT NULL REFERENCES public.users(id), current_version INTEGER NOT NULL DEFAULT 1 CHECK(current_version>0), created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
  54. CHECK(approver_ref_one<>approver_ref_two)
  55. );
  56. CREATE UNIQUE INDEX uq_trusted_delivery_active_hold ON public.trusted_delivery_legal_holds(asset_uid) WHERE status='active';
  57. CREATE TABLE public.trusted_delivery_evidence (
  58. uid UUID PRIMARY KEY, action VARCHAR(80) NOT NULL, actor_uid UUID NOT NULL REFERENCES public.users(id),
  59. safe_detail JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), CHECK(jsonb_typeof(safe_detail)='object')
  60. );
  61. CREATE OR REPLACE FUNCTION public.protect_trusted_delivery_evidence() RETURNS trigger LANGUAGE plpgsql AS $$
  62. BEGIN RAISE EXCEPTION 'trusted delivery evidence is append-only'; END $$;
  63. CREATE TRIGGER trg_trusted_delivery_evidence_append_only BEFORE UPDATE OR DELETE OR TRUNCATE ON public.trusted_delivery_evidence FOR EACH STATEMENT EXECUTE FUNCTION public.protect_trusted_delivery_evidence();
  64. """
  65. )
  66. def downgrade() -> None:
  67. op.execute(
  68. """DO $$ BEGIN
  69. IF EXISTS(SELECT 1 FROM public.trusted_delivery_policy_versions) OR EXISTS(SELECT 1 FROM public.trusted_delivery_grants)
  70. OR EXISTS(SELECT 1 FROM public.trusted_delivery_deliveries) OR EXISTS(SELECT 1 FROM public.trusted_delivery_receipts)
  71. OR EXISTS(SELECT 1 FROM public.trusted_delivery_legal_holds) OR EXISTS(SELECT 1 FROM public.trusted_delivery_evidence) THEN
  72. RAISE EXCEPTION 'downgrade requires approved archival migration';
  73. END IF;
  74. END $$;
  75. DROP TABLE public.trusted_delivery_evidence;
  76. DROP FUNCTION public.protect_trusted_delivery_evidence();
  77. DROP TABLE public.trusted_delivery_legal_holds;
  78. DROP TABLE public.trusted_delivery_receipts;
  79. DROP FUNCTION public.protect_trusted_delivery_receipt();
  80. DROP TABLE public.trusted_delivery_deliveries;
  81. DROP TABLE public.trusted_delivery_grants;
  82. DROP TABLE public.trusted_delivery_policy_versions;"""
  83. )