20260818_559_plugin_three_identity_gate.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. """Require three distinct identities for every WP13 approval."""
  2. from alembic import op
  3. revision = "20260818_559"
  4. down_revision = "20260818_558"
  5. branch_labels = None
  6. depends_on = None
  7. _FACT_TABLES = (
  8. "plugin_registry_versions", "plugin_approvals", "plugin_runtime_leases",
  9. "plugin_runs", "plugin_dead_letters", "plugin_audit_outbox",
  10. "plugin_request_claims", "plugin_runtime_breakers", "plugin_recovery_claims",
  11. )
  12. def upgrade() -> None:
  13. op.get_bind().exec_driver_sql(r'''
  14. ALTER TABLE public.plugin_registry_versions
  15. ADD COLUMN review_manifest_digest char(64) CHECK(review_manifest_digest ~ '^[0-9a-f]{64}$');
  16. DO $preflight$
  17. BEGIN
  18. IF EXISTS(SELECT 1 FROM public.plugin_registry_versions WHERE state<>'draft') THEN
  19. RAISE EXCEPTION 'plugin_review_fact_backfill_denied';
  20. END IF;
  21. END $preflight$;
  22. CREATE FUNCTION public.plugin_platform_control_v5(p jsonb) RETURNS jsonb
  23. LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,public AS $control$
  24. DECLARE r record; a record; result jsonb;
  25. BEGIN
  26. IF NOT pg_has_role(session_user,'dataops_plugin_platform_control','MEMBER') OR jsonb_typeof(p)<>'object' THEN RAISE EXCEPTION 'plugin_control_denied'; END IF;
  27. IF p->>'action'='register' THEN
  28. result:=public.plugin_platform_control_v4(p);
  29. UPDATE public.plugin_registry_versions SET review_manifest_digest=NULL
  30. WHERE plugin_uid=p->>'plugin_uid' AND version=p->>'version';
  31. RETURN result;
  32. ELSIF p->>'action'='review' THEN
  33. IF p-ARRAY['action','plugin_uid','version','actor_ref']<>'{}'::jsonb THEN RAISE EXCEPTION 'plugin_review_closed'; END IF;
  34. SELECT * INTO r FROM public.plugin_registry_versions WHERE plugin_uid=p->>'plugin_uid' AND version=p->>'version' FOR UPDATE;
  35. IF NOT FOUND OR r.state<>'draft' OR r.submitter_ref IS NULL OR r.submitter_ref=p->>'actor_ref' THEN RAISE EXCEPTION 'plugin_review_independence_denied'; END IF;
  36. result:=public.plugin_platform_control_v3(p);
  37. UPDATE public.plugin_registry_versions
  38. SET review_generation=review_generation+1,reviewed_at=clock_timestamp(),review_manifest_digest=manifest_digest
  39. WHERE plugin_uid=p->>'plugin_uid' AND version=p->>'version' AND state='reviewed';
  40. RETURN result;
  41. ELSIF p->>'action'='issue_approval' THEN
  42. IF p-ARRAY['action','plugin_uid','version','actor_ref','approval_action','tenant_ref','domain_ref','expires_in_seconds']<>'{}'::jsonb THEN RAISE EXCEPTION 'plugin_approval_closed'; END IF;
  43. SELECT * INTO r FROM public.plugin_registry_versions WHERE plugin_uid=p->>'plugin_uid' AND version=p->>'version' FOR UPDATE;
  44. IF NOT FOUND OR r.state<>'reviewed' OR r.submitter_ref IS NULL OR r.review_actor IS NULL
  45. OR r.submitter_ref=r.review_actor OR r.submitter_ref=p->>'actor_ref'
  46. OR r.review_actor=p->>'actor_ref' OR r.reviewed_at IS NULL
  47. OR r.review_manifest_digest IS NULL OR r.review_manifest_digest<>r.manifest_digest THEN RAISE EXCEPTION 'plugin_three_identity_denied'; END IF;
  48. RETURN public.plugin_platform_control_v4(p);
  49. ELSIF p->>'action'='transition' THEN
  50. IF p-ARRAY['action','plugin_uid','version','actor_ref','tenant_ref','domain_ref','approval_uid','target_state','expected_fence','incident_uid']<>'{}'::jsonb THEN RAISE EXCEPTION 'plugin_transition_closed'; END IF;
  51. SELECT * INTO r FROM public.plugin_registry_versions WHERE plugin_uid=p->>'plugin_uid' AND version=p->>'version' FOR UPDATE;
  52. SELECT * INTO a FROM public.plugin_approvals WHERE approval_uid=(p->>'approval_uid')::uuid FOR UPDATE;
  53. IF NOT FOUND OR r.submitter_ref IS NULL OR r.review_actor IS NULL
  54. OR r.submitter_ref=r.review_actor OR r.submitter_ref=p->>'actor_ref'
  55. OR r.review_actor=p->>'actor_ref' OR a.actor_ref<>p->>'actor_ref'
  56. OR a.actor_ref=r.submitter_ref OR a.reviewer_ref<>r.review_actor
  57. OR r.reviewed_at IS NULL OR r.review_manifest_digest IS NULL
  58. OR r.review_manifest_digest<>r.manifest_digest OR a.manifest_digest<>r.review_manifest_digest THEN RAISE EXCEPTION 'plugin_three_identity_denied'; END IF;
  59. RETURN public.plugin_platform_control_v4(p);
  60. END IF;
  61. RETURN public.plugin_platform_control_v4(p);
  62. END $control$;
  63. ALTER FUNCTION public.plugin_platform_control_v5(jsonb) OWNER TO dataops_plugin_platform_owner;
  64. REVOKE ALL ON FUNCTION public.plugin_platform_control_v4(jsonb) FROM PUBLIC,dataops_app,dataops_app_runtime,dataops_plugin_platform_control;
  65. GRANT EXECUTE ON FUNCTION public.plugin_platform_control_v5(jsonb) TO dataops_plugin_platform_control;
  66. ''')
  67. def downgrade() -> None:
  68. bind = op.get_bind()
  69. checks = " OR ".join(f"EXISTS(SELECT 1 FROM public.{table} LIMIT 1)" for table in _FACT_TABLES)
  70. if bind.exec_driver_sql(f"SELECT {checks}").scalar():
  71. raise RuntimeError("downgrade refused: three-identity WP13 facts are nonempty")
  72. bind.exec_driver_sql('''
  73. REVOKE ALL ON FUNCTION public.plugin_platform_control_v5(jsonb) FROM PUBLIC,dataops_app,dataops_app_runtime,dataops_plugin_platform_control;
  74. DROP FUNCTION public.plugin_platform_control_v5(jsonb);
  75. ALTER TABLE public.plugin_registry_versions DROP COLUMN review_manifest_digest;
  76. GRANT EXECUTE ON FUNCTION public.plugin_platform_control_v4(jsonb) TO dataops_plugin_platform_control;
  77. ''')