@@ -0,0 +1,281 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
create table "outbox" (
|
||||
"id" uuid primary key,
|
||||
"org_id" uuid not null,
|
||||
"name" text not null,
|
||||
"payload" jsonb not null,
|
||||
"created_at" timestamptz not null,
|
||||
"attempts" integer not null,
|
||||
"published_at" timestamptz,
|
||||
"last_error" text,
|
||||
"deferrals" integer not null default 0,
|
||||
"deferred_since" timestamptz,
|
||||
"deferred_until" timestamptz
|
||||
);
|
||||
|
||||
create index "outbox_pending_idx" on "outbox" ("created_at") where "published_at" is null;
|
||||
|
||||
alter table "outbox" enable row level security;
|
||||
|
||||
alter table "outbox" force row level security;
|
||||
|
||||
create policy "outbox_tenant" on "outbox" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
create table "idempotency_keys" (
|
||||
"org_id" uuid not null,
|
||||
-- Not null, with the zero uuid where a scope has no workspace or project: a primary key
|
||||
-- ignores a row whose column is NULL, and the key would stop being unique.
|
||||
"workspace_id" uuid not null,
|
||||
"project_id" uuid not null,
|
||||
"procedure" text not null,
|
||||
"key" text not null,
|
||||
-- The response may itself be the JSON value null, so "finished" is a column of its own
|
||||
-- rather than a non-null response. Null while the transaction that claimed this key is
|
||||
-- running; a second caller's insert blocks on the key until that transaction ends.
|
||||
"response" jsonb,
|
||||
"completed_at" timestamptz,
|
||||
"created_at" timestamptz not null,
|
||||
primary key ("org_id", "workspace_id", "project_id", "procedure", "key")
|
||||
);
|
||||
|
||||
alter table "idempotency_keys" enable row level security;
|
||||
|
||||
alter table "idempotency_keys" force row level security;
|
||||
|
||||
create policy "idempotency_keys_tenant" on "idempotency_keys" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
create table "auth_api_keys" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"name" text not null,
|
||||
"token_hash" text not null,
|
||||
"prefix" text not null,
|
||||
"user_id" uuid not null,
|
||||
"expires_at" text,
|
||||
"revoked_at" text
|
||||
);
|
||||
|
||||
alter table "auth_api_keys" add constraint "auth_api_keys_org_id_token_hash_key" unique nulls not distinct ("org_id", "token_hash");
|
||||
|
||||
create index "auth_api_keys_org_id_user_id_idx" on "auth_api_keys" ("org_id", "user_id");
|
||||
|
||||
create index "auth_api_keys_org_id_prefix_idx" on "auth_api_keys" ("org_id", "prefix");
|
||||
|
||||
create table "auth_sessions" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"user_id" uuid not null,
|
||||
"issuer" text not null,
|
||||
"subject" text not null,
|
||||
"handle_hash" text not null,
|
||||
"tokens" text not null,
|
||||
"access_token_expires_at" text not null,
|
||||
"last_seen_at" text not null,
|
||||
"absolute_expires_at" text not null,
|
||||
"revoked_at" text
|
||||
);
|
||||
|
||||
alter table "auth_sessions" add constraint "auth_sessions_handle_hash_key" unique nulls not distinct ("handle_hash");
|
||||
|
||||
create index "auth_sessions_user_id_idx" on "auth_sessions" ("user_id");
|
||||
|
||||
create index "auth_sessions_absolute_expires_at_idx" on "auth_sessions" ("absolute_expires_at");
|
||||
|
||||
create table "extensions" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"subject_type" text not null,
|
||||
"subject_id" uuid not null,
|
||||
"namespace" text not null,
|
||||
"payload" jsonb not null,
|
||||
"payload_version" integer not null
|
||||
);
|
||||
|
||||
alter table "extensions" add constraint "extensions_org_id_subject_type_subject_id_namespace_key" unique nulls not distinct ("org_id", "subject_type", "subject_id", "namespace");
|
||||
|
||||
create index "extensions_org_id_namespace_idx" on "extensions" ("org_id", "namespace");
|
||||
|
||||
create table "invitations" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"email" text not null,
|
||||
"role" text not null,
|
||||
"workspace_id" uuid,
|
||||
"workspace_role" text,
|
||||
"token_hash" text not null,
|
||||
"expires_at" text not null,
|
||||
"accepted_at" text,
|
||||
"invited_by" uuid not null
|
||||
);
|
||||
|
||||
alter table "invitations" add constraint "invitations_org_id_token_hash_key" unique nulls not distinct ("org_id", "token_hash");
|
||||
|
||||
create index "invitations_org_id_email_idx" on "invitations" ("org_id", "email");
|
||||
|
||||
create index "invitations_org_id_accepted_at_idx" on "invitations" ("org_id", "accepted_at");
|
||||
|
||||
create table "kit_processed_events" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"event_id" uuid not null,
|
||||
"subscriber" text not null,
|
||||
"event" text not null
|
||||
);
|
||||
|
||||
alter table "kit_processed_events" add constraint "kit_processed_events_org_id_event_id_subscriber_key" unique nulls not distinct ("org_id", "event_id", "subscriber");
|
||||
|
||||
create index "kit_processed_events_org_id_event_id_idx" on "kit_processed_events" ("org_id", "event_id");
|
||||
|
||||
create table "org_memberships" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"user_id" uuid not null,
|
||||
"role" text not null
|
||||
);
|
||||
|
||||
alter table "org_memberships" add constraint "org_memberships_org_id_user_id_key" unique nulls not distinct ("org_id", "user_id");
|
||||
|
||||
create index "org_memberships_org_id_role_idx" on "org_memberships" ("org_id", "role");
|
||||
|
||||
create table "organizations" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"slug" text not null,
|
||||
"name" text not null,
|
||||
"status" text not null,
|
||||
"settings" jsonb not null,
|
||||
"branding" jsonb
|
||||
);
|
||||
|
||||
alter table "organizations" add constraint "organizations_slug_key" unique nulls not distinct ("slug");
|
||||
|
||||
create index "organizations_status_idx" on "organizations" ("status");
|
||||
|
||||
create table "projects" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"workspace_id" uuid not null,
|
||||
"slug" text not null,
|
||||
"name" text not null,
|
||||
"module_key" text not null,
|
||||
"archived_at" text
|
||||
);
|
||||
|
||||
alter table "projects" add constraint "projects_org_id_workspace_id_slug_key" unique nulls not distinct ("org_id", "workspace_id", "slug");
|
||||
|
||||
create index "projects_org_id_workspace_id_archived_at_idx" on "projects" ("org_id", "workspace_id", "archived_at");
|
||||
|
||||
create table "users" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"issuer" text not null,
|
||||
"subject" text not null,
|
||||
"email" text not null,
|
||||
"display_name" text not null,
|
||||
"locale" text not null
|
||||
);
|
||||
|
||||
alter table "users" add constraint "users_issuer_subject_key" unique nulls not distinct ("issuer", "subject");
|
||||
|
||||
create index "users_email_idx" on "users" ("email");
|
||||
|
||||
create table "workspace_memberships" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"workspace_id" uuid not null,
|
||||
"user_id" uuid not null,
|
||||
"role" text not null
|
||||
);
|
||||
|
||||
alter table "workspace_memberships" add constraint "workspace_memberships_org_id_workspace_id_user_id_key" unique nulls not distinct ("org_id", "workspace_id", "user_id");
|
||||
|
||||
create index "workspace_memberships_org_id_user_id_idx" on "workspace_memberships" ("org_id", "user_id");
|
||||
|
||||
create table "workspaces" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"slug" text not null,
|
||||
"name" text not null,
|
||||
"is_default" boolean not null,
|
||||
"visibility" text not null
|
||||
);
|
||||
|
||||
alter table "workspaces" add constraint "workspaces_org_id_slug_key" unique nulls not distinct ("org_id", "slug");
|
||||
|
||||
alter table "auth_api_keys" enable row level security;
|
||||
|
||||
alter table "auth_api_keys" force row level security;
|
||||
|
||||
create policy "auth_api_keys_tenant" on "auth_api_keys" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "extensions" enable row level security;
|
||||
|
||||
alter table "extensions" force row level security;
|
||||
|
||||
create policy "extensions_tenant" on "extensions" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "invitations" enable row level security;
|
||||
|
||||
alter table "invitations" force row level security;
|
||||
|
||||
create policy "invitations_tenant" on "invitations" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "kit_processed_events" enable row level security;
|
||||
|
||||
alter table "kit_processed_events" force row level security;
|
||||
|
||||
create policy "kit_processed_events_tenant" on "kit_processed_events" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "org_memberships" enable row level security;
|
||||
|
||||
alter table "org_memberships" force row level security;
|
||||
|
||||
create policy "org_memberships_tenant" on "org_memberships" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "projects" enable row level security;
|
||||
|
||||
alter table "projects" force row level security;
|
||||
|
||||
create policy "projects_tenant" on "projects" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "workspace_memberships" enable row level security;
|
||||
|
||||
alter table "workspace_memberships" force row level security;
|
||||
|
||||
create policy "workspace_memberships_tenant" on "workspace_memberships" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "workspaces" enable row level security;
|
||||
|
||||
alter table "workspaces" force row level security;
|
||||
|
||||
create policy "workspaces_tenant" on "workspaces" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
@@ -0,0 +1,34 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
create table "files_file" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"workspace_id" uuid not null,
|
||||
"project_id" uuid not null,
|
||||
"status" text not null,
|
||||
"storage_key" text not null,
|
||||
"filename" text not null,
|
||||
"content_type" text not null,
|
||||
"size" integer,
|
||||
"sha256" text,
|
||||
"uploaded_by" uuid not null,
|
||||
"stored_at" text,
|
||||
"upload_id" text,
|
||||
"declared_size" integer,
|
||||
"part_size" integer,
|
||||
"deleting_at" text
|
||||
);
|
||||
|
||||
alter table "files_file" add constraint "files_file_org_id_workspace_id_project_id_storage_key_key" unique nulls not distinct ("org_id", "workspace_id", "project_id", "storage_key");
|
||||
|
||||
create index "files_file_org_id_workspace_id_project_id_status_idx" on "files_file" ("org_id", "workspace_id", "project_id", "status");
|
||||
|
||||
create index "files_file_org_id_workspace_id_project_id_sha256_idx" on "files_file" ("org_id", "workspace_id", "project_id", "sha256");
|
||||
|
||||
alter table "files_file" enable row level security;
|
||||
|
||||
alter table "files_file" force row level security;
|
||||
|
||||
create policy "files_file_tenant" on "files_file" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
@@ -0,0 +1,34 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
create table "proposals" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"workspace_id" uuid not null,
|
||||
"project_id" uuid not null,
|
||||
"status" text not null,
|
||||
"target_key" text not null,
|
||||
"target_record_id" text,
|
||||
"written_record_id" text,
|
||||
"change" jsonb not null,
|
||||
"evidence" jsonb not null,
|
||||
"model" text not null,
|
||||
"prompt_version" text not null,
|
||||
"created_by" text not null,
|
||||
"edited_by" text,
|
||||
"edited_at" text,
|
||||
"decided_by" text,
|
||||
"decided_at" text,
|
||||
"reason" text
|
||||
);
|
||||
|
||||
create index "proposals_org_id_workspace_id_project_id_status_idx" on "proposals" ("org_id", "workspace_id", "project_id", "status");
|
||||
|
||||
create index "proposals_org_id_workspace_id_project_id_target_key_target_record_id_idx" on "proposals" ("org_id", "workspace_id", "project_id", "target_key", "target_record_id");
|
||||
|
||||
alter table "proposals" enable row level security;
|
||||
|
||||
alter table "proposals" force row level security;
|
||||
|
||||
create policy "proposals_tenant" on "proposals" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
@@ -0,0 +1,36 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
create table "app_user_identity" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"issuer" text not null,
|
||||
"subject" text not null,
|
||||
"user_id" uuid not null,
|
||||
"migration_id" text
|
||||
);
|
||||
|
||||
alter table "app_user_identity" add constraint "app_user_identity_issuer_subject_key" unique nulls not distinct ("issuer", "subject");
|
||||
|
||||
create index "app_user_identity_user_id_idx" on "app_user_identity" ("user_id");
|
||||
|
||||
insert into "app_user_identity" (
|
||||
"id",
|
||||
"version",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"issuer",
|
||||
"subject",
|
||||
"user_id",
|
||||
"migration_id"
|
||||
)
|
||||
select
|
||||
"id",
|
||||
1,
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"issuer",
|
||||
"subject",
|
||||
"id",
|
||||
'legacy-users-issuer-subject-v1'
|
||||
from "users";
|
||||
@@ -0,0 +1,51 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
create table "kit_live_change_log" (
|
||||
"org_id" uuid not null,
|
||||
"txid" xid8 not null default pg_current_xact_id(),
|
||||
"seq" bigint generated always as identity,
|
||||
"entity" text not null,
|
||||
"entity_id" text not null,
|
||||
"op" "char" not null,
|
||||
"version" bigint,
|
||||
"workspace_id" uuid,
|
||||
"project_id" uuid,
|
||||
"at" timestamptz not null default now(),
|
||||
constraint "kit_live_change_log_op_known" check ("op" in ('u', 'd')),
|
||||
primary key ("org_id", "txid", "seq")
|
||||
);
|
||||
|
||||
create index "kit_live_change_log_at_idx" on "kit_live_change_log" ("at");
|
||||
|
||||
alter table "kit_live_change_log" enable row level security;
|
||||
|
||||
alter table "kit_live_change_log" force row level security;
|
||||
|
||||
create policy "kit_live_change_log_tenant" on "kit_live_change_log" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
create function "kit_live_capture"() returns trigger language plpgsql as $$
|
||||
begin
|
||||
if TG_OP = 'DELETE' then
|
||||
insert into "kit_live_change_log" ("org_id", "entity", "entity_id", "op", "version", "workspace_id", "project_id")
|
||||
select (r->>'org_id')::uuid, TG_ARGV[0], r->>'id', 'd', null,
|
||||
nullif(r->>'workspace_id', '')::uuid, nullif(r->>'project_id', '')::uuid
|
||||
from (select to_jsonb(o) as r from old_rows o) changed;
|
||||
perform pg_notify('kit_live', org) from (select distinct to_jsonb(o)->>'org_id' as org from old_rows o) orgs;
|
||||
else
|
||||
insert into "kit_live_change_log" ("org_id", "entity", "entity_id", "op", "version", "workspace_id", "project_id")
|
||||
select (r->>'org_id')::uuid, TG_ARGV[0], r->>'id', 'u', (r->>'version')::bigint,
|
||||
nullif(r->>'workspace_id', '')::uuid, nullif(r->>'project_id', '')::uuid
|
||||
from (select to_jsonb(n) as r from new_rows n) changed;
|
||||
perform pg_notify('kit_live', org) from (select distinct to_jsonb(n)->>'org_id' as org from new_rows n) orgs;
|
||||
end if;
|
||||
return null;
|
||||
end
|
||||
$$;
|
||||
|
||||
do $$ begin execute format('create policy "kit_live_change_log_prune" on "kit_live_change_log" for delete to %I using (true)', current_user); end $$;
|
||||
|
||||
do $$ begin execute format('create policy "kit_live_change_log_prune_read" on "kit_live_change_log" for select to %I using (true)', current_user); end $$;
|
||||
|
||||
create function "kit_live_prune"(hours integer) returns bigint language sql security definer set search_path = public as $$
|
||||
with gone as (delete from "kit_live_change_log" where "at" < now() - make_interval(hours => hours) returning 1)
|
||||
select count(*) from gone
|
||||
$$;
|
||||
@@ -0,0 +1,60 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
create table "tenancy_roles" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"key" text not null,
|
||||
"name" text not null,
|
||||
"level" text not null,
|
||||
"permissions" jsonb not null,
|
||||
"known_permissions" jsonb not null,
|
||||
"created_by" uuid,
|
||||
"revision" integer not null
|
||||
);
|
||||
|
||||
alter table "tenancy_roles" add constraint "tenancy_roles_org_id_key_key" unique nulls not distinct ("org_id", "key");
|
||||
|
||||
create table "project_memberships" (
|
||||
"id" uuid primary key,
|
||||
"version" integer not null,
|
||||
"created_at" timestamptz not null,
|
||||
"updated_at" timestamptz not null,
|
||||
"org_id" uuid not null,
|
||||
"project_id" uuid not null,
|
||||
"user_id" uuid not null,
|
||||
"role_key" text not null
|
||||
);
|
||||
|
||||
alter table "project_memberships" add constraint "project_memberships_org_id_project_id_user_id_key" unique nulls not distinct ("org_id", "project_id", "user_id");
|
||||
|
||||
create index "project_memberships_org_id_user_id_idx" on "project_memberships" ("org_id", "user_id");
|
||||
|
||||
create index "project_memberships_org_id_role_key_idx" on "project_memberships" ("org_id", "role_key");
|
||||
|
||||
alter table "org_memberships" add column "role_key" text;
|
||||
|
||||
alter table "workspace_memberships" add column "role_key" text;
|
||||
|
||||
alter table "invitations" add column "role_key" text;
|
||||
|
||||
alter table "projects" add column "visibility" text;
|
||||
|
||||
create index "org_memberships_org_id_role_key_idx" on "org_memberships" ("org_id", "role_key");
|
||||
|
||||
create index "workspace_memberships_org_id_role_key_idx" on "workspace_memberships" ("org_id", "role_key");
|
||||
|
||||
create index "invitations_org_id_role_key_idx" on "invitations" ("org_id", "role_key");
|
||||
|
||||
alter table "tenancy_roles" enable row level security;
|
||||
|
||||
alter table "tenancy_roles" force row level security;
|
||||
|
||||
create policy "tenancy_roles_tenant" on "tenancy_roles" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
|
||||
alter table "project_memberships" enable row level security;
|
||||
|
||||
alter table "project_memberships" force row level security;
|
||||
|
||||
create policy "project_memberships_tenant" on "project_memberships" using ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid) with check ("org_id" = nullif(current_setting('app.org_id', true), '')::uuid);
|
||||
@@ -0,0 +1,2 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
alter table "projects" add column "description" text;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
alter table "invitations" add column "revoked_at" text;
|
||||
@@ -0,0 +1,74 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
-- An exact lookup through a platform-only role, never SELECT on auth tables.
|
||||
-- This is a read side, not a decision, action lease or App user declaration.
|
||||
CREATE FUNCTION public.conusai_app_approval_session_v1(p_handle_hash text,p_app_tenant uuid)
|
||||
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER
|
||||
SET search_path=pg_catalog SET row_security=on AS $$
|
||||
DECLARE observed jsonb; matches integer;
|
||||
BEGIN
|
||||
IF session_user::text<>current_database()||'_approval' OR p_handle_hash !~ '^[0-9a-f]{64}$' OR p_app_tenant IS NULL
|
||||
OR p_app_tenant='00000000-0000-0000-0000-000000000000'::uuid THEN
|
||||
RAISE EXCEPTION 'app approval session refused' USING ERRCODE='42501';
|
||||
END IF;
|
||||
IF NOT EXISTS(SELECT FROM pg_roles WHERE rolname=session_user
|
||||
AND NOT rolsuper AND NOT rolbypassrls AND NOT rolcreatedb AND NOT rolcreaterole AND NOT rolreplication AND rolcanlogin)
|
||||
OR EXISTS(SELECT FROM pg_auth_members WHERE member=session_user::regrole)
|
||||
OR EXISTS(SELECT FROM pg_class WHERE relnamespace='public'::regnamespace AND relowner=session_user::regrole)
|
||||
OR EXISTS(SELECT FROM pg_database WHERE datname=current_database() AND datdba=session_user::regrole)
|
||||
THEN RAISE EXCEPTION 'app approval session refused' USING ERRCODE='42501'; END IF;
|
||||
PERFORM pg_catalog.set_config('app.org_id',p_app_tenant::text,true);
|
||||
SELECT count(*),min(x.record::text)::jsonb INTO matches,observed FROM (
|
||||
SELECT jsonb_build_object(
|
||||
'schema_version',1,'session_id',s.id,'session_version',s.version,
|
||||
'member_id',u.id,'identity_id',a.id,'identity_version',a.version,
|
||||
'issuer',s.issuer,'provider_subject',s.subject,'sealed_tokens',s.tokens,
|
||||
'created_at',s.created_at,'last_seen_at',s.last_seen_at,
|
||||
'absolute_expires_at',s.absolute_expires_at,
|
||||
'access_token_expires_at',s.access_token_expires_at,
|
||||
'app_tenant',o.id,'membership_id',m.id,'membership_version',m.version,
|
||||
'membership_role',m.role,'membership_role_key',m.role_key,
|
||||
'organization_version',o.version
|
||||
) AS record FROM public.auth_sessions s
|
||||
JOIN public.app_user_identity a ON a.issuer=s.issuer AND a.subject=s.subject AND a.user_id=s.user_id
|
||||
JOIN public.users u ON u.id=a.user_id
|
||||
JOIN public.org_memberships m ON m.user_id=u.id AND m.org_id=p_app_tenant
|
||||
JOIN public.organizations o ON o.id=m.org_id
|
||||
WHERE s.handle_hash=p_handle_hash AND s.revoked_at IS NULL
|
||||
AND o.status='active'
|
||||
LIMIT 2
|
||||
) x;
|
||||
IF matches<>1 THEN RETURN NULL; END IF;
|
||||
RETURN observed;
|
||||
END
|
||||
$$;
|
||||
REVOKE ALL ON FUNCTION public.conusai_app_approval_session_v1(text,uuid) FROM PUBLIC;
|
||||
|
||||
-- DS21 gives the ordinary runtime EXECUTE by owner default privileges. This
|
||||
-- narrow private function must explicitly remove those inherited ACL entries.
|
||||
DO $$
|
||||
DECLARE role_name text;
|
||||
BEGIN
|
||||
FOREACH role_name IN ARRAY ARRAY[current_database()||'_app',current_database()||'_reader'] LOOP
|
||||
IF octet_length(role_name)<=63 AND EXISTS(SELECT FROM pg_roles WHERE rolname=role_name) THEN
|
||||
EXECUTE format('REVOKE ALL ON FUNCTION public.conusai_app_approval_session_v1(text,uuid) FROM %I',role_name);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- The managed provider creates this platform-only role. Local/self-managed
|
||||
-- installations without it gain no authority and receive no table grant.
|
||||
DO $$
|
||||
DECLARE approval_role text:=current_database()||'_approval'; safe boolean;
|
||||
BEGIN
|
||||
IF octet_length(approval_role)<=63 AND EXISTS(SELECT FROM pg_roles WHERE rolname=approval_role) THEN
|
||||
SELECT NOT rolsuper AND NOT rolbypassrls AND NOT rolcreatedb AND NOT rolcreaterole
|
||||
AND NOT rolreplication AND rolcanlogin INTO safe FROM pg_roles WHERE rolname=approval_role;
|
||||
IF NOT safe OR EXISTS(SELECT FROM pg_auth_members WHERE member=(SELECT oid FROM pg_roles WHERE rolname=approval_role)) THEN
|
||||
RAISE EXCEPTION 'app approval role refused' USING ERRCODE='42501';
|
||||
END IF;
|
||||
EXECUTE format('GRANT USAGE ON SCHEMA public TO %I',approval_role);
|
||||
EXECUTE format('GRANT EXECUTE ON FUNCTION public.conusai_app_approval_session_v1(text,uuid) TO %I',approval_role);
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
Reference in New Issue
Block a user