@opengeni/db 4.2.2 → 4.3.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-6GAPAYZR.js → chunk-7JPLGBSA.js} +562 -86
- package/dist/chunk-7JPLGBSA.js.map +1 -0
- package/dist/{chunk-SNLIKELL.js → chunk-IHADTE7C.js} +2 -2
- package/dist/{chunk-7WX3R3YL.js → chunk-ILKJKDII.js} +7 -2
- package/dist/chunk-ILKJKDII.js.map +1 -0
- package/dist/{chunk-MGVOYVPS.js → chunk-NCSPNCQK.js} +3 -3
- package/dist/{chunk-B2KR3TEM.js → chunk-SGJDIF2D.js} +2 -2
- package/dist/{chunk-S4Y42VSX.js → chunk-Y5XFVJJM.js} +207 -3
- package/dist/chunk-Y5XFVJJM.js.map +1 -0
- package/dist/{chunk-FPTI4NP4.js → chunk-YGBOVXC7.js} +2 -2
- package/dist/{chunk-6Y7ZDVJ5.js → chunk-YGRT6VZO.js} +2 -2
- package/dist/{chunk-6IQ4X35Y.js → chunk-YVYBAWBU.js} +46 -1
- package/dist/chunk-YVYBAWBU.js.map +1 -0
- package/dist/connect-attempts.d.ts +60 -0
- package/dist/connection-setup-authority.d.ts +10 -0
- package/dist/connection-token-resolver.d.ts +11 -2
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/external-identities.d.ts +16 -0
- package/dist/external-identity-links.d.ts +74 -0
- package/dist/external-link-work.d.ts +51 -0
- package/dist/host-mcp-binding-match.d.ts +5 -0
- package/dist/host-mcp-bindings.d.ts +73 -0
- package/dist/host-mcp-task-authority.d.ts +47 -0
- package/dist/host-selection-identity.d.ts +6 -0
- package/dist/index.d.ts +51 -35
- package/dist/index.js +15171 -13775
- package/dist/index.js.map +1 -1
- package/dist/live-session-attempt.d.ts +275 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/memory-domain.d.ts +3 -3
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +2 -2
- package/dist/runtime-posture.d.ts +6 -6
- package/dist/scheduled-task-revision-authority.d.ts +11 -0
- package/dist/schema.d.ts +1279 -9
- package/dist/schema.js +11 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +4 -4
- package/dist/session-event-slices.js +2 -2
- package/dist/session-queue-commands.d.ts +7 -0
- package/dist/session-tenancy.d.ts +2 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +4 -4
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0437_organization_scoped_external_workspaces.sql +71 -0
- package/drizzle/0438_durable_connect_attempts.sql +95 -0
- package/drizzle/0439_external_identity_provisioning.sql +128 -0
- package/drizzle/0440_external_workspace_member_removal.sql +59 -0
- package/drizzle/0441_external_identity_membership_lifecycle.sql +163 -0
- package/drizzle/0442_external_owning_user_authority.sql +81 -0
- package/drizzle/0443_host_mcp_binding_registry.sql +93 -0
- package/drizzle/0444_host_mcp_delegations.sql +129 -0
- package/drizzle/0445_host_mcp_turn_authorities.sql +145 -0
- package/drizzle/0446_host_mcp_causal_continuation.sql +126 -0
- package/drizzle/0447_host_mcp_task_authorities.sql +264 -0
- package/drizzle/0448_host_mcp_child_authority.sql +115 -0
- package/drizzle/0449_external_identity_link_lifecycle.sql +107 -0
- package/drizzle/0450_external_identity_link_work.sql +179 -0
- package/drizzle/0451_external_link_preview_and_permission_ceiling.sql +82 -0
- package/drizzle/0452_external_link_scheduled_origin.sql +34 -0
- package/drizzle/0453_host_mcp_native_owner.sql +35 -0
- package/drizzle/0454_connect_origin_authority.sql +42 -0
- package/drizzle/0455_external_link_inventory_labels.sql +44 -0
- package/drizzle/0456_social_connection_versions.sql +14 -0
- package/drizzle/0457_canonical_session_scope_subject.sql +46 -0
- package/drizzle/0458_skill_review_wire_compatibility.sql +36 -0
- package/package.json +6 -6
- package/src/connect-attempts.ts +317 -0
- package/src/connection-authority.ts +9 -0
- package/src/connection-setup-authority.ts +35 -0
- package/src/connection-token-resolver.ts +177 -48
- package/src/external-identities.ts +70 -0
- package/src/external-identity-links.ts +364 -0
- package/src/external-link-work.ts +228 -0
- package/src/host-mcp-binding-match.ts +33 -0
- package/src/host-mcp-bindings.ts +741 -0
- package/src/host-mcp-task-authority.ts +383 -0
- package/src/host-selection-identity.ts +29 -0
- package/src/index.ts +398 -147
- package/src/live-session-attempt.ts +85 -0
- package/src/managed-human-provisioning.ts +6 -1
- package/src/memory-domain.ts +6 -11
- package/src/provision-roles.ts +3 -0
- package/src/runtime-posture.ts +47 -0
- package/src/scheduled-task-revision-authority.ts +30 -0
- package/src/schema.ts +228 -3
- package/src/session-control.ts +14 -0
- package/src/session-queue-commands.ts +11 -0
- package/src/session-tenancy.ts +7 -0
- package/src/workspace-authority.ts +5 -5
- package/dist/chunk-6GAPAYZR.js.map +0 -1
- package/dist/chunk-6IQ4X35Y.js.map +0 -1
- package/dist/chunk-7WX3R3YL.js.map +0 -1
- package/dist/chunk-S4Y42VSX.js.map +0 -1
- /package/dist/{chunk-SNLIKELL.js.map → chunk-IHADTE7C.js.map} +0 -0
- /package/dist/{chunk-MGVOYVPS.js.map → chunk-NCSPNCQK.js.map} +0 -0
- /package/dist/{chunk-B2KR3TEM.js.map → chunk-SGJDIF2D.js.map} +0 -0
- /package/dist/{chunk-FPTI4NP4.js.map → chunk-YGBOVXC7.js.map} +0 -0
- /package/dist/{chunk-6Y7ZDVJ5.js.map → chunk-YGRT6VZO.js.map} +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Link provenance is separate from ownership and from historical creator audit.
|
|
3
|
+
-- An inherited revoked link remains attached: omission must never turn old work
|
|
4
|
+
-- into an unrestricted native-user operation.
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
DO $drain$
|
|
8
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
9
|
+
BEGIN
|
|
10
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' THEN
|
|
11
|
+
RAISE EXCEPTION '0450 requires application database roles' USING ERRCODE = '55000';
|
|
12
|
+
END IF;
|
|
13
|
+
IF jsonb_array_length(roles) NOT BETWEEN 1 AND 16 OR EXISTS (
|
|
14
|
+
SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
|
|
15
|
+
OR btrim(item #>> '{}') = '' OR item #>> '{}' <> btrim(item #>> '{}')
|
|
16
|
+
OR octet_length(item #>> '{}') > 63
|
|
17
|
+
) THEN RAISE EXCEPTION '0450 invalid application roles' USING ERRCODE = '55000'; END IF;
|
|
18
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
19
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
20
|
+
RAISE EXCEPTION '0450 requires stopped application sessions' USING ERRCODE = '55000';
|
|
21
|
+
END IF;
|
|
22
|
+
END
|
|
23
|
+
$drain$;
|
|
24
|
+
|
|
25
|
+
CREATE TABLE external_link_turn_authorities (
|
|
26
|
+
turn_id uuid PRIMARY KEY REFERENCES session_turns(id) ON DELETE CASCADE,
|
|
27
|
+
account_id uuid NOT NULL,
|
|
28
|
+
workspace_id uuid NOT NULL,
|
|
29
|
+
session_id uuid NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
30
|
+
link_id uuid NOT NULL REFERENCES external_identity_links(id),
|
|
31
|
+
link_revision bigint NOT NULL CHECK (link_revision BETWEEN 1 AND 9007199254740991),
|
|
32
|
+
canonical_snapshot jsonb NOT NULL CHECK (jsonb_typeof(canonical_snapshot) = 'object' AND octet_length(canonical_snapshot::text) <= 65536),
|
|
33
|
+
source_kind text NOT NULL CHECK (source_kind IN ('direct','causal','child','scheduled')),
|
|
34
|
+
source_turn_id uuid REFERENCES session_turns(id),
|
|
35
|
+
source_task_id uuid REFERENCES scheduled_tasks(id),
|
|
36
|
+
source_task_revision bigint,
|
|
37
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
38
|
+
FOREIGN KEY (workspace_id, account_id) REFERENCES workspaces(id, account_id),
|
|
39
|
+
CHECK ((source_kind = 'direct' AND source_turn_id IS NULL AND source_task_id IS NULL AND source_task_revision IS NULL)
|
|
40
|
+
OR (source_kind IN ('causal','child') AND source_turn_id IS NOT NULL AND source_task_id IS NULL AND source_task_revision IS NULL)
|
|
41
|
+
OR (source_kind = 'scheduled' AND source_turn_id IS NULL AND source_task_id IS NOT NULL AND source_task_revision > 0))
|
|
42
|
+
);
|
|
43
|
+
CREATE TABLE external_link_task_authorities (
|
|
44
|
+
task_id uuid NOT NULL REFERENCES scheduled_tasks(id) ON DELETE CASCADE,
|
|
45
|
+
task_revision bigint NOT NULL CHECK (task_revision BETWEEN 1 AND 9007199254740991),
|
|
46
|
+
account_id uuid NOT NULL,
|
|
47
|
+
workspace_id uuid NOT NULL,
|
|
48
|
+
link_id uuid NOT NULL REFERENCES external_identity_links(id),
|
|
49
|
+
link_revision bigint NOT NULL CHECK (link_revision BETWEEN 1 AND 9007199254740991),
|
|
50
|
+
canonical_snapshot jsonb NOT NULL CHECK (jsonb_typeof(canonical_snapshot) = 'object' AND octet_length(canonical_snapshot::text) <= 65536),
|
|
51
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
52
|
+
PRIMARY KEY (task_id, task_revision),
|
|
53
|
+
FOREIGN KEY (workspace_id, account_id) REFERENCES workspaces(id, account_id)
|
|
54
|
+
);
|
|
55
|
+
ALTER TABLE external_link_turn_authorities ENABLE ROW LEVEL SECURITY;
|
|
56
|
+
ALTER TABLE external_link_turn_authorities FORCE ROW LEVEL SECURITY;
|
|
57
|
+
CREATE POLICY session_visibility_isolation ON external_link_turn_authorities AS RESTRICTIVE
|
|
58
|
+
USING (session_reference_visible(account_id, workspace_id, session_id))
|
|
59
|
+
WITH CHECK (session_reference_visible(account_id, workspace_id, session_id));
|
|
60
|
+
ALTER TABLE external_link_task_authorities ENABLE ROW LEVEL SECURITY;
|
|
61
|
+
ALTER TABLE external_link_task_authorities FORCE ROW LEVEL SECURITY;
|
|
62
|
+
CREATE POLICY external_link_turn_authorities_scope ON external_link_turn_authorities
|
|
63
|
+
USING (account_id = opengeni_private.current_account_id() AND workspace_id = opengeni_private.current_workspace_id())
|
|
64
|
+
WITH CHECK (account_id = opengeni_private.current_account_id() AND workspace_id = opengeni_private.current_workspace_id());
|
|
65
|
+
CREATE POLICY external_link_task_authorities_scope ON external_link_task_authorities
|
|
66
|
+
USING (account_id = opengeni_private.current_account_id() AND workspace_id = opengeni_private.current_workspace_id())
|
|
67
|
+
WITH CHECK (account_id = opengeni_private.current_account_id() AND workspace_id = opengeni_private.current_workspace_id());
|
|
68
|
+
|
|
69
|
+
CREATE FUNCTION opengeni_private.guard_external_link_work_snapshot() RETURNS trigger
|
|
70
|
+
LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$
|
|
71
|
+
DECLARE
|
|
72
|
+
l external_identity_links%ROWTYPE;
|
|
73
|
+
t session_turns%ROWTYPE;
|
|
74
|
+
s sessions%ROWTYPE;
|
|
75
|
+
source_snapshot jsonb;
|
|
76
|
+
revision_authority jsonb;
|
|
77
|
+
BEGIN
|
|
78
|
+
IF TG_OP <> 'INSERT' OR NEW.account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
79
|
+
OR NEW.workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id() THEN
|
|
80
|
+
RAISE EXCEPTION 'external link work scope unavailable' USING ERRCODE = '42501';
|
|
81
|
+
END IF;
|
|
82
|
+
SELECT * INTO l FROM external_identity_links WHERE id = NEW.link_id AND account_id = NEW.account_id;
|
|
83
|
+
IF NOT FOUND OR l.native_subject_id IS NULL
|
|
84
|
+
OR NEW.canonical_snapshot #>> '{actor,accountId}' IS DISTINCT FROM NEW.account_id::text
|
|
85
|
+
OR NEW.canonical_snapshot #>> '{actor,linkId}' IS DISTINCT FROM NEW.link_id::text
|
|
86
|
+
OR NEW.canonical_snapshot #>> '{actor,linkRevision}' IS DISTINCT FROM NEW.link_revision::text
|
|
87
|
+
OR NEW.canonical_snapshot #>> '{actor,actingMode}' IS DISTINCT FROM 'linked_native'
|
|
88
|
+
OR NEW.canonical_snapshot #>> '{actor,effectiveSubjectId}' IS DISTINCT FROM l.native_subject_id
|
|
89
|
+
OR NEW.canonical_snapshot #>> '{actor,externalIdentityId}' IS DISTINCT FROM l.external_identity_id::text
|
|
90
|
+
OR NEW.canonical_snapshot #>> '{actor,externalSubjectId}' IS DISTINCT FROM l.external_subject_id
|
|
91
|
+
OR NEW.canonical_snapshot #>> '{actor,externalAuthorizationRevision}' IS DISTINCT FROM l.external_authorization_revision::text
|
|
92
|
+
OR jsonb_typeof(NEW.canonical_snapshot -> 'permissions') IS DISTINCT FROM 'array'
|
|
93
|
+
OR NOT (NEW.canonical_snapshot -> 'permissions') <@ l.permissions THEN
|
|
94
|
+
RAISE EXCEPTION 'invalid external link work snapshot' USING ERRCODE = '42501';
|
|
95
|
+
END IF;
|
|
96
|
+
IF TG_TABLE_NAME = 'external_link_task_authorities' THEN
|
|
97
|
+
IF NOT EXISTS (SELECT 1 FROM scheduled_tasks task WHERE task.id = NEW.task_id
|
|
98
|
+
AND task.account_id = NEW.account_id AND task.workspace_id = NEW.workspace_id
|
|
99
|
+
AND task.authority_revision = NEW.task_revision) THEN
|
|
100
|
+
RAISE EXCEPTION 'external link task revision unavailable' USING ERRCODE = '42501';
|
|
101
|
+
END IF;
|
|
102
|
+
revision_authority := scheduled_task_revision_authority_snapshot(NEW.account_id, NEW.workspace_id, NEW.task_id, NEW.task_revision);
|
|
103
|
+
IF revision_authority IS NULL OR revision_authority ->> 'subjectId' IS DISTINCT FROM l.native_subject_id
|
|
104
|
+
OR revision_authority ->> 'organizationMembershipId' IS DISTINCT FROM l.native_membership_id::text
|
|
105
|
+
OR revision_authority ->> 'membershipAuthorizationRevision' IS DISTINCT FROM l.native_authorization_revision::text THEN
|
|
106
|
+
RAISE EXCEPTION 'external link task owner changed' USING ERRCODE = '42501';
|
|
107
|
+
END IF;
|
|
108
|
+
RETURN NEW;
|
|
109
|
+
END IF;
|
|
110
|
+
SELECT * INTO t FROM session_turns WHERE id = NEW.turn_id AND session_id = NEW.session_id
|
|
111
|
+
AND account_id = NEW.account_id AND workspace_id = NEW.workspace_id;
|
|
112
|
+
SELECT * INTO s FROM sessions WHERE id = NEW.session_id AND account_id = NEW.account_id AND workspace_id = NEW.workspace_id;
|
|
113
|
+
IF t.id IS NULL OR s.id IS NULL OR t.initiating_human_subject_id IS DISTINCT FROM l.native_subject_id
|
|
114
|
+
OR EXISTS (SELECT 1 FROM session_turn_attempts a WHERE a.turn_id = t.id) THEN
|
|
115
|
+
RAISE EXCEPTION 'external link turn admission unavailable' USING ERRCODE = '42501';
|
|
116
|
+
END IF;
|
|
117
|
+
IF NEW.source_kind = 'direct' THEN
|
|
118
|
+
IF t.source NOT IN ('user','api') OR t.status <> 'queued' OR t.active_attempt_id IS NOT NULL
|
|
119
|
+
OR l.status <> 'active' OR l.revision <> NEW.link_revision
|
|
120
|
+
OR (l.expires_at IS NOT NULL AND l.expires_at <= clock_timestamp()) THEN
|
|
121
|
+
RAISE EXCEPTION 'external link direct admission unavailable' USING ERRCODE = '42501';
|
|
122
|
+
END IF;
|
|
123
|
+
ELSIF NEW.source_kind IN ('causal','child') THEN
|
|
124
|
+
SELECT a.canonical_snapshot INTO source_snapshot FROM external_link_turn_authorities a
|
|
125
|
+
WHERE a.turn_id = NEW.source_turn_id AND a.account_id = NEW.account_id AND a.workspace_id = NEW.workspace_id;
|
|
126
|
+
IF source_snapshot IS DISTINCT FROM NEW.canonical_snapshot THEN
|
|
127
|
+
RAISE EXCEPTION 'external link source snapshot changed' USING ERRCODE = '42501';
|
|
128
|
+
END IF;
|
|
129
|
+
IF NEW.source_kind = 'child' AND (s.parent_turn_id IS DISTINCT FROM NEW.source_turn_id OR t.status <> 'queued') THEN
|
|
130
|
+
RAISE EXCEPTION 'external link child source unavailable' USING ERRCODE = '42501';
|
|
131
|
+
END IF;
|
|
132
|
+
IF NEW.source_kind = 'child' AND NOT EXISTS (SELECT 1 FROM session_turns source
|
|
133
|
+
WHERE source.id = NEW.source_turn_id AND source.session_id = s.parent_session_id) THEN
|
|
134
|
+
RAISE EXCEPTION 'external link child parent mismatch' USING ERRCODE = '42501';
|
|
135
|
+
END IF;
|
|
136
|
+
IF NEW.source_kind = 'causal' AND (t.source NOT IN ('goal','system') OR t.status <> 'running'
|
|
137
|
+
OR NOT EXISTS (SELECT 1 FROM session_turns source WHERE source.id = NEW.source_turn_id AND source.session_id = t.session_id)) THEN
|
|
138
|
+
RAISE EXCEPTION 'external link causal source unavailable' USING ERRCODE = '42501';
|
|
139
|
+
END IF;
|
|
140
|
+
IF NEW.source_kind = 'causal' AND NOT EXISTS (SELECT 1 FROM session_system_updates u
|
|
141
|
+
WHERE u.account_id = NEW.account_id AND u.workspace_id = NEW.workspace_id AND u.session_id = NEW.session_id
|
|
142
|
+
AND u.delivered_turn_id = NEW.turn_id AND u.state = 'delivered' AND u.delivered_history_item_id IS NOT NULL
|
|
143
|
+
AND ((u.kind = 'goal_continuation' AND u.lineage ->> 'causalTurnId' = NEW.source_turn_id::text)
|
|
144
|
+
OR (u.kind IN ('child_terminal_result','child_requires_action','child_requires_action_resolved','child_paused','child_waiting_capacity','child_progress')
|
|
145
|
+
AND u.lineage ->> 'parentTurnId' = NEW.source_turn_id::text))) THEN
|
|
146
|
+
RAISE EXCEPTION 'external link exact causal delivery unavailable' USING ERRCODE = '42501';
|
|
147
|
+
END IF;
|
|
148
|
+
ELSE
|
|
149
|
+
SELECT a.canonical_snapshot INTO source_snapshot FROM external_link_task_authorities a
|
|
150
|
+
WHERE a.task_id = NEW.source_task_id AND a.task_revision = NEW.source_task_revision
|
|
151
|
+
AND a.account_id = NEW.account_id AND a.workspace_id = NEW.workspace_id;
|
|
152
|
+
IF source_snapshot IS DISTINCT FROM NEW.canonical_snapshot OR t.source <> 'system' OR t.status <> 'running' THEN
|
|
153
|
+
RAISE EXCEPTION 'external link scheduled source unavailable' USING ERRCODE = '42501';
|
|
154
|
+
END IF;
|
|
155
|
+
IF NOT EXISTS (SELECT 1 FROM scheduled_task_runs r WHERE r.id = t.scheduled_task_run_id
|
|
156
|
+
AND r.task_id = NEW.source_task_id AND r.task_authority_revision = NEW.source_task_revision
|
|
157
|
+
AND r.account_id = NEW.account_id AND r.workspace_id = NEW.workspace_id AND r.session_id = NEW.session_id
|
|
158
|
+
AND r.accepted_execution #>> '{causalHuman,subjectId}' = l.native_subject_id) THEN
|
|
159
|
+
RAISE EXCEPTION 'external link scheduled run mismatch' USING ERRCODE = '42501';
|
|
160
|
+
END IF;
|
|
161
|
+
END IF;
|
|
162
|
+
RETURN NEW;
|
|
163
|
+
END
|
|
164
|
+
$body$;
|
|
165
|
+
CREATE TRIGGER external_link_turn_snapshot_guard BEFORE INSERT OR UPDATE ON external_link_turn_authorities
|
|
166
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.guard_external_link_work_snapshot();
|
|
167
|
+
CREATE TRIGGER external_link_task_snapshot_guard BEFORE INSERT OR UPDATE ON external_link_task_authorities
|
|
168
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.guard_external_link_work_snapshot();
|
|
169
|
+
REVOKE ALL ON TABLE external_link_turn_authorities, external_link_task_authorities FROM PUBLIC;
|
|
170
|
+
REVOKE ALL ON FUNCTION opengeni_private.guard_external_link_work_snapshot() FROM PUBLIC;
|
|
171
|
+
DO $drain$
|
|
172
|
+
BEGIN
|
|
173
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a
|
|
174
|
+
JOIN jsonb_array_elements_text(current_setting('opengeni.migration_application_roles')::jsonb) r ON r = a.usename
|
|
175
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
176
|
+
RAISE EXCEPTION '0450 requires stopped application sessions' USING ERRCODE = '55000';
|
|
177
|
+
END IF;
|
|
178
|
+
END
|
|
179
|
+
$drain$;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
SET LOCAL lock_timeout = '5s';
|
|
3
|
+
SET LOCAL statement_timeout = '5min';
|
|
4
|
+
DO $drain$
|
|
5
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
6
|
+
BEGIN
|
|
7
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' THEN
|
|
8
|
+
RAISE EXCEPTION '0451 requires application database roles' USING ERRCODE = '55000';
|
|
9
|
+
END IF;
|
|
10
|
+
IF jsonb_array_length(roles) NOT BETWEEN 1 AND 16 OR EXISTS (
|
|
11
|
+
SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
|
|
12
|
+
OR btrim(item #>> '{}') = '' OR item #>> '{}' <> btrim(item #>> '{}')
|
|
13
|
+
OR octet_length(item #>> '{}') > 63
|
|
14
|
+
) THEN RAISE EXCEPTION '0451 invalid application roles' USING ERRCODE = '55000'; END IF;
|
|
15
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
16
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
17
|
+
RAISE EXCEPTION '0451 requires stopped application sessions' USING ERRCODE = '55000';
|
|
18
|
+
END IF;
|
|
19
|
+
END
|
|
20
|
+
$drain$;
|
|
21
|
+
|
|
22
|
+
-- Preserve all 0426 exact-work guards. Replace one asserted clause, not a
|
|
23
|
+
-- best-effort search, so divergence from that immutable routine aborts migration.
|
|
24
|
+
DO $ceiling$
|
|
25
|
+
DECLARE
|
|
26
|
+
definition text := pg_get_functiondef('opengeni_private.guard_external_link_work_snapshot()'::regprocedure);
|
|
27
|
+
prior text := 'OR NOT (NEW.canonical_snapshot -> ''permissions'') <@ l.permissions THEN';
|
|
28
|
+
replacement text := $clause$OR EXISTS (
|
|
29
|
+
SELECT 1 FROM jsonb_array_elements(NEW.canonical_snapshot -> 'permissions') p
|
|
30
|
+
WHERE jsonb_typeof(p) <> 'string' OR NOT (
|
|
31
|
+
l.permissions ? (p #>> '{}') OR
|
|
32
|
+
(l.permissions ? 'workspace:admin' AND p #>> '{}' <> 'secrets:read')
|
|
33
|
+
)
|
|
34
|
+
) THEN$clause$;
|
|
35
|
+
BEGIN
|
|
36
|
+
IF length(definition) - length(replace(definition, prior, '')) <> length(prior) THEN
|
|
37
|
+
RAISE EXCEPTION '0451 expected exactly one 0426 permission clause' USING ERRCODE = '55000';
|
|
38
|
+
END IF;
|
|
39
|
+
EXECUTE replace(definition, prior, replacement);
|
|
40
|
+
END
|
|
41
|
+
$ceiling$;
|
|
42
|
+
|
|
43
|
+
-- The app role cannot SELECT external_identities directly. Expose only the
|
|
44
|
+
-- challenged request's display reference to an active native organization member.
|
|
45
|
+
CREATE FUNCTION get_external_identity_link_reference(p_account_id uuid, p_link_id uuid, p_challenge_digest text)
|
|
46
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
47
|
+
DECLARE
|
|
48
|
+
result jsonb;
|
|
49
|
+
prior text := coalesce(current_setting('opengeni.organization_tenancy_lifecycle', true), '');
|
|
50
|
+
subject text := opengeni_private.current_subject_id();
|
|
51
|
+
BEGIN
|
|
52
|
+
IF p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
53
|
+
OR subject IS NULL OR subject !~ '^user:[^\r\n]+$'
|
|
54
|
+
OR p_challenge_digest !~ '^[0-9a-f]{64}$' THEN
|
|
55
|
+
RAISE EXCEPTION 'native identity link preview authority required' USING ERRCODE = '42501';
|
|
56
|
+
END IF;
|
|
57
|
+
IF NOT EXISTS (SELECT 1 FROM jsonb_array_elements(list_self_organization_memberships(subject)) m
|
|
58
|
+
WHERE m ->> 'organizationId' = p_account_id::text AND m ->> 'status' = 'active') THEN
|
|
59
|
+
RAISE EXCEPTION 'native identity link organization unavailable' USING ERRCODE = '42501';
|
|
60
|
+
END IF;
|
|
61
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', 'external_identity_provisioning', true);
|
|
62
|
+
SELECT jsonb_build_object('source', e.source, 'externalId', e.external_id) INTO result
|
|
63
|
+
FROM external_identity_links l JOIN external_identities e
|
|
64
|
+
ON e.id = l.external_identity_id AND e.account_id = l.account_id
|
|
65
|
+
WHERE l.account_id = p_account_id AND l.id = p_link_id AND l.challenge_digest = p_challenge_digest
|
|
66
|
+
AND l.status = 'pending' AND l.confirm_before > clock_timestamp()
|
|
67
|
+
AND (l.expires_at IS NULL OR l.expires_at > clock_timestamp())
|
|
68
|
+
AND e.status = 'active' AND e.authorization_revision = l.external_authorization_revision;
|
|
69
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', prior, true);
|
|
70
|
+
RETURN result;
|
|
71
|
+
END
|
|
72
|
+
$body$;
|
|
73
|
+
REVOKE ALL ON FUNCTION get_external_identity_link_reference(uuid, uuid, text) FROM PUBLIC;
|
|
74
|
+
DO $drain$
|
|
75
|
+
BEGIN
|
|
76
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a
|
|
77
|
+
JOIN jsonb_array_elements_text(current_setting('opengeni.migration_application_roles')::jsonb) r ON r = a.usename
|
|
78
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
79
|
+
RAISE EXCEPTION '0451 requires stopped application sessions' USING ERRCODE = '55000';
|
|
80
|
+
END IF;
|
|
81
|
+
END
|
|
82
|
+
$drain$;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
SET LOCAL lock_timeout = '5s';
|
|
3
|
+
SET LOCAL statement_timeout = '5min';
|
|
4
|
+
DO $migration$
|
|
5
|
+
DECLARE
|
|
6
|
+
roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
7
|
+
definition text := pg_get_functiondef('opengeni_private.guard_external_link_work_snapshot()'::regprocedure);
|
|
8
|
+
prior text := 'AND r.accepted_execution #>> ''{causalHuman,subjectId}'' = l.native_subject_id';
|
|
9
|
+
replacement text := $clause$AND r.status = 'dispatched'
|
|
10
|
+
AND r.accepted_execution_snapshot ->> 'causalHumanSubjectId' = l.native_subject_id
|
|
11
|
+
AND validate_scheduled_agent_run_live_authority(NEW.account_id, NEW.workspace_id, r.id) IS NULL$clause$;
|
|
12
|
+
BEGIN
|
|
13
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' THEN
|
|
14
|
+
RAISE EXCEPTION '0452 requires application database roles' USING ERRCODE = '55000';
|
|
15
|
+
END IF;
|
|
16
|
+
IF jsonb_array_length(roles) NOT BETWEEN 1 AND 16 OR EXISTS (
|
|
17
|
+
SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
|
|
18
|
+
OR btrim(item #>> '{}') = '' OR item #>> '{}' <> btrim(item #>> '{}')
|
|
19
|
+
OR octet_length(item #>> '{}') > 63
|
|
20
|
+
) THEN RAISE EXCEPTION '0452 invalid application roles' USING ERRCODE = '55000'; END IF;
|
|
21
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
22
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
23
|
+
RAISE EXCEPTION '0452 requires stopped application sessions' USING ERRCODE = '55000';
|
|
24
|
+
END IF;
|
|
25
|
+
IF length(definition) - length(replace(definition, prior, '')) <> length(prior) THEN
|
|
26
|
+
RAISE EXCEPTION '0452 expected exactly one scheduled origin clause' USING ERRCODE = '55000';
|
|
27
|
+
END IF;
|
|
28
|
+
EXECUTE replace(definition, prior, replacement);
|
|
29
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
30
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
31
|
+
RAISE EXCEPTION '0452 requires stopped application sessions' USING ERRCODE = '55000';
|
|
32
|
+
END IF;
|
|
33
|
+
END
|
|
34
|
+
$migration$;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Host ownership follows the effective organization member. Linking creates no
|
|
3
|
+
-- ownership transfer: old external bindings remain external, new native bindings
|
|
4
|
+
-- are independent. Accepted linked work retains its separate revocable link.
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
DO $drain$
|
|
8
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
9
|
+
BEGIN
|
|
10
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' THEN
|
|
11
|
+
RAISE EXCEPTION '0453 requires application database roles' USING ERRCODE = '55000';
|
|
12
|
+
END IF;
|
|
13
|
+
IF jsonb_array_length(roles) NOT BETWEEN 1 AND 16 OR EXISTS (
|
|
14
|
+
SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
|
|
15
|
+
OR btrim(item #>> '{}') = '' OR item #>> '{}' <> btrim(item #>> '{}')
|
|
16
|
+
OR octet_length(item #>> '{}') > 63
|
|
17
|
+
) THEN RAISE EXCEPTION '0453 invalid application roles' USING ERRCODE = '55000'; END IF;
|
|
18
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
19
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
20
|
+
RAISE EXCEPTION '0453 requires stopped application sessions' USING ERRCODE = '55000';
|
|
21
|
+
END IF;
|
|
22
|
+
END
|
|
23
|
+
$drain$;
|
|
24
|
+
ALTER TABLE host_mcp_bindings
|
|
25
|
+
DROP CONSTRAINT host_mcp_bindings_external_owner_fk,
|
|
26
|
+
ADD CONSTRAINT host_mcp_bindings_member_owner_fk FOREIGN KEY (account_id, owner_subject_id)
|
|
27
|
+
REFERENCES organization_memberships(account_id, subject_id),
|
|
28
|
+
ADD CONSTRAINT host_mcp_bindings_human_owner_check CHECK (
|
|
29
|
+
owner_subject_id ~ '^(external_user:|user:)[^\r\n]+$');
|
|
30
|
+
ALTER TABLE host_mcp_delegations
|
|
31
|
+
DROP CONSTRAINT host_mcp_delegations_external_owner_fk,
|
|
32
|
+
ADD CONSTRAINT host_mcp_delegations_member_owner_fk FOREIGN KEY (account_id, owner_subject_id)
|
|
33
|
+
REFERENCES organization_memberships(account_id, subject_id),
|
|
34
|
+
ADD CONSTRAINT host_mcp_delegations_human_owner_check CHECK (
|
|
35
|
+
owner_subject_id ~ '^(external_user:|user:)[^\r\n]+$');
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Preserve the initiating external restriction separately from the effective
|
|
3
|
+
-- resource owner. This is credential-free provenance, never a bearer token.
|
|
4
|
+
SET LOCAL lock_timeout = '5s';
|
|
5
|
+
SET LOCAL statement_timeout = '5min';
|
|
6
|
+
DO $drain$
|
|
7
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
8
|
+
BEGIN
|
|
9
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' THEN
|
|
10
|
+
RAISE EXCEPTION '0454 requires application database roles' USING ERRCODE = '55000';
|
|
11
|
+
END IF;
|
|
12
|
+
IF jsonb_array_length(roles) NOT BETWEEN 1 AND 16 OR EXISTS (
|
|
13
|
+
SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
|
|
14
|
+
OR btrim(item #>> '{}') = '' OR item #>> '{}' <> btrim(item #>> '{}')
|
|
15
|
+
OR octet_length(item #>> '{}') > 63
|
|
16
|
+
) THEN RAISE EXCEPTION '0454 invalid application roles' USING ERRCODE = '55000'; END IF;
|
|
17
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
18
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
19
|
+
RAISE EXCEPTION '0454 requires stopped application sessions' USING ERRCODE = '55000';
|
|
20
|
+
END IF;
|
|
21
|
+
END
|
|
22
|
+
$drain$;
|
|
23
|
+
ALTER TABLE connect_attempts ADD COLUMN external_continuation jsonb
|
|
24
|
+
CHECK (external_continuation IS NULL OR (jsonb_typeof(external_continuation) = 'object'
|
|
25
|
+
AND octet_length(external_continuation::text) <= 32768
|
|
26
|
+
AND external_continuation #>> '{actor,accountId}' = account_id::text
|
|
27
|
+
AND external_continuation #>> '{actor,effectiveSubjectId}' = subject_id));
|
|
28
|
+
CREATE FUNCTION opengeni_private.guard_connect_attempt_origin() RETURNS trigger
|
|
29
|
+
LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$
|
|
30
|
+
BEGIN
|
|
31
|
+
IF ROW(NEW.id, NEW.account_id, NEW.workspace_id, NEW.subject_id, NEW.idempotency_key_hash,
|
|
32
|
+
NEW.request_digest, NEW.return_url, NEW.external_continuation, NEW.expires_at, NEW.created_at)
|
|
33
|
+
IS DISTINCT FROM ROW(OLD.id, OLD.account_id, OLD.workspace_id, OLD.subject_id, OLD.idempotency_key_hash,
|
|
34
|
+
OLD.request_digest, OLD.return_url, OLD.external_continuation, OLD.expires_at, OLD.created_at) THEN
|
|
35
|
+
RAISE EXCEPTION 'Connect origin is immutable' USING ERRCODE = '42501';
|
|
36
|
+
END IF;
|
|
37
|
+
RETURN NEW;
|
|
38
|
+
END
|
|
39
|
+
$body$;
|
|
40
|
+
REVOKE ALL ON FUNCTION opengeni_private.guard_connect_attempt_origin() FROM PUBLIC;
|
|
41
|
+
CREATE TRIGGER connect_attempt_origin_guard BEFORE UPDATE ON connect_attempts
|
|
42
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.guard_connect_attempt_origin();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
SET LOCAL lock_timeout = '5s';
|
|
3
|
+
SET LOCAL statement_timeout = '5min';
|
|
4
|
+
DO $drain$
|
|
5
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
6
|
+
BEGIN
|
|
7
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' OR jsonb_array_length(roles) NOT BETWEEN 1 AND 16 THEN
|
|
8
|
+
RAISE EXCEPTION '0455 requires application database roles' USING ERRCODE = '55000';
|
|
9
|
+
END IF;
|
|
10
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
11
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
12
|
+
RAISE EXCEPTION '0455 requires stopped application sessions' USING ERRCODE = '55000';
|
|
13
|
+
END IF;
|
|
14
|
+
END
|
|
15
|
+
$drain$;
|
|
16
|
+
|
|
17
|
+
-- No SELECT grant on external_identities. Only a current participant can resolve
|
|
18
|
+
-- labels for the bounded links already returned by the participant inventory.
|
|
19
|
+
CREATE FUNCTION get_external_identity_link_inventory_references(p_account_id uuid, p_link_ids uuid[])
|
|
20
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
21
|
+
DECLARE
|
|
22
|
+
result jsonb;
|
|
23
|
+
prior text := coalesce(current_setting('opengeni.organization_tenancy_lifecycle', true), '');
|
|
24
|
+
subject text := opengeni_private.current_subject_id();
|
|
25
|
+
BEGIN
|
|
26
|
+
IF p_account_id IS DISTINCT FROM opengeni_private.current_account_id() OR subject IS NULL
|
|
27
|
+
OR p_link_ids IS NULL OR coalesce(cardinality(p_link_ids), 0) > 50 THEN
|
|
28
|
+
RAISE EXCEPTION 'identity link inventory authority required' USING ERRCODE = '42501';
|
|
29
|
+
END IF;
|
|
30
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', 'external_identity_provisioning', true);
|
|
31
|
+
IF NOT EXISTS (SELECT 1 FROM organization_memberships m WHERE m.account_id = p_account_id
|
|
32
|
+
AND m.subject_id = subject AND m.status = 'active') THEN
|
|
33
|
+
RAISE EXCEPTION 'identity link organization unavailable' USING ERRCODE = '42501';
|
|
34
|
+
END IF;
|
|
35
|
+
SELECT coalesce(jsonb_object_agg(l.id::text, jsonb_build_object('source', e.source, 'externalId', e.external_id)), '{}'::jsonb)
|
|
36
|
+
INTO result FROM external_identity_links l JOIN external_identities e
|
|
37
|
+
ON e.id = l.external_identity_id AND e.account_id = l.account_id
|
|
38
|
+
WHERE l.account_id = p_account_id AND l.id = ANY(p_link_ids)
|
|
39
|
+
AND (l.external_subject_id = subject OR l.native_subject_id = subject);
|
|
40
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', prior, true);
|
|
41
|
+
RETURN result;
|
|
42
|
+
END
|
|
43
|
+
$body$;
|
|
44
|
+
REVOKE ALL ON FUNCTION get_external_identity_link_inventory_references(uuid, uuid[]) FROM PUBLIC;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
SET LOCAL lock_timeout = '5s';
|
|
3
|
+
SET LOCAL statement_timeout = '5min';
|
|
4
|
+
ALTER TABLE social_connections ADD COLUMN version integer NOT NULL DEFAULT 1 CHECK (version > 0);
|
|
5
|
+
CREATE FUNCTION bump_social_connection_version() RETURNS trigger
|
|
6
|
+
LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$
|
|
7
|
+
BEGIN
|
|
8
|
+
NEW.version := OLD.version + 1;
|
|
9
|
+
RETURN NEW;
|
|
10
|
+
END
|
|
11
|
+
$body$;
|
|
12
|
+
REVOKE ALL ON FUNCTION bump_social_connection_version() FROM PUBLIC;
|
|
13
|
+
CREATE TRIGGER social_connection_version BEFORE UPDATE ON social_connections
|
|
14
|
+
FOR EACH ROW EXECUTE FUNCTION bump_social_connection_version();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Deploy with the embedding maintenance cutover: stop old API/worker writers
|
|
3
|
+
-- before activation and never restart a label-authority binary afterward.
|
|
4
|
+
-- New sessions bind agent scope to canonical authenticated user authority.
|
|
5
|
+
-- Do not infer a user from historical end_user labels or promote old private
|
|
6
|
+
-- memory rows. Old sessions remain readable under their existing visibility;
|
|
7
|
+
-- a null scope cannot grant cross-tree user access. Their old labels and
|
|
8
|
+
-- session-scoped memory records are retained as historical data only.
|
|
9
|
+
SET LOCAL lock_timeout = '5s';
|
|
10
|
+
SET LOCAL statement_timeout = '5min';
|
|
11
|
+
|
|
12
|
+
DO $drain$
|
|
13
|
+
DECLARE
|
|
14
|
+
roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
15
|
+
BEGIN
|
|
16
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' THEN
|
|
17
|
+
RAISE EXCEPTION '0457 requires an explicit application database role list' USING ERRCODE = '55000';
|
|
18
|
+
END IF;
|
|
19
|
+
IF jsonb_array_length(roles) NOT BETWEEN 1 AND 16 OR EXISTS (
|
|
20
|
+
SELECT 1 FROM jsonb_array_elements(roles) item
|
|
21
|
+
WHERE jsonb_typeof(item) <> 'string' OR btrim(item #>> '{}') = ''
|
|
22
|
+
OR item #>> '{}' <> btrim(item #>> '{}') OR octet_length(item #>> '{}') > 63
|
|
23
|
+
) THEN
|
|
24
|
+
RAISE EXCEPTION '0457 received invalid application database roles' USING ERRCODE = '55000';
|
|
25
|
+
END IF;
|
|
26
|
+
IF EXISTS (
|
|
27
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
28
|
+
JOIN jsonb_array_elements_text(roles) role_name ON role_name = activity.usename
|
|
29
|
+
WHERE activity.datname = current_database() AND activity.pid <> pg_backend_pid()
|
|
30
|
+
) THEN
|
|
31
|
+
RAISE EXCEPTION '0457 requires all application database sessions stopped' USING ERRCODE = '55000';
|
|
32
|
+
END IF;
|
|
33
|
+
END
|
|
34
|
+
$drain$;
|
|
35
|
+
|
|
36
|
+
ALTER TABLE sessions ADD COLUMN scope_subject_id text,
|
|
37
|
+
ADD CONSTRAINT sessions_scope_subject_id_check CHECK (
|
|
38
|
+
scope_subject_id IS NULL OR (
|
|
39
|
+
octet_length(scope_subject_id) BETWEEN 1 AND 1024
|
|
40
|
+
AND scope_subject_id ~ '^(user:|external_user:).+'
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE INDEX sessions_workspace_scope_subject_idx
|
|
45
|
+
ON sessions (workspace_id, scope_subject_id)
|
|
46
|
+
WHERE scope_subject_id IS NOT NULL;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- No pending cards or historical answers are rewritten. Compatibility applies
|
|
3
|
+
-- only to an explicit Save/Don't save answer admitted by the canonical human
|
|
4
|
+
-- path, with all existing immutable-receipt, tenant, turn and revision fences.
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '1min';
|
|
7
|
+
|
|
8
|
+
DO $patch$
|
|
9
|
+
DECLARE
|
|
10
|
+
definition text := pg_get_functiondef('skill_apply_lifecycle(uuid,uuid,jsonb,jsonb)'::regprocedure);
|
|
11
|
+
anchor text;
|
|
12
|
+
replacement text;
|
|
13
|
+
BEGIN
|
|
14
|
+
-- The old generic choice serializer forced Other on. It is not consent:
|
|
15
|
+
-- retain a typed boolean shape, and independently forbid Other in the answer.
|
|
16
|
+
anchor := $old$AND coalesce(h.questions->0->'allowOther','false'::jsonb)='false'::jsonb$old$;
|
|
17
|
+
replacement := $new$AND coalesce(h.questions->0->'allowOther','false'::jsonb) IN ('false'::jsonb,'true'::jsonb)
|
|
18
|
+
AND (answer->'other' IS NULL OR answer->'other'='null'::jsonb OR answer->'other'='""'::jsonb)$new$;
|
|
19
|
+
IF (length(definition)-length(replace(definition,anchor,'')))/length(anchor) <> 1 THEN
|
|
20
|
+
RAISE EXCEPTION '0458 Skill Other compatibility anchor mismatch';
|
|
21
|
+
END IF;
|
|
22
|
+
definition := replace(definition,anchor,replacement);
|
|
23
|
+
|
|
24
|
+
-- Null optional descriptions are the same absent presentation value. Keep
|
|
25
|
+
-- exact option count/order/ids/labels and reject every non-null extra field.
|
|
26
|
+
anchor := $old$AND h.questions->0->'options'='[{"id":"save","label":"Save"},{"id":"skip","label":"Don''t save"}]'::jsonb$old$;
|
|
27
|
+
replacement := $new$AND jsonb_array_length(h.questions->0->'options')=2
|
|
28
|
+
AND ((h.questions->0->'options'->0) - 'description')='{"id":"save","label":"Save"}'::jsonb
|
|
29
|
+
AND ((h.questions->0->'options'->1) - 'description')='{"id":"skip","label":"Don''t save"}'::jsonb
|
|
30
|
+
AND coalesce(h.questions->0->'options'->0->'description','null'::jsonb)='null'::jsonb
|
|
31
|
+
AND coalesce(h.questions->0->'options'->1->'description','null'::jsonb)='null'::jsonb$new$;
|
|
32
|
+
IF (length(definition)-length(replace(definition,anchor,'')))/length(anchor) <> 1 THEN
|
|
33
|
+
RAISE EXCEPTION '0458 Skill option compatibility anchor mismatch';
|
|
34
|
+
END IF;
|
|
35
|
+
EXECUTE replace(definition,anchor,replacement);
|
|
36
|
+
END $patch$;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/db",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0-canary.0",
|
|
4
4
|
"description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -94,11 +94,11 @@
|
|
|
94
94
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
95
95
|
},
|
|
96
96
|
"dependencies": {
|
|
97
|
-
"@opengeni/codemode": "^0.5.
|
|
98
|
-
"@opengeni/codex": "^0.2.22",
|
|
99
|
-
"@opengeni/config": "^1.0.
|
|
100
|
-
"@opengeni/contracts": "^
|
|
101
|
-
"@opengeni/network": "^0.3.1",
|
|
97
|
+
"@opengeni/codemode": "^0.5.4-canary.0",
|
|
98
|
+
"@opengeni/codex": "^0.2.22-canary.6",
|
|
99
|
+
"@opengeni/config": "^1.1.0-canary.0",
|
|
100
|
+
"@opengeni/contracts": "^3.0.0-canary.0",
|
|
101
|
+
"@opengeni/network": "^0.3.1-canary.6",
|
|
102
102
|
"drizzle-orm": "^0.45.2",
|
|
103
103
|
"postgres": "^3.4.7"
|
|
104
104
|
},
|