64 lines
2.9 KiB
SQL
64 lines
2.9 KiB
SQL
-- Groups + per-user group memberships, plus the `memory_access` enum.
|
|||
|
|
--
|
||
|
|
-- This migration is the substrate for the upcoming group-scoped sharing
|
||
|
|
-- feature (project_shares). It owns:
|
||
|
|
--
|
||
|
|
-- * memory_access enum — reserved for project_shares to reference.
|
||
|
|
-- * groups table — one row per distinct group seen in any user's
|
||
|
|
-- OIDC `groups` claim, keyed by (oidc_iss, name)
|
||
|
|
-- so different IdPs can both have a group called
|
||
|
|
-- e.g. "platform" without colliding.
|
||
|
|
-- * user_groups table — current group memberships for each user. Synced
|
||
|
|
-- on every sign-in: rows are inserted/deleted to
|
||
|
|
-- mirror the freshly-issued claim, so IdP
|
||
|
|
-- membership changes propagate at next login.
|
||
|
|
--
|
||
|
|
-- We deliberately do NOT add project_shares here — that's Agent B's 0004.
|
||
|
|
-- Defining the enum in 0003 lets 0004 reference it without sequencing
|
||
|
|
-- gymnastics.
|
||
|
|
|
||
|
|
-- =============================================================================
|
||
|
|
-- Enums
|
||
|
|
-- =============================================================================
|
||
|
|
|
||
|
|
CREATE TYPE "memory_access" AS ENUM ('ro', 'rw');
|
||
|
|
|
||
|
|
-- =============================================================================
|
||
|
|
-- groups
|
||
|
|
-- =============================================================================
|
||
|
|
|
||
|
|
CREATE TABLE "groups" (
|
||
|
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
-- OIDC issuer this group's identity comes from. Pairs with `name` to
|
||
|
|
-- form the natural key — same group name in two IdPs are distinct rows.
|
||
|
|
"oidc_iss" text NOT NULL,
|
||
|
|
-- The group name as it appears in the OIDC `groups` claim.
|
||
|
|
"name" text NOT NULL,
|
||
|
|
-- Optional human-friendly label. Most IdPs only emit names so this is
|
||
|
|
-- typically NULL; reserved for future enrichment.
|
||
|
|
"display_name" text,
|
||
|
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
||
|
|
"updated_at" timestamptz NOT NULL DEFAULT now()
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE UNIQUE INDEX "groups_iss_name_uq" ON "groups" ("oidc_iss", "name");
|
||
|
|
|
||
|
|
CREATE TRIGGER groups_set_updated_at BEFORE UPDATE ON "groups"
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||
|
|
|
||
|
|
-- =============================================================================
|
||
|
|
-- user_groups
|
||
|
|
-- =============================================================================
|
||
|
|
|
||
|
|
CREATE TABLE "user_groups" (
|
||
|
|
"user_id" uuid NOT NULL REFERENCES "users"("id") ON DELETE CASCADE,
|
||
|
|
"group_id" uuid NOT NULL REFERENCES "groups"("id") ON DELETE CASCADE,
|
||
|
|
-- When this membership was last observed in a sign-in claim. The auth
|
||
|
|
-- callback rewrites this on every login (insert ... on conflict do
|
||
|
|
-- update) so it's effectively "last sign-in seen this membership".
|
||
|
|
"synced_at" timestamptz NOT NULL DEFAULT now(),
|
||
|
|
PRIMARY KEY ("user_id", "group_id")
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX "user_groups_user_idx" ON "user_groups" ("user_id");
|