0050_project_storage_grants.sql31 lines · main
1-- 0050_project_storage_grants — M5 S3 sprint (cross-project storage sharing).
2--
3-- A GRANTER project explicitly shares one file (by file id) OR a whole path
4-- prefix with a GRANTEE project. The grantee can then mint a download URL for
5-- exactly the granted resource — nothing else. This is the ONLY sanctioned
6-- exception to strict cross-project storage isolation.
7--
8-- Strict-deny by construction: access is allowed ONLY when a matching row
9-- exists with revoked_at IS NULL. Revoke sets revoked_at (never deletes) so the
10-- row can be re-activated on a fresh grant and the unique index stays intact.
11-- Lands on the control DB (Postgres). Additive + idempotent.
12CREATE TABLE IF NOT EXISTS "project_storage_grants" (
13 "id" text PRIMARY KEY NOT NULL,
14 "granter_project_id" text NOT NULL,
15 "grantee_project_id" text NOT NULL,
16 "resource" text NOT NULL,
17 "is_prefix" boolean DEFAULT false NOT NULL,
18 "created_by" text,
19 "created_at" timestamp with time zone DEFAULT now() NOT NULL,
20 "revoked_at" timestamp with time zone
21);
22--> statement-breakpoint
23DO $$ BEGIN
24 ALTER TABLE "project_storage_grants" ADD CONSTRAINT "project_storage_grants_granter_project_id_projects_id_fk" FOREIGN KEY ("granter_project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;
25EXCEPTION
26 WHEN duplicate_object THEN null;
27END $$;
28--> statement-breakpoint
29CREATE UNIQUE INDEX IF NOT EXISTS "project_storage_grants_unique_idx" ON "project_storage_grants" ("granter_project_id","grantee_project_id","resource");
30--> statement-breakpoint
31CREATE INDEX IF NOT EXISTS "project_storage_grants_grantee_idx" ON "project_storage_grants" ("grantee_project_id");