@opengeni/db 3.7.4 → 4.0.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/automations.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +2 -0
- package/dist/{chunk-6DPJA36C.js → chunk-2XEZK4LF.js} +7 -1
- package/dist/chunk-2XEZK4LF.js.map +1 -0
- package/dist/{chunk-4NAR7FBA.js → chunk-3RI33YQN.js} +67 -1
- package/dist/chunk-3RI33YQN.js.map +1 -0
- package/dist/{chunk-SZAWDXQC.js → chunk-CJROYHSC.js} +3 -3
- package/dist/{chunk-DZCYW2WT.js → chunk-MX6V5F65.js} +2 -2
- package/dist/{chunk-S3TDDCS2.js → chunk-N5VXJ4I2.js} +151 -78
- package/dist/chunk-N5VXJ4I2.js.map +1 -0
- package/dist/{chunk-U4WPPEBH.js → chunk-NLFHMPUB.js} +2 -2
- package/dist/{chunk-5NNCGFQY.js → chunk-P6NSEG2S.js} +2 -2
- package/dist/{chunk-O56DM5GZ.js → chunk-QM6YOWHZ.js} +624 -57
- package/dist/chunk-QM6YOWHZ.js.map +1 -0
- package/dist/{chunk-US4IS6IV.js → chunk-RZNOSEMC.js} +2 -2
- package/dist/{chunk-F6PBS6WE.js → chunk-UFAMOUWL.js} +2 -2
- package/dist/codemode-operations.d.ts +5 -0
- package/dist/codex-token-resolver.d.ts +6 -2
- package/dist/connection-token-resolver.d.ts +8 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/generated-images.d.ts +15 -0
- package/dist/index.d.ts +393 -91
- package/dist/index.js +9992 -6069
- 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/managed-user-setup.d.ts +3 -0
- package/dist/mcp-oauth.d.ts +80 -0
- package/dist/migrate.d.ts +1 -0
- package/dist/migrate.js +3 -1
- package/dist/model-catalog.d.ts +153 -0
- package/dist/model-context-snapshots.d.ts +20 -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 +8 -8
- package/dist/schema.d.ts +7708 -5050
- package/dist/schema.js +25 -1
- package/dist/session-background-commands.d.ts +18 -6
- package/dist/session-background-commands.js +15 -9
- package/dist/session-control.d.ts +37 -2
- package/dist/session-goal-pacing.d.ts +3 -6
- package/dist/session-queue-commands.d.ts +17 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/tool-gateway-approvals.d.ts +31 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-artifacts.d.ts +17 -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/drizzle/0400_session_attempt_model_context_snapshots.sql +65 -0
- package/drizzle/0401_organization_user_setup_token_transport.sql +178 -0
- package/drizzle/0402_session_input_wait_and_background_command_results.sql +1023 -0
- package/drizzle/0403_codex_unconditional_credential_leasing.sql +181 -0
- package/drizzle/0404_mcp_oauth_authorization_server.sql +471 -0
- package/drizzle/0405_tool_gateway_approval_capabilities.sql +184 -0
- package/drizzle/0406_workspace_html_site_sources.sql +71 -0
- package/package.json +6 -6
- package/src/automations.ts +9 -1
- package/src/capability-integrations.ts +8 -0
- package/src/codemode-operations.ts +31 -21
- package/src/codex-token-resolver.ts +16 -6
- package/src/connection-token-resolver.ts +52 -3
- package/src/generated-images.ts +70 -6
- package/src/index.ts +3894 -994
- package/src/insights-model-bundle.ts +54 -5
- package/src/insights.ts +53 -7
- package/src/managed-user-setup.ts +14 -3
- package/src/mcp-oauth.ts +488 -0
- package/src/migrate.ts +9 -1
- package/src/model-catalog.ts +682 -0
- package/src/model-context-snapshots.ts +151 -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 +4 -0
- package/src/runtime-posture.ts +64 -0
- package/src/schema.ts +666 -61
- package/src/session-background-commands.ts +180 -78
- package/src/session-control.ts +181 -12
- package/src/session-goal-pacing.ts +4 -8
- package/src/session-queue-commands.ts +157 -82
- package/src/tool-gateway-approvals.ts +168 -0
- package/src/workspace-artifacts.ts +633 -178
- package/src/xai-subscription.ts +66 -14
- package/dist/chunk-4NAR7FBA.js.map +0 -1
- package/dist/chunk-6DPJA36C.js.map +0 -1
- package/dist/chunk-O56DM5GZ.js.map +0 -1
- package/dist/chunk-S3TDDCS2.js.map +0 -1
- /package/dist/{chunk-SZAWDXQC.js.map → chunk-CJROYHSC.js.map} +0 -0
- /package/dist/{chunk-DZCYW2WT.js.map → chunk-MX6V5F65.js.map} +0 -0
- /package/dist/{chunk-U4WPPEBH.js.map → chunk-NLFHMPUB.js.map} +0 -0
- /package/dist/{chunk-5NNCGFQY.js.map → chunk-P6NSEG2S.js.map} +0 -0
- /package/dist/{chunk-US4IS6IV.js.map → chunk-RZNOSEMC.js.map} +0 -0
- /package/dist/{chunk-F6PBS6WE.js.map → chunk-UFAMOUWL.js.map} +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Migration 0403 retires the temporary allocator cutover bits. Every Codex
|
|
3
|
+
-- turn now uses the durable credential lease protocol; rotation_enabled remains
|
|
4
|
+
-- the user-owned policy deciding whether a new lease may leave the active
|
|
5
|
+
-- account. Stop every pre-0403 API, control-worker, and turn-worker before this
|
|
6
|
+
-- migration, then start only the 0403-aware binary.
|
|
7
|
+
|
|
8
|
+
SET LOCAL lock_timeout = '5s';
|
|
9
|
+
SET LOCAL statement_timeout = '5min';
|
|
10
|
+
|
|
11
|
+
DO $codex_unconditional_leasing_runtime_drain_before_lock$
|
|
12
|
+
DECLARE
|
|
13
|
+
configured_roles_text text := nullif(
|
|
14
|
+
current_setting('opengeni.migration_application_roles', true), ''
|
|
15
|
+
);
|
|
16
|
+
configured_roles jsonb;
|
|
17
|
+
BEGIN
|
|
18
|
+
IF configured_roles_text IS NULL THEN
|
|
19
|
+
RAISE EXCEPTION
|
|
20
|
+
'0403 unconditional Codex leasing requires an explicit application database role list'
|
|
21
|
+
USING ERRCODE = '55000';
|
|
22
|
+
END IF;
|
|
23
|
+
BEGIN
|
|
24
|
+
configured_roles := configured_roles_text::jsonb;
|
|
25
|
+
EXCEPTION WHEN OTHERS THEN
|
|
26
|
+
RAISE EXCEPTION
|
|
27
|
+
'0403 unconditional Codex leasing received a malformed application database role list'
|
|
28
|
+
USING ERRCODE = '55000';
|
|
29
|
+
END;
|
|
30
|
+
IF jsonb_typeof(configured_roles) <> 'array'
|
|
31
|
+
OR jsonb_array_length(configured_roles) NOT BETWEEN 1 AND 16
|
|
32
|
+
OR EXISTS (
|
|
33
|
+
SELECT 1 FROM jsonb_array_elements(configured_roles) AS roles(value)
|
|
34
|
+
WHERE jsonb_typeof(value) <> 'string'
|
|
35
|
+
OR btrim(value #>> '{}') = ''
|
|
36
|
+
OR value #>> '{}' <> btrim(value #>> '{}')
|
|
37
|
+
OR octet_length(value #>> '{}') > 63
|
|
38
|
+
)
|
|
39
|
+
OR (
|
|
40
|
+
SELECT count(*) FROM jsonb_array_elements_text(configured_roles)
|
|
41
|
+
) <> (
|
|
42
|
+
SELECT count(DISTINCT value)
|
|
43
|
+
FROM jsonb_array_elements_text(configured_roles) AS roles(value)
|
|
44
|
+
)
|
|
45
|
+
THEN
|
|
46
|
+
RAISE EXCEPTION
|
|
47
|
+
'0403 unconditional Codex leasing received an invalid application database role list'
|
|
48
|
+
USING ERRCODE = '55000';
|
|
49
|
+
END IF;
|
|
50
|
+
IF EXISTS (
|
|
51
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
52
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
53
|
+
ON roles.role_name = activity.usename
|
|
54
|
+
WHERE activity.datname = current_database()
|
|
55
|
+
AND activity.pid <> pg_backend_pid()
|
|
56
|
+
)
|
|
57
|
+
THEN
|
|
58
|
+
RAISE EXCEPTION
|
|
59
|
+
'0403 unconditional Codex leasing requires all configured OpenGeni application database sessions to be stopped'
|
|
60
|
+
USING ERRCODE = '55000';
|
|
61
|
+
END IF;
|
|
62
|
+
END
|
|
63
|
+
$codex_unconditional_leasing_runtime_drain_before_lock$;
|
|
64
|
+
|
|
65
|
+
LOCK TABLE organization_codex_rotation_settings IN ACCESS EXCLUSIVE MODE;
|
|
66
|
+
LOCK TABLE codex_rotation_settings IN ACCESS EXCLUSIVE MODE;
|
|
67
|
+
LOCK TABLE session_goals IN ACCESS EXCLUSIVE MODE;
|
|
68
|
+
LOCK TABLE codex_capacity_waiters IN ACCESS EXCLUSIVE MODE;
|
|
69
|
+
|
|
70
|
+
DO $codex_unconditional_leasing_runtime_drain_after_lock$
|
|
71
|
+
DECLARE
|
|
72
|
+
configured_roles jsonb := current_setting(
|
|
73
|
+
'opengeni.migration_application_roles', false
|
|
74
|
+
)::jsonb;
|
|
75
|
+
BEGIN
|
|
76
|
+
IF EXISTS (
|
|
77
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
78
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
79
|
+
ON roles.role_name = activity.usename
|
|
80
|
+
WHERE activity.datname = current_database()
|
|
81
|
+
AND activity.pid <> pg_backend_pid()
|
|
82
|
+
)
|
|
83
|
+
THEN
|
|
84
|
+
RAISE EXCEPTION
|
|
85
|
+
'0403 unconditional Codex leasing requires all configured OpenGeni application database sessions to be stopped'
|
|
86
|
+
USING ERRCODE = '55000';
|
|
87
|
+
END IF;
|
|
88
|
+
END
|
|
89
|
+
$codex_unconditional_leasing_runtime_drain_after_lock$;
|
|
90
|
+
|
|
91
|
+
-- Preserve an active goal without allowing an unchanged bounded Codex
|
|
92
|
+
-- failover failure to synthesize a fresh turn and reset its per-turn budget.
|
|
93
|
+
ALTER TABLE session_goals
|
|
94
|
+
ADD COLUMN IF NOT EXISTS continuation_suppressed_turn_id uuid;
|
|
95
|
+
|
|
96
|
+
ALTER TABLE organization_codex_rotation_settings
|
|
97
|
+
DROP COLUMN IF EXISTS lease_rotation_enabled;
|
|
98
|
+
|
|
99
|
+
ALTER TABLE codex_rotation_settings
|
|
100
|
+
DROP COLUMN IF EXISTS lease_rotation_enabled;
|
|
101
|
+
|
|
102
|
+
-- Policy/status waits are woken by allocator, connection, source, and pin
|
|
103
|
+
-- mutations. They must not fan out provider quota reads on every waiter timer.
|
|
104
|
+
ALTER TABLE codex_capacity_waiters
|
|
105
|
+
DROP CONSTRAINT IF EXISTS codex_capacity_waiters_reset_kind_check;
|
|
106
|
+
ALTER TABLE codex_capacity_waiters
|
|
107
|
+
ADD CONSTRAINT codex_capacity_waiters_reset_kind_check
|
|
108
|
+
CHECK (reset_kind IN ('authoritative', 'bounded_refresh', 'mutation_only'));
|
|
109
|
+
|
|
110
|
+
-- Migration 0381 installed these organization lease guards with `now()`,
|
|
111
|
+
-- whose value is fixed at transaction start. Reinstall the same guards with
|
|
112
|
+
-- execution-time clock semantics before the durable lease protocol becomes
|
|
113
|
+
-- authoritative, so a long maintenance-adjacent transaction cannot treat an
|
|
114
|
+
-- already-expired lease as live.
|
|
115
|
+
DO $codex_execution_time_lease_guards$
|
|
116
|
+
DECLARE data_schema text := current_schema();
|
|
117
|
+
BEGIN
|
|
118
|
+
EXECUTE format($ddl$
|
|
119
|
+
CREATE OR REPLACE FUNCTION opengeni_private.codex_organization_live_lease_count(
|
|
120
|
+
p_account_id uuid,
|
|
121
|
+
p_credential_id uuid,
|
|
122
|
+
p_exclude_turn_id uuid
|
|
123
|
+
) RETURNS integer
|
|
124
|
+
LANGUAGE plpgsql
|
|
125
|
+
SECURITY DEFINER
|
|
126
|
+
SET search_path = pg_catalog, %1$I
|
|
127
|
+
AS $function$
|
|
128
|
+
DECLARE live_count integer;
|
|
129
|
+
BEGIN
|
|
130
|
+
IF p_account_id IS NULL
|
|
131
|
+
OR p_credential_id IS NULL
|
|
132
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
133
|
+
OR opengeni_private.current_workspace_id() IS NULL
|
|
134
|
+
OR NOT EXISTS (
|
|
135
|
+
SELECT 1 FROM codex_subscription_credentials credential
|
|
136
|
+
WHERE credential.account_id = p_account_id
|
|
137
|
+
AND credential.id = p_credential_id
|
|
138
|
+
AND credential.organization_id = p_account_id
|
|
139
|
+
AND credential.authority_scope = 'organization'
|
|
140
|
+
)
|
|
141
|
+
OR NOT opengeni_private.codex_credential_serves_workspace(
|
|
142
|
+
p_account_id,
|
|
143
|
+
opengeni_private.current_workspace_id(),
|
|
144
|
+
p_credential_id
|
|
145
|
+
)
|
|
146
|
+
THEN
|
|
147
|
+
RAISE EXCEPTION 'organization Codex lease-count authority required'
|
|
148
|
+
USING ERRCODE = '42501';
|
|
149
|
+
END IF;
|
|
150
|
+
SELECT count(*)::integer INTO live_count
|
|
151
|
+
FROM codex_credential_leases lease
|
|
152
|
+
WHERE lease.account_id = p_account_id
|
|
153
|
+
AND lease.credential_id = p_credential_id
|
|
154
|
+
AND lease.leased_until > clock_timestamp()
|
|
155
|
+
AND (p_exclude_turn_id IS NULL OR lease.turn_id <> p_exclude_turn_id);
|
|
156
|
+
RETURN live_count;
|
|
157
|
+
END
|
|
158
|
+
$function$;
|
|
159
|
+
|
|
160
|
+
CREATE OR REPLACE FUNCTION opengeni_private.prevent_organization_codex_disconnect_with_live_leases()
|
|
161
|
+
RETURNS trigger
|
|
162
|
+
LANGUAGE plpgsql
|
|
163
|
+
SECURITY DEFINER
|
|
164
|
+
SET search_path = pg_catalog, %1$I
|
|
165
|
+
AS $function$
|
|
166
|
+
BEGIN
|
|
167
|
+
IF EXISTS (
|
|
168
|
+
SELECT 1 FROM codex_credential_leases lease
|
|
169
|
+
WHERE lease.account_id = OLD.account_id
|
|
170
|
+
AND lease.credential_id = OLD.id
|
|
171
|
+
AND lease.leased_until > clock_timestamp()
|
|
172
|
+
) THEN
|
|
173
|
+
RAISE EXCEPTION 'Codex subscription cannot disconnect while active turns are using it'
|
|
174
|
+
USING ERRCODE = '55006';
|
|
175
|
+
END IF;
|
|
176
|
+
RETURN OLD;
|
|
177
|
+
END
|
|
178
|
+
$function$;
|
|
179
|
+
$ddl$, data_schema);
|
|
180
|
+
END
|
|
181
|
+
$codex_execution_time_lease_guards$;
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Opt-in OAuth 2.1-style authorization-server state for exact workspace MCP
|
|
3
|
+
-- resources. Only hashes of authorization codes and bearer/refresh tokens are
|
|
4
|
+
-- retained; grants freeze permissions and tool identities for later live
|
|
5
|
+
-- intersection.
|
|
6
|
+
-- This changes the exact runtime-posture table/grant contract. Stop every API,
|
|
7
|
+
-- control worker, and turn worker before applying it, and never restart a
|
|
8
|
+
-- pre-0404 image after commit.
|
|
9
|
+
|
|
10
|
+
SET LOCAL lock_timeout = '5s';
|
|
11
|
+
SET LOCAL statement_timeout = '5min';
|
|
12
|
+
|
|
13
|
+
DO $mcp_oauth_runtime_drain_before$
|
|
14
|
+
DECLARE
|
|
15
|
+
configured_roles_text text := nullif(
|
|
16
|
+
current_setting('opengeni.migration_application_roles', true), ''
|
|
17
|
+
);
|
|
18
|
+
configured_roles jsonb;
|
|
19
|
+
BEGIN
|
|
20
|
+
IF configured_roles_text IS NULL THEN
|
|
21
|
+
RAISE EXCEPTION
|
|
22
|
+
'0403 MCP OAuth activation requires an explicit application database role list'
|
|
23
|
+
USING ERRCODE = '55000';
|
|
24
|
+
END IF;
|
|
25
|
+
BEGIN
|
|
26
|
+
configured_roles := configured_roles_text::jsonb;
|
|
27
|
+
EXCEPTION WHEN OTHERS THEN
|
|
28
|
+
RAISE EXCEPTION
|
|
29
|
+
'0403 MCP OAuth activation received a malformed application database role list'
|
|
30
|
+
USING ERRCODE = '55000';
|
|
31
|
+
END;
|
|
32
|
+
IF jsonb_typeof(configured_roles) <> 'array'
|
|
33
|
+
OR jsonb_array_length(configured_roles) NOT BETWEEN 1 AND 16
|
|
34
|
+
OR EXISTS (
|
|
35
|
+
SELECT 1 FROM jsonb_array_elements(configured_roles) AS roles(value)
|
|
36
|
+
WHERE jsonb_typeof(value) <> 'string'
|
|
37
|
+
OR btrim(value #>> '{}') = ''
|
|
38
|
+
OR octet_length(value #>> '{}') > 63
|
|
39
|
+
)
|
|
40
|
+
OR (
|
|
41
|
+
SELECT count(*) FROM jsonb_array_elements_text(configured_roles)
|
|
42
|
+
) <> (
|
|
43
|
+
SELECT count(DISTINCT value)
|
|
44
|
+
FROM jsonb_array_elements_text(configured_roles) AS roles(value)
|
|
45
|
+
)
|
|
46
|
+
THEN
|
|
47
|
+
RAISE EXCEPTION
|
|
48
|
+
'0403 MCP OAuth activation received an invalid application database role list'
|
|
49
|
+
USING ERRCODE = '55000';
|
|
50
|
+
END IF;
|
|
51
|
+
IF EXISTS (
|
|
52
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
53
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
54
|
+
ON roles.role_name = activity.usename
|
|
55
|
+
WHERE activity.datname = current_database()
|
|
56
|
+
AND activity.pid <> pg_backend_pid()
|
|
57
|
+
)
|
|
58
|
+
THEN
|
|
59
|
+
RAISE EXCEPTION
|
|
60
|
+
'0403 MCP OAuth activation requires all configured OpenGeni application database sessions to be stopped'
|
|
61
|
+
USING ERRCODE = '55000';
|
|
62
|
+
END IF;
|
|
63
|
+
END
|
|
64
|
+
$mcp_oauth_runtime_drain_before$;
|
|
65
|
+
|
|
66
|
+
CREATE TABLE mcp_oauth_clients (
|
|
67
|
+
client_id text PRIMARY KEY CHECK (octet_length(client_id) BETWEEN 16 AND 256),
|
|
68
|
+
redirect_uris jsonb NOT NULL CHECK (
|
|
69
|
+
jsonb_typeof(redirect_uris) = 'array' AND jsonb_array_length(redirect_uris) BETWEEN 1 AND 16
|
|
70
|
+
),
|
|
71
|
+
client_name text CHECK (client_name IS NULL OR octet_length(client_name) BETWEEN 1 AND 200),
|
|
72
|
+
token_endpoint_auth_method text NOT NULL DEFAULT 'none' CHECK (token_endpoint_auth_method = 'none'),
|
|
73
|
+
grant_types jsonb NOT NULL CHECK (jsonb_typeof(grant_types) = 'array'),
|
|
74
|
+
response_types jsonb NOT NULL CHECK (response_types = '["code"]'::jsonb),
|
|
75
|
+
registration_scope_hash text NOT NULL CHECK (registration_scope_hash ~ '^[0-9a-f]{64}$'),
|
|
76
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
77
|
+
expires_at timestamptz NOT NULL DEFAULT (now() + interval '1 day'),
|
|
78
|
+
CHECK (expires_at > created_at)
|
|
79
|
+
);
|
|
80
|
+
CREATE INDEX mcp_oauth_clients_created_idx ON mcp_oauth_clients(created_at);
|
|
81
|
+
CREATE INDEX mcp_oauth_clients_scope_created_idx
|
|
82
|
+
ON mcp_oauth_clients(registration_scope_hash, created_at);
|
|
83
|
+
CREATE INDEX mcp_oauth_clients_expires_idx ON mcp_oauth_clients(expires_at, client_id);
|
|
84
|
+
|
|
85
|
+
CREATE TABLE mcp_oauth_authorization_requests (
|
|
86
|
+
request_hash text PRIMARY KEY CHECK (request_hash ~ '^[0-9a-f]{64}$'),
|
|
87
|
+
client_id text NOT NULL REFERENCES mcp_oauth_clients(client_id) ON DELETE CASCADE,
|
|
88
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
89
|
+
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
90
|
+
subject_id text NOT NULL CHECK (octet_length(subject_id) BETWEEN 1 AND 1024),
|
|
91
|
+
resource text NOT NULL CHECK (octet_length(resource) BETWEEN 1 AND 2048),
|
|
92
|
+
redirect_uri text NOT NULL CHECK (octet_length(redirect_uri) BETWEEN 1 AND 2048),
|
|
93
|
+
code_challenge text NOT NULL CHECK (code_challenge ~ '^[A-Za-z0-9_-]{43}$'),
|
|
94
|
+
state text CHECK (state IS NULL OR octet_length(state) BETWEEN 1 AND 1024),
|
|
95
|
+
permissions jsonb NOT NULL CHECK (jsonb_typeof(permissions) = 'array'),
|
|
96
|
+
tool_identities jsonb NOT NULL CHECK (
|
|
97
|
+
jsonb_typeof(tool_identities) = 'array' AND jsonb_array_length(tool_identities) <= 4096
|
|
98
|
+
),
|
|
99
|
+
expires_at timestamptz NOT NULL,
|
|
100
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
101
|
+
);
|
|
102
|
+
ALTER TABLE mcp_oauth_authorization_requests
|
|
103
|
+
ADD CONSTRAINT mcp_oauth_authorization_requests_workspace_account_fk
|
|
104
|
+
FOREIGN KEY (workspace_id, account_id) REFERENCES workspaces(id, account_id) ON DELETE CASCADE;
|
|
105
|
+
CREATE INDEX mcp_oauth_authorization_requests_expires_idx
|
|
106
|
+
ON mcp_oauth_authorization_requests(expires_at);
|
|
107
|
+
|
|
108
|
+
CREATE TABLE mcp_oauth_authorization_codes (
|
|
109
|
+
code_hash text PRIMARY KEY CHECK (code_hash ~ '^[0-9a-f]{64}$'),
|
|
110
|
+
client_id text NOT NULL REFERENCES mcp_oauth_clients(client_id) ON DELETE CASCADE,
|
|
111
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
112
|
+
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
113
|
+
subject_id text NOT NULL CHECK (octet_length(subject_id) BETWEEN 1 AND 1024),
|
|
114
|
+
resource text NOT NULL CHECK (octet_length(resource) BETWEEN 1 AND 2048),
|
|
115
|
+
redirect_uri text NOT NULL CHECK (octet_length(redirect_uri) BETWEEN 1 AND 2048),
|
|
116
|
+
code_challenge text NOT NULL CHECK (code_challenge ~ '^[A-Za-z0-9_-]{43}$'),
|
|
117
|
+
permissions jsonb NOT NULL CHECK (jsonb_typeof(permissions) = 'array'),
|
|
118
|
+
tool_identities jsonb NOT NULL CHECK (
|
|
119
|
+
jsonb_typeof(tool_identities) = 'array' AND jsonb_array_length(tool_identities) <= 4096
|
|
120
|
+
),
|
|
121
|
+
expires_at timestamptz NOT NULL,
|
|
122
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
123
|
+
);
|
|
124
|
+
ALTER TABLE mcp_oauth_authorization_codes
|
|
125
|
+
ADD CONSTRAINT mcp_oauth_authorization_codes_workspace_account_fk
|
|
126
|
+
FOREIGN KEY (workspace_id, account_id) REFERENCES workspaces(id, account_id) ON DELETE CASCADE;
|
|
127
|
+
CREATE INDEX mcp_oauth_authorization_codes_expires_idx
|
|
128
|
+
ON mcp_oauth_authorization_codes(expires_at);
|
|
129
|
+
|
|
130
|
+
CREATE TABLE mcp_oauth_refresh_tokens (
|
|
131
|
+
token_hash text PRIMARY KEY CHECK (token_hash ~ '^[0-9a-f]{64}$'),
|
|
132
|
+
family_id uuid NOT NULL,
|
|
133
|
+
generation integer NOT NULL CHECK (generation > 0),
|
|
134
|
+
client_id text NOT NULL REFERENCES mcp_oauth_clients(client_id) ON DELETE CASCADE,
|
|
135
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
136
|
+
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
137
|
+
subject_id text NOT NULL CHECK (octet_length(subject_id) BETWEEN 1 AND 1024),
|
|
138
|
+
resource text NOT NULL CHECK (octet_length(resource) BETWEEN 1 AND 2048),
|
|
139
|
+
permissions jsonb NOT NULL CHECK (jsonb_typeof(permissions) = 'array'),
|
|
140
|
+
tool_identities jsonb NOT NULL CHECK (
|
|
141
|
+
jsonb_typeof(tool_identities) = 'array' AND jsonb_array_length(tool_identities) <= 4096
|
|
142
|
+
),
|
|
143
|
+
expires_at timestamptz NOT NULL,
|
|
144
|
+
revoked_at timestamptz,
|
|
145
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
146
|
+
UNIQUE (family_id, generation)
|
|
147
|
+
);
|
|
148
|
+
ALTER TABLE mcp_oauth_refresh_tokens
|
|
149
|
+
ADD CONSTRAINT mcp_oauth_refresh_tokens_workspace_account_fk
|
|
150
|
+
FOREIGN KEY (workspace_id, account_id) REFERENCES workspaces(id, account_id) ON DELETE CASCADE;
|
|
151
|
+
CREATE INDEX mcp_oauth_refresh_tokens_expires_idx ON mcp_oauth_refresh_tokens(expires_at);
|
|
152
|
+
|
|
153
|
+
CREATE TABLE mcp_oauth_access_tokens (
|
|
154
|
+
token_hash text PRIMARY KEY CHECK (token_hash ~ '^[0-9a-f]{64}$'),
|
|
155
|
+
refresh_family_id uuid NOT NULL,
|
|
156
|
+
refresh_generation integer NOT NULL CHECK (refresh_generation > 0),
|
|
157
|
+
client_id text NOT NULL REFERENCES mcp_oauth_clients(client_id) ON DELETE CASCADE,
|
|
158
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
159
|
+
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
160
|
+
subject_id text NOT NULL CHECK (octet_length(subject_id) BETWEEN 1 AND 1024),
|
|
161
|
+
resource text NOT NULL CHECK (octet_length(resource) BETWEEN 1 AND 2048),
|
|
162
|
+
permissions jsonb NOT NULL CHECK (jsonb_typeof(permissions) = 'array'),
|
|
163
|
+
tool_identities jsonb NOT NULL CHECK (
|
|
164
|
+
jsonb_typeof(tool_identities) = 'array' AND jsonb_array_length(tool_identities) <= 4096
|
|
165
|
+
),
|
|
166
|
+
expires_at timestamptz NOT NULL,
|
|
167
|
+
revoked_at timestamptz,
|
|
168
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
169
|
+
);
|
|
170
|
+
ALTER TABLE mcp_oauth_access_tokens
|
|
171
|
+
ADD CONSTRAINT mcp_oauth_access_tokens_workspace_account_fk
|
|
172
|
+
FOREIGN KEY (workspace_id, account_id) REFERENCES workspaces(id, account_id) ON DELETE CASCADE;
|
|
173
|
+
CREATE INDEX mcp_oauth_access_tokens_family_idx
|
|
174
|
+
ON mcp_oauth_access_tokens(refresh_family_id, refresh_generation);
|
|
175
|
+
CREATE INDEX mcp_oauth_access_tokens_expires_idx ON mcp_oauth_access_tokens(expires_at);
|
|
176
|
+
|
|
177
|
+
-- Public dynamic client registration must remain bounded across every API
|
|
178
|
+
-- replica. Untouched registrations expire after one day; a successful client
|
|
179
|
+
-- lookup extends the client through the maximum refresh-token lifetime plus a
|
|
180
|
+
-- one-day grace period. The reaper is deliberately batch-bounded and is invoked
|
|
181
|
+
-- by registration and client lookup, so normal OAuth traffic continuously
|
|
182
|
+
-- removes expired protocol state without a separate scheduler.
|
|
183
|
+
CREATE FUNCTION opengeni_private.reap_mcp_oauth_state(p_limit integer DEFAULT 128)
|
|
184
|
+
RETURNS integer
|
|
185
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
186
|
+
SET search_path FROM CURRENT
|
|
187
|
+
AS $body$
|
|
188
|
+
DECLARE
|
|
189
|
+
bounded_limit integer := least(greatest(coalesce(p_limit, 128), 1), 1000);
|
|
190
|
+
removed integer := 0;
|
|
191
|
+
affected integer;
|
|
192
|
+
BEGIN
|
|
193
|
+
WITH expired AS (
|
|
194
|
+
SELECT request_hash
|
|
195
|
+
FROM mcp_oauth_authorization_requests
|
|
196
|
+
WHERE expires_at <= pg_catalog.clock_timestamp()
|
|
197
|
+
ORDER BY expires_at, request_hash
|
|
198
|
+
FOR UPDATE SKIP LOCKED
|
|
199
|
+
LIMIT bounded_limit
|
|
200
|
+
)
|
|
201
|
+
DELETE FROM mcp_oauth_authorization_requests target
|
|
202
|
+
USING expired
|
|
203
|
+
WHERE target.request_hash = expired.request_hash;
|
|
204
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
205
|
+
removed := removed + affected;
|
|
206
|
+
|
|
207
|
+
WITH expired AS (
|
|
208
|
+
SELECT code_hash
|
|
209
|
+
FROM mcp_oauth_authorization_codes
|
|
210
|
+
WHERE expires_at <= pg_catalog.clock_timestamp()
|
|
211
|
+
ORDER BY expires_at, code_hash
|
|
212
|
+
FOR UPDATE SKIP LOCKED
|
|
213
|
+
LIMIT bounded_limit
|
|
214
|
+
)
|
|
215
|
+
DELETE FROM mcp_oauth_authorization_codes target
|
|
216
|
+
USING expired
|
|
217
|
+
WHERE target.code_hash = expired.code_hash;
|
|
218
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
219
|
+
removed := removed + affected;
|
|
220
|
+
|
|
221
|
+
WITH expired AS (
|
|
222
|
+
SELECT token_hash
|
|
223
|
+
FROM mcp_oauth_access_tokens
|
|
224
|
+
WHERE expires_at <= pg_catalog.clock_timestamp()
|
|
225
|
+
OR revoked_at <= pg_catalog.clock_timestamp() - interval '1 day'
|
|
226
|
+
ORDER BY expires_at, token_hash
|
|
227
|
+
FOR UPDATE SKIP LOCKED
|
|
228
|
+
LIMIT bounded_limit
|
|
229
|
+
)
|
|
230
|
+
DELETE FROM mcp_oauth_access_tokens target
|
|
231
|
+
USING expired
|
|
232
|
+
WHERE target.token_hash = expired.token_hash;
|
|
233
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
234
|
+
removed := removed + affected;
|
|
235
|
+
|
|
236
|
+
WITH expired AS (
|
|
237
|
+
SELECT candidate.token_hash
|
|
238
|
+
FROM mcp_oauth_refresh_tokens candidate
|
|
239
|
+
WHERE (
|
|
240
|
+
candidate.expires_at <= pg_catalog.clock_timestamp()
|
|
241
|
+
OR candidate.revoked_at <= pg_catalog.clock_timestamp() - interval '1 day'
|
|
242
|
+
)
|
|
243
|
+
-- A rotated generation is the durable replay detector for its family.
|
|
244
|
+
-- Keep every tombstone until no descendant refresh token can remain live.
|
|
245
|
+
AND NOT EXISTS (
|
|
246
|
+
SELECT 1
|
|
247
|
+
FROM mcp_oauth_refresh_tokens live_family
|
|
248
|
+
WHERE live_family.family_id = candidate.family_id
|
|
249
|
+
AND live_family.revoked_at IS NULL
|
|
250
|
+
AND live_family.expires_at > pg_catalog.clock_timestamp()
|
|
251
|
+
)
|
|
252
|
+
ORDER BY candidate.expires_at, candidate.token_hash
|
|
253
|
+
FOR UPDATE SKIP LOCKED
|
|
254
|
+
LIMIT bounded_limit
|
|
255
|
+
)
|
|
256
|
+
DELETE FROM mcp_oauth_refresh_tokens target
|
|
257
|
+
USING expired
|
|
258
|
+
WHERE target.token_hash = expired.token_hash;
|
|
259
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
260
|
+
removed := removed + affected;
|
|
261
|
+
|
|
262
|
+
WITH expired AS (
|
|
263
|
+
SELECT client_id
|
|
264
|
+
FROM mcp_oauth_clients
|
|
265
|
+
WHERE expires_at <= pg_catalog.clock_timestamp()
|
|
266
|
+
ORDER BY expires_at, client_id
|
|
267
|
+
FOR UPDATE SKIP LOCKED
|
|
268
|
+
LIMIT bounded_limit
|
|
269
|
+
)
|
|
270
|
+
DELETE FROM mcp_oauth_clients target
|
|
271
|
+
USING expired
|
|
272
|
+
WHERE target.client_id = expired.client_id;
|
|
273
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
274
|
+
RETURN removed + affected;
|
|
275
|
+
END
|
|
276
|
+
$body$;
|
|
277
|
+
|
|
278
|
+
CREATE FUNCTION opengeni_private.register_mcp_oauth_client(
|
|
279
|
+
p_client_id text,
|
|
280
|
+
p_redirect_uris jsonb,
|
|
281
|
+
p_client_name text,
|
|
282
|
+
p_grant_types jsonb,
|
|
283
|
+
p_response_types jsonb,
|
|
284
|
+
p_registration_scope_hash text
|
|
285
|
+
)
|
|
286
|
+
RETURNS SETOF mcp_oauth_clients
|
|
287
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
288
|
+
SET search_path FROM CURRENT
|
|
289
|
+
AS $body$
|
|
290
|
+
DECLARE
|
|
291
|
+
now_value timestamptz := pg_catalog.clock_timestamp();
|
|
292
|
+
global_count bigint;
|
|
293
|
+
scoped_count bigint;
|
|
294
|
+
BEGIN
|
|
295
|
+
IF p_registration_scope_hash !~ '^[0-9a-f]{64}$' THEN
|
|
296
|
+
RAISE EXCEPTION 'invalid MCP OAuth registration scope'
|
|
297
|
+
USING ERRCODE = '22023';
|
|
298
|
+
END IF;
|
|
299
|
+
|
|
300
|
+
-- One short global transaction lock makes the count-and-insert quota exact
|
|
301
|
+
-- across replicas. Registration is rare and the lock is held only for this
|
|
302
|
+
-- bounded cleanup/count/insert command.
|
|
303
|
+
PERFORM pg_catalog.pg_advisory_xact_lock(
|
|
304
|
+
pg_catalog.hashtextextended('opengeni:mcp-oauth:registration', 0)
|
|
305
|
+
);
|
|
306
|
+
PERFORM opengeni_private.reap_mcp_oauth_state(128);
|
|
307
|
+
|
|
308
|
+
SELECT count(*) INTO global_count
|
|
309
|
+
FROM mcp_oauth_clients
|
|
310
|
+
WHERE created_at > now_value - interval '10 minutes';
|
|
311
|
+
|
|
312
|
+
SELECT count(*) INTO scoped_count
|
|
313
|
+
FROM mcp_oauth_clients
|
|
314
|
+
WHERE registration_scope_hash = p_registration_scope_hash
|
|
315
|
+
AND created_at > now_value - interval '10 minutes';
|
|
316
|
+
|
|
317
|
+
IF global_count >= 600 OR scoped_count >= 20 THEN
|
|
318
|
+
RAISE EXCEPTION 'MCP OAuth client registration rate limited'
|
|
319
|
+
USING ERRCODE = 'P0004';
|
|
320
|
+
END IF;
|
|
321
|
+
|
|
322
|
+
RETURN QUERY
|
|
323
|
+
INSERT INTO mcp_oauth_clients (
|
|
324
|
+
client_id, redirect_uris, client_name, grant_types, response_types,
|
|
325
|
+
registration_scope_hash, expires_at
|
|
326
|
+
) VALUES (
|
|
327
|
+
p_client_id, p_redirect_uris, p_client_name, p_grant_types, p_response_types,
|
|
328
|
+
p_registration_scope_hash, now_value + interval '1 day'
|
|
329
|
+
)
|
|
330
|
+
RETURNING *;
|
|
331
|
+
END
|
|
332
|
+
$body$;
|
|
333
|
+
|
|
334
|
+
REVOKE ALL ON mcp_oauth_clients, mcp_oauth_authorization_requests,
|
|
335
|
+
mcp_oauth_authorization_codes, mcp_oauth_refresh_tokens, mcp_oauth_access_tokens FROM PUBLIC;
|
|
336
|
+
REVOKE ALL ON FUNCTION opengeni_private.reap_mcp_oauth_state(integer) FROM PUBLIC;
|
|
337
|
+
REVOKE ALL ON FUNCTION opengeni_private.register_mcp_oauth_client(
|
|
338
|
+
text, jsonb, text, jsonb, jsonb, text
|
|
339
|
+
) FROM PUBLIC;
|
|
340
|
+
|
|
341
|
+
-- Owner ALTER DEFAULT PRIVILEGES may still name a legacy application login or
|
|
342
|
+
-- an old side of a role rotation. The exact role list above is drain detection
|
|
343
|
+
-- only. Strip every explicit non-owner ACL from the new objects; the mandatory
|
|
344
|
+
-- post-migration db:provision-roles step grants only the deployment's current
|
|
345
|
+
-- target application role.
|
|
346
|
+
DO $mcp_oauth_table_acl_reset$
|
|
347
|
+
DECLARE
|
|
348
|
+
data_schema text := pg_catalog.current_schema();
|
|
349
|
+
relation_name text;
|
|
350
|
+
role_name text;
|
|
351
|
+
BEGIN
|
|
352
|
+
FOR relation_name IN
|
|
353
|
+
SELECT relation.value
|
|
354
|
+
FROM pg_catalog.unnest(
|
|
355
|
+
ARRAY[
|
|
356
|
+
'mcp_oauth_clients',
|
|
357
|
+
'mcp_oauth_authorization_requests',
|
|
358
|
+
'mcp_oauth_authorization_codes',
|
|
359
|
+
'mcp_oauth_refresh_tokens',
|
|
360
|
+
'mcp_oauth_access_tokens'
|
|
361
|
+
]::text[]
|
|
362
|
+
) AS relation(value)
|
|
363
|
+
LOOP
|
|
364
|
+
EXECUTE pg_catalog.format(
|
|
365
|
+
'REVOKE ALL ON TABLE %I.%I FROM PUBLIC',
|
|
366
|
+
data_schema,
|
|
367
|
+
relation_name
|
|
368
|
+
);
|
|
369
|
+
FOR role_name IN
|
|
370
|
+
SELECT grantee_role.rolname
|
|
371
|
+
FROM pg_catalog.pg_class relation
|
|
372
|
+
CROSS JOIN LATERAL pg_catalog.aclexplode(
|
|
373
|
+
coalesce(
|
|
374
|
+
relation.relacl,
|
|
375
|
+
pg_catalog.acldefault('r', relation.relowner)
|
|
376
|
+
)
|
|
377
|
+
) privilege
|
|
378
|
+
INNER JOIN pg_catalog.pg_roles grantee_role
|
|
379
|
+
ON grantee_role.oid = privilege.grantee
|
|
380
|
+
WHERE relation.oid = pg_catalog.to_regclass(
|
|
381
|
+
pg_catalog.format('%I.%I', data_schema, relation_name)
|
|
382
|
+
)
|
|
383
|
+
AND privilege.grantee <> 0
|
|
384
|
+
AND privilege.grantee <> relation.relowner
|
|
385
|
+
GROUP BY grantee_role.rolname
|
|
386
|
+
ORDER BY grantee_role.rolname COLLATE "C"
|
|
387
|
+
LOOP
|
|
388
|
+
EXECUTE pg_catalog.format(
|
|
389
|
+
'REVOKE ALL ON TABLE %I.%I FROM %I',
|
|
390
|
+
data_schema,
|
|
391
|
+
relation_name,
|
|
392
|
+
role_name
|
|
393
|
+
);
|
|
394
|
+
END LOOP;
|
|
395
|
+
END LOOP;
|
|
396
|
+
END
|
|
397
|
+
$mcp_oauth_table_acl_reset$;
|
|
398
|
+
|
|
399
|
+
DO $mcp_oauth_function_acl_reset$
|
|
400
|
+
DECLARE
|
|
401
|
+
role_name text;
|
|
402
|
+
target record;
|
|
403
|
+
BEGIN
|
|
404
|
+
FOR target IN
|
|
405
|
+
SELECT *
|
|
406
|
+
FROM (VALUES
|
|
407
|
+
(
|
|
408
|
+
'reap_mcp_oauth_state'::text,
|
|
409
|
+
'p_limit integer'::text,
|
|
410
|
+
'integer'::text
|
|
411
|
+
),
|
|
412
|
+
(
|
|
413
|
+
'register_mcp_oauth_client'::text,
|
|
414
|
+
'p_client_id text, p_redirect_uris jsonb, p_client_name text, p_grant_types jsonb, p_response_types jsonb, p_registration_scope_hash text'::text,
|
|
415
|
+
'text, jsonb, text, jsonb, jsonb, text'::text
|
|
416
|
+
)
|
|
417
|
+
) target_row(function_name, identity_arguments, call_arguments)
|
|
418
|
+
LOOP
|
|
419
|
+
FOR role_name IN
|
|
420
|
+
SELECT grantee_role.rolname
|
|
421
|
+
FROM pg_catalog.pg_proc procedure
|
|
422
|
+
INNER JOIN pg_catalog.pg_namespace namespace
|
|
423
|
+
ON namespace.oid = procedure.pronamespace
|
|
424
|
+
CROSS JOIN LATERAL pg_catalog.aclexplode(
|
|
425
|
+
coalesce(
|
|
426
|
+
procedure.proacl,
|
|
427
|
+
pg_catalog.acldefault('f', procedure.proowner)
|
|
428
|
+
)
|
|
429
|
+
) privilege
|
|
430
|
+
INNER JOIN pg_catalog.pg_roles grantee_role
|
|
431
|
+
ON grantee_role.oid = privilege.grantee
|
|
432
|
+
WHERE namespace.nspname = 'opengeni_private'
|
|
433
|
+
AND procedure.proname = target.function_name
|
|
434
|
+
AND pg_catalog.pg_get_function_identity_arguments(procedure.oid) =
|
|
435
|
+
target.identity_arguments
|
|
436
|
+
AND privilege.grantee <> 0
|
|
437
|
+
AND privilege.grantee <> procedure.proowner
|
|
438
|
+
GROUP BY grantee_role.rolname
|
|
439
|
+
ORDER BY grantee_role.rolname COLLATE "C"
|
|
440
|
+
LOOP
|
|
441
|
+
EXECUTE pg_catalog.format(
|
|
442
|
+
'REVOKE ALL ON FUNCTION opengeni_private.%I(%s) FROM %I',
|
|
443
|
+
target.function_name,
|
|
444
|
+
target.call_arguments,
|
|
445
|
+
role_name
|
|
446
|
+
);
|
|
447
|
+
END LOOP;
|
|
448
|
+
END LOOP;
|
|
449
|
+
END
|
|
450
|
+
$mcp_oauth_function_acl_reset$;
|
|
451
|
+
|
|
452
|
+
DO $mcp_oauth_runtime_drain_after$
|
|
453
|
+
DECLARE
|
|
454
|
+
configured_roles jsonb := current_setting(
|
|
455
|
+
'opengeni.migration_application_roles', false
|
|
456
|
+
)::jsonb;
|
|
457
|
+
BEGIN
|
|
458
|
+
IF EXISTS (
|
|
459
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
460
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
461
|
+
ON roles.role_name = activity.usename
|
|
462
|
+
WHERE activity.datname = current_database()
|
|
463
|
+
AND activity.pid <> pg_backend_pid()
|
|
464
|
+
)
|
|
465
|
+
THEN
|
|
466
|
+
RAISE EXCEPTION
|
|
467
|
+
'0403 MCP OAuth activation observed a configured OpenGeni application database session after schema installation'
|
|
468
|
+
USING ERRCODE = '55000';
|
|
469
|
+
END IF;
|
|
470
|
+
END
|
|
471
|
+
$mcp_oauth_runtime_drain_after$;
|