@opengeni/db 3.7.4 → 3.8.2-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/automations.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-O56DM5GZ.js → chunk-53A2PEHT.js} +328 -31
- package/dist/chunk-53A2PEHT.js.map +1 -0
- package/dist/{chunk-SZAWDXQC.js → chunk-5S4BQAHX.js} +3 -3
- package/dist/{chunk-S3TDDCS2.js → chunk-CITYUGMW.js} +3 -3
- package/dist/{chunk-4NAR7FBA.js → chunk-F5YOZEYQ.js} +49 -1
- package/dist/chunk-F5YOZEYQ.js.map +1 -0
- package/dist/{chunk-F6PBS6WE.js → chunk-HZV7O6JR.js} +2 -2
- package/dist/{chunk-5NNCGFQY.js → chunk-RLQVPOVR.js} +2 -2
- package/dist/{chunk-DZCYW2WT.js → chunk-UKOGHUGC.js} +2 -2
- package/dist/{chunk-US4IS6IV.js → chunk-X6TO5HII.js} +2 -2
- package/dist/{chunk-U4WPPEBH.js → chunk-Y53WEFYB.js} +2 -2
- package/dist/connection-token-resolver.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +164 -14
- package/dist/index.js +7327 -5442
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +9 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/model-catalog.d.ts +153 -0
- package/dist/new-session-drafts.d.ts +12 -0
- package/dist/organization-membership-lifecycle.d.ts +8 -1
- package/dist/organization-model-providers.d.ts +101 -0
- package/dist/pack-components.d.ts +1 -0
- package/dist/pr-review.d.ts +9 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +7 -7
- package/dist/schema.d.ts +989 -2
- package/dist/schema.js +11 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +35 -0
- package/dist/session-queue-commands.d.ts +17 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/xai-subscription.d.ts +8 -0
- package/drizzle/0387_bound_host_export_session_payloads.sql +165 -0
- package/drizzle/0388_sandbox_provider_deadline_interactions.sql +491 -0
- package/drizzle/0389_model_catalog_and_gateway_custom_models.sql +241 -0
- package/drizzle/0390_organization_model_provider_connections.sql +197 -0
- package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
- package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
- package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
- package/drizzle/0394_session_selected_skill_activation.sql +20 -0
- package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
- package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
- package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
- package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
- package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
- package/package.json +6 -6
- package/src/automations.ts +9 -1
- package/src/connection-token-resolver.ts +12 -0
- package/src/index.ts +1551 -272
- package/src/insights-model-bundle.ts +54 -5
- package/src/insights.ts +53 -7
- package/src/model-catalog.ts +682 -0
- package/src/new-session-drafts.ts +66 -21
- package/src/organization-membership-lifecycle.ts +47 -5
- package/src/organization-model-providers.ts +614 -0
- package/src/pack-components.ts +15 -2
- package/src/pr-review.ts +19 -0
- package/src/provision-roles.ts +2 -0
- package/src/runtime-posture.ts +48 -0
- package/src/schema.ts +348 -32
- package/src/session-control.ts +170 -0
- package/src/session-queue-commands.ts +157 -82
- package/src/xai-subscription.ts +66 -14
- package/dist/chunk-4NAR7FBA.js.map +0 -1
- package/dist/chunk-O56DM5GZ.js.map +0 -1
- /package/dist/{chunk-SZAWDXQC.js.map → chunk-5S4BQAHX.js.map} +0 -0
- /package/dist/{chunk-S3TDDCS2.js.map → chunk-CITYUGMW.js.map} +0 -0
- /package/dist/{chunk-F6PBS6WE.js.map → chunk-HZV7O6JR.js.map} +0 -0
- /package/dist/{chunk-5NNCGFQY.js.map → chunk-RLQVPOVR.js.map} +0 -0
- /package/dist/{chunk-DZCYW2WT.js.map → chunk-UKOGHUGC.js.map} +0 -0
- /package/dist/{chunk-US4IS6IV.js.map → chunk-X6TO5HII.js.map} +0 -0
- /package/dist/{chunk-U4WPPEBH.js.map → chunk-Y53WEFYB.js.map} +0 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Migration 0399 follows organization workspace management activation.
|
|
3
|
+
-- Lets an already-onboarded, verified managed human create another independent
|
|
4
|
+
-- organization. One transaction creates the organization, its Personal
|
|
5
|
+
-- workspace, its first shared workspace, and both access anchors. The original
|
|
6
|
+
-- first-sign-in setup remains a separate one-shot lifecycle so invitations and
|
|
7
|
+
-- legacy-account adoption keep their existing precedence and semantics.
|
|
8
|
+
|
|
9
|
+
SET LOCAL lock_timeout = '5s';
|
|
10
|
+
SET LOCAL statement_timeout = '10min';
|
|
11
|
+
|
|
12
|
+
CREATE TABLE additional_organization_creation_receipts (
|
|
13
|
+
operation_id uuid PRIMARY KEY,
|
|
14
|
+
actor_subject_id text NOT NULL,
|
|
15
|
+
account_id uuid NOT NULL UNIQUE
|
|
16
|
+
REFERENCES managed_accounts(id) ON DELETE RESTRICT,
|
|
17
|
+
organization_membership_id uuid NOT NULL UNIQUE
|
|
18
|
+
REFERENCES organization_memberships(id) ON DELETE RESTRICT,
|
|
19
|
+
personal_workspace_id uuid NOT NULL UNIQUE
|
|
20
|
+
REFERENCES workspaces(id) ON DELETE RESTRICT,
|
|
21
|
+
shared_workspace_id uuid NOT NULL UNIQUE
|
|
22
|
+
REFERENCES workspaces(id) ON DELETE RESTRICT,
|
|
23
|
+
organization_name text NOT NULL,
|
|
24
|
+
workspace_name text NOT NULL,
|
|
25
|
+
result jsonb NOT NULL,
|
|
26
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
27
|
+
CONSTRAINT additional_organization_creation_actor_check CHECK (
|
|
28
|
+
actor_subject_id LIKE 'user:%'
|
|
29
|
+
AND octet_length(convert_to(actor_subject_id, 'UTF8')) BETWEEN 6 AND 1024
|
|
30
|
+
),
|
|
31
|
+
CONSTRAINT additional_organization_creation_names_check CHECK (
|
|
32
|
+
organization_name = btrim(organization_name)
|
|
33
|
+
AND char_length(organization_name) BETWEEN 1 AND 120
|
|
34
|
+
AND octet_length(convert_to(organization_name, 'UTF8')) BETWEEN 1 AND 480
|
|
35
|
+
AND workspace_name = btrim(workspace_name)
|
|
36
|
+
AND char_length(workspace_name) BETWEEN 1 AND 120
|
|
37
|
+
AND octet_length(convert_to(workspace_name, 'UTF8')) BETWEEN 1 AND 480
|
|
38
|
+
),
|
|
39
|
+
CONSTRAINT additional_organization_creation_distinct_workspaces_check CHECK (
|
|
40
|
+
personal_workspace_id <> shared_workspace_id
|
|
41
|
+
),
|
|
42
|
+
CONSTRAINT additional_organization_creation_result_check CHECK (
|
|
43
|
+
jsonb_typeof(result) = 'object'
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
CREATE INDEX additional_organization_creation_receipts_actor_subject_idx
|
|
47
|
+
ON additional_organization_creation_receipts (actor_subject_id);
|
|
48
|
+
ALTER TABLE additional_organization_creation_receipts ENABLE ROW LEVEL SECURITY;
|
|
49
|
+
ALTER TABLE additional_organization_creation_receipts FORCE ROW LEVEL SECURITY;
|
|
50
|
+
REVOKE ALL ON TABLE additional_organization_creation_receipts FROM PUBLIC;
|
|
51
|
+
CREATE POLICY additional_organization_creation_lifecycle
|
|
52
|
+
ON additional_organization_creation_receipts
|
|
53
|
+
USING (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
54
|
+
= 'additional_organization_creation')
|
|
55
|
+
WITH CHECK (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
56
|
+
= 'additional_organization_creation');
|
|
57
|
+
|
|
58
|
+
CREATE TRIGGER additional_organization_creation_receipts_immutable
|
|
59
|
+
BEFORE UPDATE OR DELETE ON additional_organization_creation_receipts
|
|
60
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.organization_membership_history_immutable();
|
|
61
|
+
|
|
62
|
+
CREATE TABLE session_tenancy_additional_organization_activation_evidence (
|
|
63
|
+
account_id uuid PRIMARY KEY
|
|
64
|
+
REFERENCES session_tenancy_activations(account_id) ON DELETE RESTRICT,
|
|
65
|
+
eligibility_version integer NOT NULL,
|
|
66
|
+
creation_operation_id uuid NOT NULL UNIQUE
|
|
67
|
+
REFERENCES additional_organization_creation_receipts(operation_id) ON DELETE RESTRICT,
|
|
68
|
+
actor_subject_id text NOT NULL,
|
|
69
|
+
personal_workspace_id uuid NOT NULL UNIQUE,
|
|
70
|
+
shared_workspace_id uuid NOT NULL UNIQUE,
|
|
71
|
+
organization_membership_id uuid NOT NULL UNIQUE,
|
|
72
|
+
boundary_witness_account_id uuid NOT NULL
|
|
73
|
+
REFERENCES session_tenancy_activations(account_id) ON DELETE RESTRICT,
|
|
74
|
+
boundary_witness_activated_at timestamptz NOT NULL,
|
|
75
|
+
graph_evidence jsonb NOT NULL,
|
|
76
|
+
graph_digest text NOT NULL,
|
|
77
|
+
activation_parity_digest text NOT NULL,
|
|
78
|
+
private_setting_version bigint NOT NULL,
|
|
79
|
+
private_setting_event_id uuid NOT NULL UNIQUE
|
|
80
|
+
REFERENCES organization_private_session_setting_events(id) ON DELETE RESTRICT,
|
|
81
|
+
activated_at timestamptz NOT NULL,
|
|
82
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
83
|
+
CONSTRAINT session_tenancy_additional_organization_eligibility_check CHECK (
|
|
84
|
+
eligibility_version = 1
|
|
85
|
+
),
|
|
86
|
+
CONSTRAINT session_tenancy_additional_organization_actor_check CHECK (
|
|
87
|
+
actor_subject_id LIKE 'user:%'
|
|
88
|
+
),
|
|
89
|
+
CONSTRAINT session_tenancy_additional_organization_workspaces_check CHECK (
|
|
90
|
+
personal_workspace_id <> shared_workspace_id
|
|
91
|
+
),
|
|
92
|
+
CONSTRAINT session_tenancy_additional_organization_digests_check CHECK (
|
|
93
|
+
graph_digest ~ '^[0-9a-f]{64}$'
|
|
94
|
+
AND activation_parity_digest ~ '^[0-9a-f]{64}$'
|
|
95
|
+
),
|
|
96
|
+
CONSTRAINT session_tenancy_additional_organization_witness_check CHECK (
|
|
97
|
+
boundary_witness_account_id <> account_id
|
|
98
|
+
),
|
|
99
|
+
CONSTRAINT session_tenancy_additional_organization_graph_check CHECK (
|
|
100
|
+
jsonb_typeof(graph_evidence) = 'object'
|
|
101
|
+
),
|
|
102
|
+
CONSTRAINT session_tenancy_additional_organization_setting_version_check CHECK (
|
|
103
|
+
private_setting_version > 0
|
|
104
|
+
),
|
|
105
|
+
CONSTRAINT session_tenancy_additional_organization_personal_workspace_fk
|
|
106
|
+
FOREIGN KEY (personal_workspace_id)
|
|
107
|
+
REFERENCES additional_organization_creation_receipts(personal_workspace_id)
|
|
108
|
+
ON DELETE RESTRICT,
|
|
109
|
+
CONSTRAINT session_tenancy_additional_organization_shared_workspace_fk
|
|
110
|
+
FOREIGN KEY (shared_workspace_id)
|
|
111
|
+
REFERENCES additional_organization_creation_receipts(shared_workspace_id)
|
|
112
|
+
ON DELETE RESTRICT,
|
|
113
|
+
CONSTRAINT session_tenancy_additional_organization_membership_fk
|
|
114
|
+
FOREIGN KEY (organization_membership_id)
|
|
115
|
+
REFERENCES additional_organization_creation_receipts(organization_membership_id)
|
|
116
|
+
ON DELETE RESTRICT
|
|
117
|
+
);
|
|
118
|
+
ALTER TABLE session_tenancy_additional_organization_activation_evidence
|
|
119
|
+
ENABLE ROW LEVEL SECURITY;
|
|
120
|
+
ALTER TABLE session_tenancy_additional_organization_activation_evidence
|
|
121
|
+
FORCE ROW LEVEL SECURITY;
|
|
122
|
+
REVOKE ALL ON TABLE session_tenancy_additional_organization_activation_evidence FROM PUBLIC;
|
|
123
|
+
CREATE POLICY session_tenancy_additional_organization_activation_lifecycle
|
|
124
|
+
ON session_tenancy_additional_organization_activation_evidence
|
|
125
|
+
USING (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
126
|
+
= 'session_tenancy_greenfield_activation')
|
|
127
|
+
WITH CHECK (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
128
|
+
= 'session_tenancy_greenfield_activation');
|
|
129
|
+
|
|
130
|
+
CREATE FUNCTION activate_session_tenancy_from_additional_organization(
|
|
131
|
+
p_operation_id uuid
|
|
132
|
+
) RETURNS boolean
|
|
133
|
+
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
|
|
134
|
+
SET search_path FROM CURRENT
|
|
135
|
+
AS $activation$
|
|
136
|
+
DECLARE
|
|
137
|
+
previous_lifecycle text := pg_catalog.current_setting(
|
|
138
|
+
'opengeni.organization_tenancy_lifecycle', true
|
|
139
|
+
);
|
|
140
|
+
creation additional_organization_creation_receipts%ROWTYPE;
|
|
141
|
+
account_row managed_accounts%ROWTYPE;
|
|
142
|
+
organization_membership organization_memberships%ROWTYPE;
|
|
143
|
+
personal_workspace workspaces%ROWTYPE;
|
|
144
|
+
shared_workspace workspaces%ROWTYPE;
|
|
145
|
+
shared_access workspace_memberships%ROWTYPE;
|
|
146
|
+
witness session_tenancy_activations%ROWTYPE;
|
|
147
|
+
inserted_activation session_tenancy_activations%ROWTYPE;
|
|
148
|
+
existing_evidence session_tenancy_additional_organization_activation_evidence%ROWTYPE;
|
|
149
|
+
account_inserted_in_current_transaction boolean;
|
|
150
|
+
account_never_updated boolean;
|
|
151
|
+
organization_membership_count integer;
|
|
152
|
+
workspace_count integer;
|
|
153
|
+
workspace_control_count integer;
|
|
154
|
+
personal_workspace_control_count integer;
|
|
155
|
+
shared_workspace_control_count integer;
|
|
156
|
+
workspace_membership_count integer;
|
|
157
|
+
graph_evidence_value jsonb;
|
|
158
|
+
graph_digest_value text;
|
|
159
|
+
parity_digest_value text;
|
|
160
|
+
private_setting_event_id_value uuid := pg_catalog.gen_random_uuid();
|
|
161
|
+
BEGIN
|
|
162
|
+
IF p_operation_id IS NULL THEN
|
|
163
|
+
RAISE EXCEPTION 'additional organization activation operation is invalid'
|
|
164
|
+
USING ERRCODE = '22023';
|
|
165
|
+
END IF;
|
|
166
|
+
|
|
167
|
+
SELECT * INTO creation
|
|
168
|
+
FROM additional_organization_creation_receipts receipt
|
|
169
|
+
WHERE receipt.operation_id = p_operation_id
|
|
170
|
+
FOR KEY SHARE;
|
|
171
|
+
IF NOT FOUND
|
|
172
|
+
OR creation.actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
173
|
+
OR creation.account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
174
|
+
OR creation.result ->> 'organizationId' IS DISTINCT FROM creation.account_id::text
|
|
175
|
+
OR creation.result ->> 'personalWorkspaceId'
|
|
176
|
+
IS DISTINCT FROM creation.personal_workspace_id::text
|
|
177
|
+
OR creation.result ->> 'workspaceId' IS DISTINCT FROM creation.shared_workspace_id::text
|
|
178
|
+
THEN
|
|
179
|
+
RAISE EXCEPTION 'additional organization activation receipt is invalid'
|
|
180
|
+
USING ERRCODE = '55000';
|
|
181
|
+
END IF;
|
|
182
|
+
|
|
183
|
+
PERFORM pg_catalog.set_config(
|
|
184
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
185
|
+
'organization_membership_lifecycle', true
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
SELECT * INTO account_row
|
|
189
|
+
FROM managed_accounts account
|
|
190
|
+
WHERE account.id = creation.account_id
|
|
191
|
+
FOR KEY SHARE;
|
|
192
|
+
IF NOT FOUND
|
|
193
|
+
OR account_row.external_source IS DISTINCT FROM 'opengeni:additional-organization'
|
|
194
|
+
OR account_row.external_id IS DISTINCT FROM creation.operation_id::text
|
|
195
|
+
OR account_row.name IS DISTINCT FROM creation.organization_name
|
|
196
|
+
THEN
|
|
197
|
+
RAISE EXCEPTION 'additional organization activation requires its exact new account'
|
|
198
|
+
USING ERRCODE = '55000';
|
|
199
|
+
END IF;
|
|
200
|
+
SELECT
|
|
201
|
+
account.xmin::text::bigint = pg_catalog.pg_current_xact_id()::text::bigint,
|
|
202
|
+
account.created_at = account.updated_at
|
|
203
|
+
INTO account_inserted_in_current_transaction, account_never_updated
|
|
204
|
+
FROM managed_accounts account
|
|
205
|
+
WHERE account.id = creation.account_id;
|
|
206
|
+
IF account_inserted_in_current_transaction IS NOT TRUE
|
|
207
|
+
OR account_never_updated IS NOT TRUE
|
|
208
|
+
THEN
|
|
209
|
+
RAISE EXCEPTION 'additional organization activation requires a newly inserted account'
|
|
210
|
+
USING ERRCODE = '55000';
|
|
211
|
+
END IF;
|
|
212
|
+
|
|
213
|
+
SELECT * INTO organization_membership
|
|
214
|
+
FROM organization_memberships membership
|
|
215
|
+
WHERE membership.id = creation.organization_membership_id
|
|
216
|
+
AND membership.account_id = creation.account_id
|
|
217
|
+
FOR KEY SHARE;
|
|
218
|
+
SELECT count(*)::integer INTO organization_membership_count
|
|
219
|
+
FROM organization_memberships membership
|
|
220
|
+
WHERE membership.account_id = creation.account_id;
|
|
221
|
+
IF organization_membership.id IS NULL
|
|
222
|
+
OR organization_membership_count <> 1
|
|
223
|
+
OR organization_membership.subject_id IS DISTINCT FROM creation.actor_subject_id
|
|
224
|
+
OR organization_membership.role IS DISTINCT FROM 'owner'
|
|
225
|
+
OR organization_membership.status IS DISTINCT FROM 'active'
|
|
226
|
+
OR organization_membership.personal_workspace_id
|
|
227
|
+
IS DISTINCT FROM creation.personal_workspace_id
|
|
228
|
+
THEN
|
|
229
|
+
RAISE EXCEPTION 'additional organization membership graph is invalid'
|
|
230
|
+
USING ERRCODE = '55000';
|
|
231
|
+
END IF;
|
|
232
|
+
|
|
233
|
+
PERFORM pg_catalog.set_config(
|
|
234
|
+
'opengeni.workspace_id', creation.personal_workspace_id::text, true
|
|
235
|
+
);
|
|
236
|
+
SELECT * INTO personal_workspace
|
|
237
|
+
FROM workspaces workspace
|
|
238
|
+
WHERE workspace.id = creation.personal_workspace_id
|
|
239
|
+
AND workspace.account_id = creation.account_id
|
|
240
|
+
FOR KEY SHARE;
|
|
241
|
+
SELECT count(*)::integer INTO personal_workspace_control_count
|
|
242
|
+
FROM workspace_inference_controls control
|
|
243
|
+
WHERE control.account_id = creation.account_id
|
|
244
|
+
AND control.workspace_id = creation.personal_workspace_id;
|
|
245
|
+
|
|
246
|
+
PERFORM pg_catalog.set_config(
|
|
247
|
+
'opengeni.workspace_id', creation.shared_workspace_id::text, true
|
|
248
|
+
);
|
|
249
|
+
SELECT * INTO shared_workspace
|
|
250
|
+
FROM workspaces workspace
|
|
251
|
+
WHERE workspace.id = creation.shared_workspace_id
|
|
252
|
+
AND workspace.account_id = creation.account_id
|
|
253
|
+
FOR KEY SHARE;
|
|
254
|
+
SELECT count(*)::integer INTO workspace_count
|
|
255
|
+
FROM workspaces workspace WHERE workspace.account_id = creation.account_id;
|
|
256
|
+
SELECT count(*)::integer INTO shared_workspace_control_count
|
|
257
|
+
FROM workspace_inference_controls control
|
|
258
|
+
WHERE control.account_id = creation.account_id
|
|
259
|
+
AND control.workspace_id = creation.shared_workspace_id;
|
|
260
|
+
workspace_control_count :=
|
|
261
|
+
personal_workspace_control_count + shared_workspace_control_count;
|
|
262
|
+
SELECT count(*)::integer INTO workspace_membership_count
|
|
263
|
+
FROM workspace_memberships membership
|
|
264
|
+
WHERE membership.account_id = creation.account_id;
|
|
265
|
+
SELECT * INTO shared_access
|
|
266
|
+
FROM workspace_memberships membership
|
|
267
|
+
WHERE membership.account_id = creation.account_id
|
|
268
|
+
AND membership.workspace_id = creation.shared_workspace_id
|
|
269
|
+
AND membership.subject_id = creation.actor_subject_id
|
|
270
|
+
FOR KEY SHARE;
|
|
271
|
+
IF personal_workspace.id IS NULL
|
|
272
|
+
OR shared_workspace.id IS NULL
|
|
273
|
+
OR shared_access.id IS NULL
|
|
274
|
+
OR workspace_count <> 2
|
|
275
|
+
OR workspace_control_count <> 2
|
|
276
|
+
OR workspace_membership_count <> 1
|
|
277
|
+
OR personal_workspace.name IS DISTINCT FROM 'Personal workspace'
|
|
278
|
+
OR personal_workspace.slug IS NOT NULL
|
|
279
|
+
OR personal_workspace.external_source IS DISTINCT FROM
|
|
280
|
+
'opengeni:organization-membership'
|
|
281
|
+
OR personal_workspace.external_id IS DISTINCT FROM
|
|
282
|
+
creation.account_id::text || ':' || creation.actor_subject_id
|
|
283
|
+
OR shared_workspace.name IS DISTINCT FROM creation.workspace_name
|
|
284
|
+
OR shared_workspace.slug IS NOT NULL
|
|
285
|
+
OR shared_workspace.external_source IS DISTINCT FROM
|
|
286
|
+
'opengeni:additional-organization-default'
|
|
287
|
+
OR shared_workspace.external_id IS DISTINCT FROM creation.account_id::text
|
|
288
|
+
OR shared_access.role IS DISTINCT FROM 'admin'
|
|
289
|
+
OR shared_access.permissions IS DISTINCT FROM
|
|
290
|
+
opengeni_private.workspace_member_role_permissions('admin')
|
|
291
|
+
THEN
|
|
292
|
+
RAISE EXCEPTION 'additional organization workspace graph is invalid'
|
|
293
|
+
USING ERRCODE = '55000';
|
|
294
|
+
END IF;
|
|
295
|
+
|
|
296
|
+
graph_evidence_value := pg_catalog.jsonb_build_object(
|
|
297
|
+
'schemaVersion', 1,
|
|
298
|
+
'kind', 'additional_organization_with_first_shared_workspace',
|
|
299
|
+
'accountId', creation.account_id,
|
|
300
|
+
'operationId', creation.operation_id,
|
|
301
|
+
'actorSubjectId', creation.actor_subject_id,
|
|
302
|
+
'organizationMembershipId', creation.organization_membership_id,
|
|
303
|
+
'personalWorkspaceId', creation.personal_workspace_id,
|
|
304
|
+
'sharedWorkspaceId', creation.shared_workspace_id,
|
|
305
|
+
'organizationMembershipCount', organization_membership_count,
|
|
306
|
+
'workspaceCount', workspace_count,
|
|
307
|
+
'workspaceControlCount', workspace_control_count,
|
|
308
|
+
'workspaceMembershipCount', workspace_membership_count
|
|
309
|
+
);
|
|
310
|
+
graph_digest_value := pg_catalog.encode(pg_catalog.sha256(pg_catalog.convert_to(
|
|
311
|
+
graph_evidence_value::text, 'UTF8'
|
|
312
|
+
)), 'hex');
|
|
313
|
+
|
|
314
|
+
-- The exact graph is locked before the same deployment-wide fence used by
|
|
315
|
+
-- operator and first-signup activation. A creation that wins the fence stays
|
|
316
|
+
-- operator-managed; one after the first product witness activates atomically.
|
|
317
|
+
PERFORM lock_session_tenancy_activation_boundary();
|
|
318
|
+
PERFORM pg_catalog.set_config(
|
|
319
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
320
|
+
'session_tenancy_greenfield_activation', true
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
SELECT * INTO existing_evidence
|
|
324
|
+
FROM session_tenancy_additional_organization_activation_evidence evidence
|
|
325
|
+
WHERE evidence.account_id = creation.account_id;
|
|
326
|
+
IF FOUND THEN
|
|
327
|
+
IF existing_evidence.creation_operation_id IS DISTINCT FROM creation.operation_id
|
|
328
|
+
OR existing_evidence.actor_subject_id IS DISTINCT FROM creation.actor_subject_id
|
|
329
|
+
OR existing_evidence.personal_workspace_id IS DISTINCT FROM creation.personal_workspace_id
|
|
330
|
+
OR existing_evidence.shared_workspace_id IS DISTINCT FROM creation.shared_workspace_id
|
|
331
|
+
OR existing_evidence.organization_membership_id
|
|
332
|
+
IS DISTINCT FROM creation.organization_membership_id
|
|
333
|
+
OR existing_evidence.graph_digest IS DISTINCT FROM graph_digest_value
|
|
334
|
+
THEN
|
|
335
|
+
RAISE EXCEPTION 'additional organization activation evidence conflicts with creation'
|
|
336
|
+
USING ERRCODE = '23505';
|
|
337
|
+
END IF;
|
|
338
|
+
PERFORM pg_catalog.set_config(
|
|
339
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
340
|
+
);
|
|
341
|
+
RETURN true;
|
|
342
|
+
END IF;
|
|
343
|
+
|
|
344
|
+
SELECT * INTO witness
|
|
345
|
+
FROM session_tenancy_activations activation
|
|
346
|
+
WHERE activation.account_id <> creation.account_id
|
|
347
|
+
ORDER BY activation.activated_at, activation.account_id
|
|
348
|
+
LIMIT 1;
|
|
349
|
+
IF NOT FOUND THEN
|
|
350
|
+
PERFORM pg_catalog.set_config(
|
|
351
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
352
|
+
);
|
|
353
|
+
RETURN false;
|
|
354
|
+
END IF;
|
|
355
|
+
|
|
356
|
+
IF EXISTS (
|
|
357
|
+
SELECT 1 FROM session_tenancy_activations activation
|
|
358
|
+
WHERE activation.account_id = creation.account_id
|
|
359
|
+
) OR EXISTS (
|
|
360
|
+
SELECT 1 FROM organization_private_session_settings setting
|
|
361
|
+
WHERE setting.account_id = creation.account_id
|
|
362
|
+
) THEN
|
|
363
|
+
RAISE EXCEPTION 'additional organization session tenancy already exists without evidence'
|
|
364
|
+
USING ERRCODE = '55000';
|
|
365
|
+
END IF;
|
|
366
|
+
|
|
367
|
+
parity_digest_value := pg_catalog.encode(pg_catalog.sha256(pg_catalog.convert_to(
|
|
368
|
+
pg_catalog.jsonb_build_object(
|
|
369
|
+
'schemaVersion', 1,
|
|
370
|
+
'graphDigest', graph_digest_value,
|
|
371
|
+
'boundaryWitnessAccountId', witness.account_id,
|
|
372
|
+
'boundaryWitnessActivatedAt', witness.activated_at
|
|
373
|
+
)::text,
|
|
374
|
+
'UTF8'
|
|
375
|
+
)), 'hex');
|
|
376
|
+
|
|
377
|
+
PERFORM pg_catalog.set_config(
|
|
378
|
+
'opengeni.organization_tenancy_lifecycle', 'session_tenancy_activation', true
|
|
379
|
+
);
|
|
380
|
+
INSERT INTO session_tenancy_activations (
|
|
381
|
+
account_id, activation_version, inventory_digest, parity_digest,
|
|
382
|
+
activated_by, backfill_receipt_ids
|
|
383
|
+
) VALUES (
|
|
384
|
+
creation.account_id, 1, graph_digest_value, parity_digest_value,
|
|
385
|
+
'opengeni:additional-organization-creation:v1', ARRAY[]::uuid[]
|
|
386
|
+
) RETURNING * INTO inserted_activation;
|
|
387
|
+
|
|
388
|
+
PERFORM pg_catalog.set_config(
|
|
389
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
390
|
+
'organization_private_session_settings', true
|
|
391
|
+
);
|
|
392
|
+
INSERT INTO organization_private_session_settings (
|
|
393
|
+
account_id, enabled, version, updated_by_membership_id, updated_at
|
|
394
|
+
) VALUES (
|
|
395
|
+
creation.account_id, true, 1, creation.organization_membership_id,
|
|
396
|
+
inserted_activation.activated_at
|
|
397
|
+
);
|
|
398
|
+
INSERT INTO organization_private_session_setting_events (
|
|
399
|
+
id, account_id, actor_membership_id, requested_enabled,
|
|
400
|
+
expected_version, result_enabled, result_version, result_updated_at, changed
|
|
401
|
+
) VALUES (
|
|
402
|
+
private_setting_event_id_value, creation.account_id,
|
|
403
|
+
creation.organization_membership_id, true, 0, true, 1,
|
|
404
|
+
inserted_activation.activated_at, true
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
PERFORM pg_catalog.set_config(
|
|
408
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
409
|
+
'session_tenancy_greenfield_activation', true
|
|
410
|
+
);
|
|
411
|
+
INSERT INTO session_tenancy_additional_organization_activation_evidence (
|
|
412
|
+
account_id, eligibility_version, creation_operation_id, actor_subject_id,
|
|
413
|
+
personal_workspace_id, shared_workspace_id, organization_membership_id,
|
|
414
|
+
boundary_witness_account_id, boundary_witness_activated_at,
|
|
415
|
+
graph_evidence, graph_digest, activation_parity_digest,
|
|
416
|
+
private_setting_version, private_setting_event_id, activated_at
|
|
417
|
+
) VALUES (
|
|
418
|
+
creation.account_id, 1, creation.operation_id, creation.actor_subject_id,
|
|
419
|
+
creation.personal_workspace_id, creation.shared_workspace_id,
|
|
420
|
+
creation.organization_membership_id, witness.account_id, witness.activated_at,
|
|
421
|
+
graph_evidence_value, graph_digest_value, parity_digest_value,
|
|
422
|
+
1, private_setting_event_id_value, inserted_activation.activated_at
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
PERFORM pg_catalog.set_config(
|
|
426
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
427
|
+
);
|
|
428
|
+
RETURN true;
|
|
429
|
+
EXCEPTION WHEN OTHERS THEN
|
|
430
|
+
PERFORM pg_catalog.set_config(
|
|
431
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
432
|
+
);
|
|
433
|
+
RAISE;
|
|
434
|
+
END
|
|
435
|
+
$activation$;
|
|
436
|
+
REVOKE ALL ON FUNCTION activate_session_tenancy_from_additional_organization(uuid)
|
|
437
|
+
FROM PUBLIC;
|
|
438
|
+
|
|
439
|
+
CREATE FUNCTION create_additional_managed_organization(
|
|
440
|
+
p_subject_id text,
|
|
441
|
+
p_subject_label text,
|
|
442
|
+
p_organization_name text,
|
|
443
|
+
p_workspace_name text,
|
|
444
|
+
p_operation_id uuid
|
|
445
|
+
) RETURNS jsonb
|
|
446
|
+
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
|
|
447
|
+
SET search_path FROM CURRENT
|
|
448
|
+
AS $creation$
|
|
449
|
+
DECLARE
|
|
450
|
+
additional_organization_limit constant integer := 10;
|
|
451
|
+
auth_user_id_value text := substring(p_subject_id from length('user:') + 1);
|
|
452
|
+
organization_name_value text := nullif(pg_catalog.btrim(p_organization_name), '');
|
|
453
|
+
workspace_name_value text := nullif(pg_catalog.btrim(p_workspace_name), '');
|
|
454
|
+
subject_label_value text := nullif(pg_catalog.btrim(p_subject_label), '');
|
|
455
|
+
auth_user auth_users%ROWTYPE;
|
|
456
|
+
prior additional_organization_creation_receipts%ROWTYPE;
|
|
457
|
+
account_row managed_accounts%ROWTYPE;
|
|
458
|
+
organization_membership organization_memberships%ROWTYPE;
|
|
459
|
+
personal_workspace workspaces%ROWTYPE;
|
|
460
|
+
shared_workspace workspaces%ROWTYPE;
|
|
461
|
+
created_organization_count integer;
|
|
462
|
+
public_result jsonb;
|
|
463
|
+
BEGIN
|
|
464
|
+
IF p_subject_id IS NULL
|
|
465
|
+
OR p_subject_id NOT LIKE 'user:%'
|
|
466
|
+
OR p_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
467
|
+
OR pg_catalog.octet_length(pg_catalog.convert_to(p_subject_id, 'UTF8'))
|
|
468
|
+
NOT BETWEEN 6 AND 1024
|
|
469
|
+
OR organization_name_value IS NULL
|
|
470
|
+
OR pg_catalog.char_length(organization_name_value) > 120
|
|
471
|
+
OR pg_catalog.octet_length(pg_catalog.convert_to(organization_name_value, 'UTF8')) > 480
|
|
472
|
+
OR workspace_name_value IS NULL
|
|
473
|
+
OR pg_catalog.char_length(workspace_name_value) > 120
|
|
474
|
+
OR pg_catalog.octet_length(pg_catalog.convert_to(workspace_name_value, 'UTF8')) > 480
|
|
475
|
+
OR (subject_label_value IS NOT NULL AND
|
|
476
|
+
pg_catalog.octet_length(pg_catalog.convert_to(subject_label_value, 'UTF8')) > 1024)
|
|
477
|
+
OR p_operation_id IS NULL
|
|
478
|
+
THEN
|
|
479
|
+
RAISE EXCEPTION 'additional organization creation input is invalid'
|
|
480
|
+
USING ERRCODE = '22023';
|
|
481
|
+
END IF;
|
|
482
|
+
|
|
483
|
+
SELECT * INTO auth_user
|
|
484
|
+
FROM auth_users candidate
|
|
485
|
+
WHERE candidate.id = auth_user_id_value
|
|
486
|
+
FOR KEY SHARE;
|
|
487
|
+
IF NOT FOUND OR auth_user.email_verified IS NOT TRUE THEN
|
|
488
|
+
RAISE EXCEPTION 'verified managed user required' USING ERRCODE = '42501';
|
|
489
|
+
END IF;
|
|
490
|
+
|
|
491
|
+
PERFORM pg_catalog.set_config(
|
|
492
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
493
|
+
'organization_membership_lifecycle', true
|
|
494
|
+
);
|
|
495
|
+
IF NOT EXISTS (
|
|
496
|
+
SELECT 1 FROM organization_memberships membership
|
|
497
|
+
WHERE membership.subject_id = p_subject_id
|
|
498
|
+
AND membership.status = 'active'
|
|
499
|
+
) THEN
|
|
500
|
+
RAISE EXCEPTION 'initial organization setup must be completed first'
|
|
501
|
+
USING ERRCODE = '42501';
|
|
502
|
+
END IF;
|
|
503
|
+
|
|
504
|
+
PERFORM pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
|
|
505
|
+
'additional-organization-creation:' || p_operation_id::text, 0
|
|
506
|
+
));
|
|
507
|
+
PERFORM pg_catalog.set_config(
|
|
508
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
509
|
+
'additional_organization_creation', true
|
|
510
|
+
);
|
|
511
|
+
SELECT * INTO prior
|
|
512
|
+
FROM additional_organization_creation_receipts receipt
|
|
513
|
+
WHERE receipt.operation_id = p_operation_id
|
|
514
|
+
FOR UPDATE;
|
|
515
|
+
IF FOUND THEN
|
|
516
|
+
IF prior.actor_subject_id IS DISTINCT FROM p_subject_id
|
|
517
|
+
OR prior.organization_name IS DISTINCT FROM organization_name_value
|
|
518
|
+
OR prior.workspace_name IS DISTINCT FROM workspace_name_value
|
|
519
|
+
THEN
|
|
520
|
+
RAISE EXCEPTION 'additional organization operation key was reused'
|
|
521
|
+
USING ERRCODE = '23505';
|
|
522
|
+
END IF;
|
|
523
|
+
RETURN prior.result;
|
|
524
|
+
END IF;
|
|
525
|
+
|
|
526
|
+
-- Fresh operation ids must not turn one verified login into an unbounded
|
|
527
|
+
-- tenant allocator. Serialize every create for the canonical subject, then
|
|
528
|
+
-- count only organizations allocated through this lifecycle. Invitations do
|
|
529
|
+
-- not consume the self-service allowance, and an exact committed retry above
|
|
530
|
+
-- continues to replay after the allowance is full.
|
|
531
|
+
PERFORM pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(
|
|
532
|
+
'additional-organization-creation-subject:' || p_subject_id, 0
|
|
533
|
+
));
|
|
534
|
+
SELECT count(*)::integer INTO created_organization_count
|
|
535
|
+
FROM additional_organization_creation_receipts receipt
|
|
536
|
+
WHERE receipt.actor_subject_id = p_subject_id;
|
|
537
|
+
IF created_organization_count >= additional_organization_limit THEN
|
|
538
|
+
RAISE EXCEPTION 'additional organization limit reached'
|
|
539
|
+
USING ERRCODE = '54000';
|
|
540
|
+
END IF;
|
|
541
|
+
|
|
542
|
+
PERFORM pg_catalog.set_config(
|
|
543
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
544
|
+
'organization_membership_lifecycle', true
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
INSERT INTO managed_accounts (name, external_source, external_id)
|
|
548
|
+
VALUES (
|
|
549
|
+
organization_name_value,
|
|
550
|
+
'opengeni:additional-organization',
|
|
551
|
+
p_operation_id::text
|
|
552
|
+
) RETURNING * INTO account_row;
|
|
553
|
+
|
|
554
|
+
PERFORM pg_catalog.pg_advisory_xact_lock(
|
|
555
|
+
pg_catalog.hashtextextended(
|
|
556
|
+
'organization-membership:' || account_row.id::text, 0
|
|
557
|
+
)
|
|
558
|
+
);
|
|
559
|
+
PERFORM pg_catalog.set_config('opengeni.account_id', account_row.id::text, true);
|
|
560
|
+
|
|
561
|
+
personal_workspace.id := pg_catalog.gen_random_uuid();
|
|
562
|
+
PERFORM pg_catalog.set_config(
|
|
563
|
+
'opengeni.workspace_id', personal_workspace.id::text, true
|
|
564
|
+
);
|
|
565
|
+
INSERT INTO workspaces (
|
|
566
|
+
id, account_id, name, slug, external_source, external_id
|
|
567
|
+
) VALUES (
|
|
568
|
+
personal_workspace.id, account_row.id, 'Personal workspace', NULL,
|
|
569
|
+
'opengeni:organization-membership',
|
|
570
|
+
account_row.id::text || ':' || p_subject_id
|
|
571
|
+
) RETURNING * INTO personal_workspace;
|
|
572
|
+
INSERT INTO workspace_inference_controls (workspace_id, account_id)
|
|
573
|
+
VALUES (personal_workspace.id, account_row.id);
|
|
574
|
+
|
|
575
|
+
INSERT INTO organization_memberships (
|
|
576
|
+
account_id, subject_id, role, status, personal_workspace_id
|
|
577
|
+
) VALUES (
|
|
578
|
+
account_row.id, p_subject_id, 'owner', 'active', personal_workspace.id
|
|
579
|
+
) RETURNING * INTO organization_membership;
|
|
580
|
+
|
|
581
|
+
shared_workspace.id := pg_catalog.gen_random_uuid();
|
|
582
|
+
PERFORM pg_catalog.set_config(
|
|
583
|
+
'opengeni.workspace_id', shared_workspace.id::text, true
|
|
584
|
+
);
|
|
585
|
+
INSERT INTO workspaces (
|
|
586
|
+
id, account_id, name, slug, external_source, external_id
|
|
587
|
+
) VALUES (
|
|
588
|
+
shared_workspace.id, account_row.id, workspace_name_value, NULL,
|
|
589
|
+
'opengeni:additional-organization-default', account_row.id::text
|
|
590
|
+
) RETURNING * INTO shared_workspace;
|
|
591
|
+
INSERT INTO workspace_inference_controls (workspace_id, account_id)
|
|
592
|
+
VALUES (shared_workspace.id, account_row.id);
|
|
593
|
+
INSERT INTO workspace_memberships (
|
|
594
|
+
account_id, workspace_id, subject_id, subject_label, role, permissions
|
|
595
|
+
) VALUES (
|
|
596
|
+
account_row.id, shared_workspace.id, p_subject_id, subject_label_value,
|
|
597
|
+
'admin', opengeni_private.workspace_member_role_permissions('admin')
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
public_result := pg_catalog.jsonb_build_object(
|
|
601
|
+
'organizationId', account_row.id,
|
|
602
|
+
'organization', pg_catalog.jsonb_build_object(
|
|
603
|
+
'id', account_row.id,
|
|
604
|
+
'name', account_row.name,
|
|
605
|
+
'createdAt', account_row.created_at,
|
|
606
|
+
'updatedAt', account_row.updated_at
|
|
607
|
+
),
|
|
608
|
+
'workspaceId', shared_workspace.id,
|
|
609
|
+
'personalWorkspaceId', personal_workspace.id
|
|
610
|
+
);
|
|
611
|
+
PERFORM pg_catalog.set_config(
|
|
612
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
613
|
+
'additional_organization_creation', true
|
|
614
|
+
);
|
|
615
|
+
INSERT INTO additional_organization_creation_receipts (
|
|
616
|
+
operation_id, actor_subject_id, account_id, organization_membership_id,
|
|
617
|
+
personal_workspace_id, shared_workspace_id, organization_name,
|
|
618
|
+
workspace_name, result
|
|
619
|
+
) VALUES (
|
|
620
|
+
p_operation_id, p_subject_id, account_row.id, organization_membership.id,
|
|
621
|
+
personal_workspace.id, shared_workspace.id, organization_name_value,
|
|
622
|
+
workspace_name_value, public_result
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
PERFORM activate_session_tenancy_from_additional_organization(p_operation_id);
|
|
626
|
+
RETURN public_result;
|
|
627
|
+
END
|
|
628
|
+
$creation$;
|
|
629
|
+
|
|
630
|
+
REVOKE ALL ON FUNCTION create_additional_managed_organization(
|
|
631
|
+
text, text, text, text, uuid
|
|
632
|
+
) FROM PUBLIC;
|
|
633
|
+
|
|
634
|
+
DO $posture$
|
|
635
|
+
DECLARE
|
|
636
|
+
data_schema text := pg_catalog.current_schema();
|
|
637
|
+
BEGIN
|
|
638
|
+
EXECUTE pg_catalog.format(
|
|
639
|
+
'ALTER FUNCTION %I.activate_session_tenancy_from_additional_organization(uuid) '
|
|
640
|
+
|| 'SET search_path = pg_catalog, %I, opengeni_private, pg_temp',
|
|
641
|
+
data_schema, data_schema
|
|
642
|
+
);
|
|
643
|
+
EXECUTE pg_catalog.format(
|
|
644
|
+
'ALTER FUNCTION %I.create_additional_managed_organization(text,text,text,text,uuid) '
|
|
645
|
+
|| 'SET search_path = pg_catalog, %I, opengeni_private, pg_temp',
|
|
646
|
+
data_schema, data_schema
|
|
647
|
+
);
|
|
648
|
+
END
|
|
649
|
+
$posture$;
|
|
650
|
+
|
|
651
|
+
DO $application_grants$
|
|
652
|
+
DECLARE
|
|
653
|
+
data_schema text := pg_catalog.current_schema();
|
|
654
|
+
application_role text;
|
|
655
|
+
BEGIN
|
|
656
|
+
FOR application_role IN
|
|
657
|
+
SELECT role_value.rolname
|
|
658
|
+
FROM pg_catalog.jsonb_array_elements_text(
|
|
659
|
+
coalesce(nullif(current_setting('opengeni.migration_application_roles', true), ''), '[]')::jsonb
|
|
660
|
+
) configured(value)
|
|
661
|
+
JOIN pg_catalog.pg_roles role_value ON role_value.rolname = configured.value
|
|
662
|
+
UNION SELECT 'opengeni_app'
|
|
663
|
+
WHERE EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'opengeni_app')
|
|
664
|
+
LOOP
|
|
665
|
+
EXECUTE pg_catalog.format(
|
|
666
|
+
'GRANT EXECUTE ON FUNCTION %1$I.create_additional_managed_organization(text,text,text,text,uuid) TO %2$I',
|
|
667
|
+
data_schema, application_role
|
|
668
|
+
);
|
|
669
|
+
EXECUTE pg_catalog.format(
|
|
670
|
+
'REVOKE ALL ON TABLE %1$I.additional_organization_creation_receipts, %1$I.session_tenancy_additional_organization_activation_evidence FROM %2$I',
|
|
671
|
+
data_schema, application_role
|
|
672
|
+
);
|
|
673
|
+
EXECUTE pg_catalog.format(
|
|
674
|
+
'REVOKE ALL ON FUNCTION %1$I.activate_session_tenancy_from_additional_organization(uuid) FROM %2$I',
|
|
675
|
+
data_schema, application_role
|
|
676
|
+
);
|
|
677
|
+
END LOOP;
|
|
678
|
+
END
|
|
679
|
+
$application_grants$;
|
|
680
|
+
|
|
681
|
+
COMMENT ON TABLE additional_organization_creation_receipts IS
|
|
682
|
+
'Immutable exact-command receipts for additional organization creation by an already-onboarded managed human.';
|
|
683
|
+
COMMENT ON TABLE session_tenancy_additional_organization_activation_evidence IS
|
|
684
|
+
'Immutable graph, boundary-witness, private-setting, and activation evidence for a newly created additional organization.';
|
|
685
|
+
COMMENT ON FUNCTION create_additional_managed_organization(text, text, text, text, uuid) IS
|
|
686
|
+
'Creates an additional organization, Personal workspace, first shared workspace, owner membership, and creator access atomically.';
|
|
687
|
+
COMMENT ON FUNCTION activate_session_tenancy_from_additional_organization(uuid) IS
|
|
688
|
+
'Owner-only atomic activation for the exact fresh graph created by create_additional_managed_organization.';
|
|
689
|
+
|
|
690
|
+
RESET statement_timeout;
|
|
691
|
+
RESET lock_timeout;
|