@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,154 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Pausing or soft-deleting a scheduled task is a durable first-claim cutoff.
|
|
3
|
+
-- Runs whose scheduler-owned turn already exists remain accepted work and keep
|
|
4
|
+
-- their normal attempt/recovery lifecycle. Every earlier occurrence becomes a
|
|
5
|
+
-- terminal receipt, so a later Resume cannot revive a deposit accepted before
|
|
6
|
+
-- the lifecycle boundary.
|
|
7
|
+
|
|
8
|
+
SET LOCAL lock_timeout = '5s';
|
|
9
|
+
SET LOCAL statement_timeout = '5min';
|
|
10
|
+
|
|
11
|
+
-- Old claimers do not inspect scheduled_task_runs.status after this migration
|
|
12
|
+
-- marks an occurrence terminal. Keep the rolling boundary safe in the database:
|
|
13
|
+
-- every binary must cross this state transition before it can create the
|
|
14
|
+
-- scheduler-owned turn or start provider work.
|
|
15
|
+
CREATE FUNCTION fence_terminal_scheduled_occurrence_delivery()
|
|
16
|
+
RETURNS trigger
|
|
17
|
+
LANGUAGE plpgsql
|
|
18
|
+
SECURITY DEFINER
|
|
19
|
+
SET search_path FROM CURRENT
|
|
20
|
+
AS $body$
|
|
21
|
+
DECLARE
|
|
22
|
+
run_status text;
|
|
23
|
+
BEGIN
|
|
24
|
+
SELECT run.status INTO run_status
|
|
25
|
+
FROM scheduled_task_runs run
|
|
26
|
+
WHERE run.id = OLD.scheduled_task_run_id
|
|
27
|
+
AND run.account_id = OLD.account_id
|
|
28
|
+
AND run.workspace_id = OLD.workspace_id
|
|
29
|
+
AND run.session_id = OLD.session_id
|
|
30
|
+
AND run.action_kind = 'agent_turn'
|
|
31
|
+
FOR UPDATE;
|
|
32
|
+
|
|
33
|
+
IF run_status IS DISTINCT FROM 'dispatched' THEN
|
|
34
|
+
RAISE EXCEPTION 'terminal scheduled occurrence cannot be delivered'
|
|
35
|
+
USING ERRCODE = '42501';
|
|
36
|
+
END IF;
|
|
37
|
+
RETURN NEW;
|
|
38
|
+
END
|
|
39
|
+
$body$;
|
|
40
|
+
|
|
41
|
+
CREATE TRIGGER scheduled_terminal_occurrence_delivery_fence
|
|
42
|
+
BEFORE UPDATE OF state ON session_system_updates
|
|
43
|
+
FOR EACH ROW
|
|
44
|
+
WHEN (
|
|
45
|
+
OLD.scheduled_task_run_id IS NOT NULL
|
|
46
|
+
AND OLD.state = 'pending'
|
|
47
|
+
AND NEW.state = 'delivered'
|
|
48
|
+
)
|
|
49
|
+
EXECUTE FUNCTION fence_terminal_scheduled_occurrence_delivery();
|
|
50
|
+
|
|
51
|
+
CREATE FUNCTION invalidate_unclaimed_scheduled_agent_runs_on_task_inactive()
|
|
52
|
+
RETURNS trigger
|
|
53
|
+
LANGUAGE plpgsql
|
|
54
|
+
SECURITY DEFINER
|
|
55
|
+
SET search_path FROM CURRENT
|
|
56
|
+
AS $body$
|
|
57
|
+
DECLARE
|
|
58
|
+
run_row record;
|
|
59
|
+
invalidation_error text;
|
|
60
|
+
BEGIN
|
|
61
|
+
invalidation_error := CASE
|
|
62
|
+
-- 0275 introduced deleted_at. JSON row introspection keeps this rolling
|
|
63
|
+
-- trigger installable in legacy-upgrade fixtures that intentionally replay
|
|
64
|
+
-- 0275 after the current tail, while current schemas still classify delete.
|
|
65
|
+
WHEN pg_catalog.to_jsonb(OLD) ->> 'deleted_at' IS NULL
|
|
66
|
+
AND pg_catalog.to_jsonb(NEW) ->> 'deleted_at' IS NOT NULL
|
|
67
|
+
THEN 'scheduled_task_deleted_before_claim'
|
|
68
|
+
ELSE 'scheduled_task_paused_before_claim'
|
|
69
|
+
END;
|
|
70
|
+
|
|
71
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
72
|
+
backend_pid, transaction_id, capability_kind
|
|
73
|
+
) VALUES (
|
|
74
|
+
pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'run_lifecycle'
|
|
75
|
+
)
|
|
76
|
+
ON CONFLICT DO NOTHING;
|
|
77
|
+
|
|
78
|
+
-- Run admission takes the task row first. Deposit and claim both lock the run
|
|
79
|
+
-- before they can publish an occurrence or a scheduler-owned turn. Once this
|
|
80
|
+
-- loop owns a run, a fresh turn lookup therefore decides the race exactly:
|
|
81
|
+
-- no turn means no claim crossed the lifecycle boundary.
|
|
82
|
+
FOR run_row IN
|
|
83
|
+
SELECT run.id
|
|
84
|
+
FROM scheduled_task_runs run
|
|
85
|
+
WHERE run.account_id = NEW.account_id
|
|
86
|
+
AND run.workspace_id = NEW.workspace_id
|
|
87
|
+
AND run.task_id = NEW.id
|
|
88
|
+
AND run.action_kind = 'agent_turn'
|
|
89
|
+
AND run.status IN ('queued', 'dispatched')
|
|
90
|
+
ORDER BY run.id
|
|
91
|
+
FOR UPDATE
|
|
92
|
+
LOOP
|
|
93
|
+
IF NOT EXISTS (
|
|
94
|
+
SELECT 1
|
|
95
|
+
FROM session_turns turn_value
|
|
96
|
+
WHERE turn_value.account_id = NEW.account_id
|
|
97
|
+
AND turn_value.workspace_id = NEW.workspace_id
|
|
98
|
+
AND turn_value.scheduled_task_run_id = run_row.id
|
|
99
|
+
) THEN
|
|
100
|
+
UPDATE scheduled_task_runs run
|
|
101
|
+
SET status = 'skipped',
|
|
102
|
+
error = invalidation_error,
|
|
103
|
+
completed_at = pg_catalog.clock_timestamp(),
|
|
104
|
+
updated_at = pg_catalog.clock_timestamp()
|
|
105
|
+
WHERE run.id = run_row.id
|
|
106
|
+
AND run.account_id = NEW.account_id
|
|
107
|
+
AND run.workspace_id = NEW.workspace_id
|
|
108
|
+
AND run.status IN ('queued', 'dispatched');
|
|
109
|
+
END IF;
|
|
110
|
+
END LOOP;
|
|
111
|
+
|
|
112
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
113
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
114
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
115
|
+
AND capability_kind = 'run_lifecycle';
|
|
116
|
+
RETURN NEW;
|
|
117
|
+
EXCEPTION WHEN OTHERS THEN
|
|
118
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
119
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
120
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
121
|
+
AND capability_kind = 'run_lifecycle';
|
|
122
|
+
RAISE;
|
|
123
|
+
END
|
|
124
|
+
$body$;
|
|
125
|
+
|
|
126
|
+
CREATE TRIGGER scheduled_task_unclaimed_occurrence_invalidation
|
|
127
|
+
AFTER UPDATE OF status ON scheduled_tasks
|
|
128
|
+
FOR EACH ROW
|
|
129
|
+
WHEN (OLD.status = 'active' AND NEW.status = 'paused')
|
|
130
|
+
EXECUTE FUNCTION invalidate_unclaimed_scheduled_agent_runs_on_task_inactive();
|
|
131
|
+
|
|
132
|
+
REVOKE ALL ON FUNCTION
|
|
133
|
+
invalidate_unclaimed_scheduled_agent_runs_on_task_inactive()
|
|
134
|
+
FROM PUBLIC;
|
|
135
|
+
REVOKE ALL ON FUNCTION fence_terminal_scheduled_occurrence_delivery() FROM PUBLIC;
|
|
136
|
+
|
|
137
|
+
DO $pin_scheduled_task_unclaimed_invalidation$
|
|
138
|
+
DECLARE
|
|
139
|
+
data_schema text := pg_catalog.current_schema();
|
|
140
|
+
BEGIN
|
|
141
|
+
EXECUTE pg_catalog.format(
|
|
142
|
+
'ALTER FUNCTION %I.invalidate_unclaimed_scheduled_agent_runs_on_task_inactive() '
|
|
143
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
144
|
+
data_schema,
|
|
145
|
+
data_schema
|
|
146
|
+
);
|
|
147
|
+
EXECUTE pg_catalog.format(
|
|
148
|
+
'ALTER FUNCTION %I.fence_terminal_scheduled_occurrence_delivery() '
|
|
149
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
150
|
+
data_schema,
|
|
151
|
+
data_schema
|
|
152
|
+
);
|
|
153
|
+
END
|
|
154
|
+
$pin_scheduled_task_unclaimed_invalidation$;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Preserve the hypothetical OpenGeni credit price separately from both the
|
|
3
|
+
-- upstream provider estimate and the amount actually priced to credits.
|
|
4
|
+
-- Historical external facts remain unknown rather than being recomputed with
|
|
5
|
+
-- today's margin or provider rates.
|
|
6
|
+
|
|
7
|
+
SET LOCAL lock_timeout = '5s';
|
|
8
|
+
SET LOCAL statement_timeout = '5min';
|
|
9
|
+
|
|
10
|
+
ALTER TABLE "model_call_facts"
|
|
11
|
+
ADD COLUMN IF NOT EXISTS "equivalent_credit_cost_micros" bigint;
|
|
12
|
+
|
|
13
|
+
ALTER TABLE "model_call_facts"
|
|
14
|
+
DROP CONSTRAINT IF EXISTS "model_call_facts_equivalent_credit_cost_check",
|
|
15
|
+
ADD CONSTRAINT "model_call_facts_equivalent_credit_cost_check"
|
|
16
|
+
CHECK (
|
|
17
|
+
"equivalent_credit_cost_micros" IS NULL
|
|
18
|
+
OR (
|
|
19
|
+
"equivalent_credit_cost_micros" >= 0
|
|
20
|
+
AND "estimated_provider_cost_micros" IS NOT NULL
|
|
21
|
+
)
|
|
22
|
+
) NOT VALID;
|
|
23
|
+
|
|
24
|
+
ALTER TABLE "model_call_facts"
|
|
25
|
+
VALIDATE CONSTRAINT "model_call_facts_equivalent_credit_cost_check";
|
|
26
|
+
|
|
27
|
+
RESET statement_timeout;
|
|
28
|
+
RESET lock_timeout;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- A finite provider lifetime is a hard destruction boundary. Once the normal
|
|
3
|
+
-- lead-time rotation request is durable, persistent Browser/Computer placement
|
|
4
|
+
-- must yield while enough time remains to quiesce writers and capture the exact
|
|
5
|
+
-- workspace generation. Waiting until the provider deadline keeps the lease
|
|
6
|
+
-- holder alive until the same instant the provider may destroy the box.
|
|
7
|
+
SET LOCAL lock_timeout = '5s';
|
|
8
|
+
SET LOCAL statement_timeout = '10min';
|
|
9
|
+
|
|
10
|
+
DO $sandbox_deadline_rotation_preemption$
|
|
11
|
+
DECLARE
|
|
12
|
+
definition text;
|
|
13
|
+
patched text;
|
|
14
|
+
eligibility_anchor constant text :=
|
|
15
|
+
E' AND lease.provider_deadline_at <= pg_catalog.now()';
|
|
16
|
+
eligibility_replacement constant text :=
|
|
17
|
+
E' -- 0397 deadline rotation preemption: an automatic finite-lifetime\n'
|
|
18
|
+
|| E' -- rotation yields persistent controllers at the lead boundary;\n'
|
|
19
|
+
|| E' -- unrelated operator rotations retain the hard-deadline fallback.\n'
|
|
20
|
+
|| E' AND (\n'
|
|
21
|
+
|| E' lease.rotation_reason = ''provider_deadline''\n'
|
|
22
|
+
|| E' OR lease.provider_deadline_at <= pg_catalog.now()\n'
|
|
23
|
+
|| E' )';
|
|
24
|
+
message_anchor constant text :=
|
|
25
|
+
E' error_message = ''Interaction controller reached its sandbox provider deadline'',';
|
|
26
|
+
message_replacement constant text :=
|
|
27
|
+
E' error_message = ''Interaction controller yielded for sandbox provider rotation'',';
|
|
28
|
+
occurrences integer;
|
|
29
|
+
BEGIN
|
|
30
|
+
SELECT pg_catalog.pg_get_functiondef(
|
|
31
|
+
'opengeni_private.reap_stale_interaction_transitions(bigint)'::regprocedure
|
|
32
|
+
) INTO definition;
|
|
33
|
+
|
|
34
|
+
IF definition IS NULL
|
|
35
|
+
OR pg_catalog.strpos(
|
|
36
|
+
definition,
|
|
37
|
+
'0391 provider-deadline interaction follow-up'
|
|
38
|
+
) = 0
|
|
39
|
+
OR pg_catalog.strpos(
|
|
40
|
+
definition,
|
|
41
|
+
'interaction_holder.kind = ''interaction'''
|
|
42
|
+
) = 0
|
|
43
|
+
OR pg_catalog.strpos(definition, 'provider_deadline_rotation') = 0
|
|
44
|
+
THEN
|
|
45
|
+
RAISE EXCEPTION '0397 interaction rotation prerequisite definition drift'
|
|
46
|
+
USING ERRCODE = '55000';
|
|
47
|
+
END IF;
|
|
48
|
+
|
|
49
|
+
IF pg_catalog.strpos(definition, '0397 deadline rotation preemption') > 0 THEN
|
|
50
|
+
IF pg_catalog.strpos(
|
|
51
|
+
definition,
|
|
52
|
+
'lease.rotation_reason = ''provider_deadline'''
|
|
53
|
+
) = 0
|
|
54
|
+
OR pg_catalog.strpos(
|
|
55
|
+
definition,
|
|
56
|
+
'Interaction controller yielded for sandbox provider rotation'
|
|
57
|
+
) = 0
|
|
58
|
+
THEN
|
|
59
|
+
RAISE EXCEPTION '0397 interaction rotation replay definition drift'
|
|
60
|
+
USING ERRCODE = '55000';
|
|
61
|
+
END IF;
|
|
62
|
+
ELSE
|
|
63
|
+
patched := definition;
|
|
64
|
+
|
|
65
|
+
occurrences := (
|
|
66
|
+
pg_catalog.length(patched)
|
|
67
|
+
- pg_catalog.length(pg_catalog.replace(patched, eligibility_anchor, ''))
|
|
68
|
+
) / pg_catalog.length(eligibility_anchor);
|
|
69
|
+
IF occurrences <> 1 THEN
|
|
70
|
+
RAISE EXCEPTION '0397 interaction rotation eligibility anchor drift'
|
|
71
|
+
USING ERRCODE = '55000';
|
|
72
|
+
END IF;
|
|
73
|
+
patched := pg_catalog.replace(
|
|
74
|
+
patched,
|
|
75
|
+
eligibility_anchor,
|
|
76
|
+
eligibility_replacement
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
occurrences := (
|
|
80
|
+
pg_catalog.length(patched)
|
|
81
|
+
- pg_catalog.length(pg_catalog.replace(patched, message_anchor, ''))
|
|
82
|
+
) / pg_catalog.length(message_anchor);
|
|
83
|
+
IF occurrences <> 1 THEN
|
|
84
|
+
RAISE EXCEPTION '0397 interaction rotation message anchor drift'
|
|
85
|
+
USING ERRCODE = '55000';
|
|
86
|
+
END IF;
|
|
87
|
+
patched := pg_catalog.replace(
|
|
88
|
+
patched,
|
|
89
|
+
message_anchor,
|
|
90
|
+
message_replacement
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
IF pg_catalog.strpos(patched, '0397 deadline rotation preemption') = 0
|
|
94
|
+
OR pg_catalog.strpos(
|
|
95
|
+
patched,
|
|
96
|
+
'lease.rotation_reason = ''provider_deadline'''
|
|
97
|
+
) = 0
|
|
98
|
+
OR pg_catalog.strpos(
|
|
99
|
+
patched,
|
|
100
|
+
'OR lease.provider_deadline_at <= pg_catalog.now()'
|
|
101
|
+
) = 0
|
|
102
|
+
THEN
|
|
103
|
+
RAISE EXCEPTION '0397 interaction rotation patch failed'
|
|
104
|
+
USING ERRCODE = '55000';
|
|
105
|
+
END IF;
|
|
106
|
+
|
|
107
|
+
EXECUTE patched;
|
|
108
|
+
END IF;
|
|
109
|
+
END
|
|
110
|
+
$sandbox_deadline_rotation_preemption$;
|
|
111
|
+
|
|
112
|
+
RESET statement_timeout;
|
|
113
|
+
RESET lock_timeout;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Shared-workspace creators receive explicit operational administration while
|
|
3
|
+
-- organization administrators retain a separate, content-blind lifecycle
|
|
4
|
+
-- authority for the workspace management surface.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
ALTER FUNCTION organization_workspace_command(jsonb)
|
|
10
|
+
RENAME TO organization_workspace_command_without_creator_access;
|
|
11
|
+
|
|
12
|
+
REVOKE ALL ON FUNCTION organization_workspace_command_without_creator_access(jsonb) FROM PUBLIC;
|
|
13
|
+
|
|
14
|
+
CREATE FUNCTION organization_workspace_command(p_command jsonb)
|
|
15
|
+
RETURNS jsonb
|
|
16
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
17
|
+
SET search_path FROM CURRENT
|
|
18
|
+
AS $body$
|
|
19
|
+
DECLARE
|
|
20
|
+
result jsonb;
|
|
21
|
+
actor_membership_id uuid;
|
|
22
|
+
creator_grant_operation_id uuid;
|
|
23
|
+
creator_grant_operation_hash text;
|
|
24
|
+
BEGIN
|
|
25
|
+
result := organization_workspace_command_without_creator_access(p_command);
|
|
26
|
+
IF p_command ->> 'action' IS DISTINCT FROM 'create' THEN
|
|
27
|
+
RETURN result;
|
|
28
|
+
END IF;
|
|
29
|
+
-- A create receipt written before this migration has no child grant receipt.
|
|
30
|
+
-- Preserve that historical outcome instead of turning a retry into a new
|
|
31
|
+
-- privilege grant after rollout.
|
|
32
|
+
IF coalesce((result ->> 'replay')::boolean, false) THEN
|
|
33
|
+
RETURN result;
|
|
34
|
+
END IF;
|
|
35
|
+
|
|
36
|
+
SELECT membership.id INTO actor_membership_id
|
|
37
|
+
FROM organization_memberships membership
|
|
38
|
+
WHERE membership.account_id = nullif(p_command ->> 'organizationId', '')::uuid
|
|
39
|
+
AND membership.subject_id = p_command ->> 'actorSubjectId'
|
|
40
|
+
AND membership.status = 'active'
|
|
41
|
+
AND membership.role IN ('owner', 'admin');
|
|
42
|
+
IF actor_membership_id IS NULL THEN
|
|
43
|
+
RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
|
|
44
|
+
END IF;
|
|
45
|
+
|
|
46
|
+
-- Derive one stable, private follow-up operation from the public create key.
|
|
47
|
+
-- A committed create can therefore be retried safely after a lost response,
|
|
48
|
+
-- without ever re-elevating a creator who was deliberately demoted later.
|
|
49
|
+
creator_grant_operation_hash := pg_catalog.md5(
|
|
50
|
+
'organization-workspace-creator-admin:' || (p_command ->> 'operationId')
|
|
51
|
+
);
|
|
52
|
+
creator_grant_operation_id := (
|
|
53
|
+
pg_catalog.substr(creator_grant_operation_hash, 1, 12)
|
|
54
|
+
|| '5'
|
|
55
|
+
|| pg_catalog.substr(creator_grant_operation_hash, 14, 3)
|
|
56
|
+
|| '8'
|
|
57
|
+
|| pg_catalog.substr(creator_grant_operation_hash, 18)
|
|
58
|
+
)::uuid;
|
|
59
|
+
|
|
60
|
+
result := result || organization_workspace_command_without_creator_access(
|
|
61
|
+
pg_catalog.jsonb_build_object(
|
|
62
|
+
'action', 'grant',
|
|
63
|
+
'organizationId', p_command ->> 'organizationId',
|
|
64
|
+
'actorSubjectId', p_command ->> 'actorSubjectId',
|
|
65
|
+
'workspaceId', result ->> 'workspaceId',
|
|
66
|
+
'targetOrganizationMembershipId', actor_membership_id,
|
|
67
|
+
'role', 'admin',
|
|
68
|
+
'operationId', creator_grant_operation_id
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
RETURN result;
|
|
72
|
+
END
|
|
73
|
+
$body$;
|
|
74
|
+
|
|
75
|
+
CREATE FUNCTION authorize_organization_shared_workspace_administration(
|
|
76
|
+
p_account_id uuid,
|
|
77
|
+
p_workspace_id uuid,
|
|
78
|
+
p_actor_subject_id text
|
|
79
|
+
) RETURNS void
|
|
80
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
81
|
+
SET search_path FROM CURRENT
|
|
82
|
+
AS $body$
|
|
83
|
+
DECLARE
|
|
84
|
+
actor organization_memberships%ROWTYPE;
|
|
85
|
+
BEGIN
|
|
86
|
+
IF p_account_id IS NULL OR p_workspace_id IS NULL OR p_actor_subject_id IS NULL
|
|
87
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
88
|
+
OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
89
|
+
OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
90
|
+
THEN
|
|
91
|
+
RAISE EXCEPTION 'organization workspace administration is invalid'
|
|
92
|
+
USING ERRCODE = '22023';
|
|
93
|
+
END IF;
|
|
94
|
+
|
|
95
|
+
PERFORM pg_catalog.pg_advisory_xact_lock(
|
|
96
|
+
pg_catalog.hashtextextended('organization-membership:' || p_account_id::text, 0)
|
|
97
|
+
);
|
|
98
|
+
PERFORM pg_catalog.set_config(
|
|
99
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
100
|
+
'organization_membership_lifecycle', true
|
|
101
|
+
);
|
|
102
|
+
PERFORM 1 FROM managed_accounts account
|
|
103
|
+
WHERE account.id = p_account_id FOR KEY SHARE;
|
|
104
|
+
IF NOT FOUND THEN
|
|
105
|
+
RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002';
|
|
106
|
+
END IF;
|
|
107
|
+
|
|
108
|
+
SELECT * INTO actor FROM organization_memberships membership
|
|
109
|
+
WHERE membership.account_id = p_account_id
|
|
110
|
+
AND membership.subject_id = p_actor_subject_id
|
|
111
|
+
FOR SHARE;
|
|
112
|
+
IF NOT FOUND OR actor.status <> 'active' OR actor.role NOT IN ('owner', 'admin') THEN
|
|
113
|
+
RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
|
|
114
|
+
END IF;
|
|
115
|
+
IF NOT EXISTS (
|
|
116
|
+
SELECT 1 FROM workspaces workspace
|
|
117
|
+
WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
|
|
118
|
+
) THEN
|
|
119
|
+
RAISE EXCEPTION 'workspace not found' USING ERRCODE = 'P0002';
|
|
120
|
+
END IF;
|
|
121
|
+
IF EXISTS (
|
|
122
|
+
SELECT 1 FROM organization_memberships membership
|
|
123
|
+
WHERE membership.account_id = p_account_id
|
|
124
|
+
AND membership.personal_workspace_id = p_workspace_id
|
|
125
|
+
) THEN
|
|
126
|
+
RAISE EXCEPTION 'personal workspaces are not administrable'
|
|
127
|
+
USING ERRCODE = '42501';
|
|
128
|
+
END IF;
|
|
129
|
+
END
|
|
130
|
+
$body$;
|
|
131
|
+
|
|
132
|
+
REVOKE ALL ON FUNCTION organization_workspace_command(jsonb) FROM PUBLIC;
|
|
133
|
+
REVOKE ALL ON FUNCTION authorize_organization_shared_workspace_administration(uuid,uuid,text)
|
|
134
|
+
FROM PUBLIC;
|
|
135
|
+
|
|
136
|
+
DO $pin_and_grant$
|
|
137
|
+
DECLARE
|
|
138
|
+
data_schema text := current_schema();
|
|
139
|
+
legacy_owner oid;
|
|
140
|
+
grantee_name text;
|
|
141
|
+
BEGIN
|
|
142
|
+
EXECUTE format(
|
|
143
|
+
'ALTER FUNCTION %I.organization_workspace_command_without_creator_access(jsonb) SET search_path = pg_catalog, %I, pg_temp',
|
|
144
|
+
data_schema, data_schema
|
|
145
|
+
);
|
|
146
|
+
EXECUTE format(
|
|
147
|
+
'ALTER FUNCTION %I.organization_workspace_command(jsonb) SET search_path = pg_catalog, %I, pg_temp',
|
|
148
|
+
data_schema, data_schema
|
|
149
|
+
);
|
|
150
|
+
EXECUTE format(
|
|
151
|
+
'ALTER FUNCTION %I.authorize_organization_shared_workspace_administration(uuid,uuid,text) SET search_path = pg_catalog, %I, pg_temp',
|
|
152
|
+
data_schema, data_schema
|
|
153
|
+
);
|
|
154
|
+
-- Renaming preserved the legacy function's ACL. Atomically move every
|
|
155
|
+
-- supported runtime role to the replacement wrapper before removing its
|
|
156
|
+
-- bypass access, so a migrate-then-provision rollout has no outage window.
|
|
157
|
+
SELECT function.proowner INTO legacy_owner
|
|
158
|
+
FROM pg_catalog.pg_proc function
|
|
159
|
+
WHERE function.oid =
|
|
160
|
+
'organization_workspace_command_without_creator_access(jsonb)'::regprocedure;
|
|
161
|
+
FOR grantee_name IN
|
|
162
|
+
SELECT role.rolname
|
|
163
|
+
FROM pg_catalog.pg_proc function
|
|
164
|
+
CROSS JOIN LATERAL pg_catalog.aclexplode(
|
|
165
|
+
coalesce(function.proacl, pg_catalog.acldefault('f', function.proowner))
|
|
166
|
+
) grant_value
|
|
167
|
+
JOIN pg_catalog.pg_roles role ON role.oid = grant_value.grantee
|
|
168
|
+
WHERE function.oid =
|
|
169
|
+
'organization_workspace_command_without_creator_access(jsonb)'::regprocedure
|
|
170
|
+
AND grant_value.privilege_type = 'EXECUTE'
|
|
171
|
+
AND grant_value.grantee <> legacy_owner
|
|
172
|
+
LOOP
|
|
173
|
+
EXECUTE format(
|
|
174
|
+
'GRANT EXECUTE ON FUNCTION %I.organization_workspace_command(jsonb) TO %I',
|
|
175
|
+
data_schema, grantee_name
|
|
176
|
+
);
|
|
177
|
+
EXECUTE format(
|
|
178
|
+
'GRANT EXECUTE ON FUNCTION %I.authorize_organization_shared_workspace_administration(uuid,uuid,text) TO %I',
|
|
179
|
+
data_schema, grantee_name
|
|
180
|
+
);
|
|
181
|
+
EXECUTE format(
|
|
182
|
+
'REVOKE EXECUTE ON FUNCTION %I.organization_workspace_command_without_creator_access(jsonb) FROM %I',
|
|
183
|
+
data_schema, grantee_name
|
|
184
|
+
);
|
|
185
|
+
END LOOP;
|
|
186
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
187
|
+
EXECUTE format(
|
|
188
|
+
'REVOKE ALL ON FUNCTION %I.organization_workspace_command_without_creator_access(jsonb) FROM opengeni_app',
|
|
189
|
+
data_schema
|
|
190
|
+
);
|
|
191
|
+
EXECUTE format(
|
|
192
|
+
'GRANT EXECUTE ON FUNCTION %I.organization_workspace_command(jsonb) TO opengeni_app',
|
|
193
|
+
data_schema
|
|
194
|
+
);
|
|
195
|
+
EXECUTE format(
|
|
196
|
+
'GRANT EXECUTE ON FUNCTION %I.authorize_organization_shared_workspace_administration(uuid,uuid,text) TO opengeni_app',
|
|
197
|
+
data_schema
|
|
198
|
+
);
|
|
199
|
+
END IF;
|
|
200
|
+
END
|
|
201
|
+
$pin_and_grant$;
|
|
202
|
+
|
|
203
|
+
COMMENT ON FUNCTION organization_workspace_command(jsonb) IS
|
|
204
|
+
'Idempotent organization-admin shared-workspace command. A create atomically materializes an explicit named admin grant for its creator.';
|
|
205
|
+
COMMENT ON FUNCTION authorize_organization_shared_workspace_administration(uuid,uuid,text) IS
|
|
206
|
+
'Transaction-scoped, content-blind organization owner/admin authority for managing one shared workspace. It grants no operational workspace access.';
|