@opengeni/db 1.0.2 → 1.2.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/artifact-catalog.d.ts +12 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-reconciliation.js +2 -2
- package/dist/{chunk-KH3TYDGY.js → chunk-3GAR5UN3.js} +2 -2
- package/dist/{chunk-UEQQ2WWU.js → chunk-3TGYFFZR.js} +3 -26
- package/dist/chunk-3TGYFFZR.js.map +1 -0
- package/dist/{chunk-F3YMVRF4.js → chunk-6EIEGHPK.js} +4 -4
- package/dist/{chunk-OQZOGCLG.js → chunk-CONWNUSL.js} +2 -2
- package/dist/{chunk-7S53WLPI.js → chunk-CYP72JTQ.js} +11 -4
- package/dist/{chunk-7S53WLPI.js.map → chunk-CYP72JTQ.js.map} +1 -1
- package/dist/{chunk-LDTQGYD2.js → chunk-FWQNHNOU.js} +54 -2
- package/dist/chunk-FWQNHNOU.js.map +1 -0
- package/dist/{chunk-KEDWSSNP.js → chunk-GDN5TSZP.js} +2 -2
- package/dist/{chunk-PQGTWFRF.js → chunk-JICUP7ZR.js} +2 -2
- package/dist/{chunk-NLSG7GGA.js → chunk-JLVWBIEM.js} +3 -3
- package/dist/{chunk-Y77PVZZD.js → chunk-LXPFNG4L.js} +2 -2
- package/dist/{chunk-56MYWKQV.js → chunk-O2Y5E5M3.js} +68 -32
- package/dist/chunk-O2Y5E5M3.js.map +1 -0
- package/dist/{chunk-7POTLB24.js → chunk-QFJNEXFL.js} +2 -2
- package/dist/{chunk-MACELCXG.js → chunk-UCIMHNII.js} +101 -21
- package/dist/chunk-UCIMHNII.js.map +1 -0
- package/dist/{chunk-OMHBVHX7.js → chunk-VDPCIFKL.js} +5 -5
- package/dist/{chunk-N5QCKHYL.js → chunk-YOYILDLY.js} +87 -19
- package/dist/chunk-YOYILDLY.js.map +1 -0
- package/dist/connect-attempts.js +3 -3
- package/dist/credit-balances.d.ts +18 -0
- package/dist/credit-promotion-policy.d.ts +10 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +35 -10
- package/dist/index.js +2432 -2255
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/managed-user-setup.d.ts +1 -0
- package/dist/mcp-operations.js +2 -2
- package/dist/organization-integration-policy.js +3 -3
- package/dist/organization-membership-lifecycle.d.ts +1 -0
- package/dist/persistence-errors.d.ts +9 -6
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +7 -6
- package/dist/schema.d.ts +214 -2
- package/dist/schema.js +5 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +5 -5
- package/dist/session-event-slices.js +3 -3
- package/dist/session-mcp-credential-rotation.js +5 -5
- package/dist/session-tenancy.d.ts +6 -1
- package/dist/session-tenancy.js +3 -5
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0610_artifact_catalog_pins.sql +165 -0
- package/drizzle/0611_universal_session_tenancy_activation.sql +166 -0
- package/drizzle/0612_scheduled_model_unavailable_refusal.sql +21 -0
- package/drizzle/0613_model_scoped_promotional_credits.sql +238 -0
- package/drizzle/0614_quiescence_command_containment.sql +7 -0
- package/drizzle/0615_credit_promotion_policy_validation.sql +63 -0
- package/package.json +7 -7
- package/src/artifact-catalog.ts +31 -5
- package/src/credit-balances.ts +109 -0
- package/src/credit-promotion-policy.ts +12 -0
- package/src/database.ts +81 -42
- package/src/index.ts +295 -79
- package/src/managed-user-setup.ts +8 -0
- package/src/organization-membership-lifecycle.ts +8 -0
- package/src/persistence-errors.ts +17 -6
- package/src/provision-roles.ts +25 -0
- package/src/runtime-posture-cli.ts +0 -2
- package/src/runtime-posture.ts +86 -27
- package/src/schema.ts +57 -1
- package/src/session-tenancy.ts +9 -31
- package/src/video-generation.ts +2 -7
- package/dist/chunk-56MYWKQV.js.map +0 -1
- package/dist/chunk-LDTQGYD2.js.map +0 -1
- package/dist/chunk-MACELCXG.js.map +0 -1
- package/dist/chunk-N5QCKHYL.js.map +0 -1
- package/dist/chunk-UEQQ2WWU.js.map +0 -1
- /package/dist/{chunk-KH3TYDGY.js.map → chunk-3GAR5UN3.js.map} +0 -0
- /package/dist/{chunk-F3YMVRF4.js.map → chunk-6EIEGHPK.js.map} +0 -0
- /package/dist/{chunk-OQZOGCLG.js.map → chunk-CONWNUSL.js.map} +0 -0
- /package/dist/{chunk-KEDWSSNP.js.map → chunk-GDN5TSZP.js.map} +0 -0
- /package/dist/{chunk-PQGTWFRF.js.map → chunk-JICUP7ZR.js.map} +0 -0
- /package/dist/{chunk-NLSG7GGA.js.map → chunk-JLVWBIEM.js.map} +0 -0
- /package/dist/{chunk-Y77PVZZD.js.map → chunk-LXPFNG4L.js.map} +0 -0
- /package/dist/{chunk-7POTLB24.js.map → chunk-QFJNEXFL.js.map} +0 -0
- /package/dist/{chunk-OMHBVHX7.js.map → chunk-VDPCIFKL.js.map} +0 -0
|
@@ -6,6 +6,12 @@ export declare class SessionTenancyConflictError extends Error {
|
|
|
6
6
|
readonly name = "SessionTenancyConflictError";
|
|
7
7
|
constructor(reason: "not_quiescent" | "authority_epoch" | "operation_reuse", blocker?: SessionTenancyBlocker | null, options?: ErrorOptions);
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The organization's owner/admin Only-me setting does not permit a new private
|
|
11
|
+
* session (or private transition) in this shared workspace. Activation itself is
|
|
12
|
+
* universal since migration 0611; the name and the SESSION_TENANCY_NOT_ACTIVATED
|
|
13
|
+
* wire code are kept for client compatibility.
|
|
14
|
+
*/
|
|
9
15
|
export declare class SessionTenancyNotActivatedError extends Error {
|
|
10
16
|
readonly name = "SessionTenancyNotActivatedError";
|
|
11
17
|
constructor(options?: ErrorOptions);
|
|
@@ -83,7 +89,6 @@ export type ForkSessionContentResult = {
|
|
|
83
89
|
runtimeEventId: string | null;
|
|
84
90
|
runtimeEventSequence: number | null;
|
|
85
91
|
};
|
|
86
|
-
export declare function sessionTenancyProductActivated(db: Database, workspaceId: string): Promise<boolean>;
|
|
87
92
|
export declare function getPrivateSessionCreatePolicy(db: Database, input: {
|
|
88
93
|
workspaceId: string;
|
|
89
94
|
actorSubjectId: string;
|
package/dist/session-tenancy.js
CHANGED
|
@@ -12,11 +12,10 @@ import {
|
|
|
12
12
|
openPrivateChildSessionCreateCapability,
|
|
13
13
|
openPrivateSessionCreateCapability,
|
|
14
14
|
replayAppliedSessionFork,
|
|
15
|
-
sessionTenancyProductActivated,
|
|
16
15
|
transitionSessionVisibility
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-3TGYFFZR.js";
|
|
17
|
+
import "./chunk-O2Y5E5M3.js";
|
|
18
|
+
import "./chunk-FWQNHNOU.js";
|
|
20
19
|
import "./chunk-P7YDM2RI.js";
|
|
21
20
|
import "./chunk-PZ5AY32C.js";
|
|
22
21
|
export {
|
|
@@ -33,7 +32,6 @@ export {
|
|
|
33
32
|
openPrivateChildSessionCreateCapability,
|
|
34
33
|
openPrivateSessionCreateCapability,
|
|
35
34
|
replayAppliedSessionFork,
|
|
36
|
-
sessionTenancyProductActivated,
|
|
37
35
|
transitionSessionVisibility
|
|
38
36
|
};
|
|
39
37
|
//# sourceMappingURL=session-tenancy.js.map
|
package/dist/video-generation.js
CHANGED
|
@@ -29,10 +29,10 @@ import {
|
|
|
29
29
|
settleVideoGenerationFailure,
|
|
30
30
|
settleVideoGenerationReady,
|
|
31
31
|
updateWorkspaceVideoGenerationPolicy
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-UCIMHNII.js";
|
|
33
33
|
import "./chunk-4UJCXBR2.js";
|
|
34
|
-
import "./chunk-
|
|
35
|
-
import "./chunk-
|
|
34
|
+
import "./chunk-O2Y5E5M3.js";
|
|
35
|
+
import "./chunk-FWQNHNOU.js";
|
|
36
36
|
import "./chunk-P7YDM2RI.js";
|
|
37
37
|
import "./chunk-PZ5AY32C.js";
|
|
38
38
|
export {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lockWorkspaceInferenceControl
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-CYP72JTQ.js";
|
|
4
|
+
import "./chunk-UCIMHNII.js";
|
|
5
5
|
import "./chunk-4UJCXBR2.js";
|
|
6
6
|
import {
|
|
7
7
|
withWorkspaceRls
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-O2Y5E5M3.js";
|
|
9
|
+
import "./chunk-FWQNHNOU.js";
|
|
10
10
|
import "./chunk-P7YDM2RI.js";
|
|
11
11
|
import "./chunk-PZ5AY32C.js";
|
|
12
12
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Shared discovery metadata only. Existing content and authorization domains
|
|
3
|
+
-- remain canonical; older binaries keep their original publication capability.
|
|
4
|
+
SET LOCAL lock_timeout = '5s';
|
|
5
|
+
SET LOCAL statement_timeout = '5min';
|
|
6
|
+
|
|
7
|
+
CREATE TABLE opengeni_private.artifact_catalog_pins (
|
|
8
|
+
account_id uuid NOT NULL,
|
|
9
|
+
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
10
|
+
kind text NOT NULL CHECK (kind IN ('site','document','spreadsheet','presentation','image','file')),
|
|
11
|
+
artifact_id text NOT NULL CHECK (length(artifact_id) BETWEEN 1 AND 128),
|
|
12
|
+
pinned_at timestamptz NOT NULL DEFAULT now(),
|
|
13
|
+
PRIMARY KEY (account_id, workspace_id, kind, artifact_id)
|
|
14
|
+
);
|
|
15
|
+
ALTER TABLE opengeni_private.artifact_catalog_pins ENABLE ROW LEVEL SECURITY;
|
|
16
|
+
ALTER TABLE opengeni_private.artifact_catalog_pins FORCE ROW LEVEL SECURITY;
|
|
17
|
+
CREATE POLICY workspace_isolation ON opengeni_private.artifact_catalog_pins
|
|
18
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
19
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
20
|
+
|
|
21
|
+
CREATE FUNCTION opengeni_private.update_artifact_pin(
|
|
22
|
+
p_account uuid, p_workspace uuid, p_kind text, p_id text, p_pinned boolean
|
|
23
|
+
) RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $body$
|
|
24
|
+
DECLARE v_visible boolean := false;
|
|
25
|
+
BEGIN
|
|
26
|
+
IF p_account IS NULL OR p_workspace IS NULL OR p_kind IS NULL OR p_id IS NULL OR p_pinned IS NULL
|
|
27
|
+
OR p_account IS DISTINCT FROM opengeni_private.current_account_id()
|
|
28
|
+
OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
29
|
+
OR p_kind NOT IN ('site','document','spreadsheet','presentation','image','file')
|
|
30
|
+
OR length(p_id) NOT BETWEEN 1 AND 128
|
|
31
|
+
THEN RAISE EXCEPTION 'artifact pin scope mismatch' USING ERRCODE = '42501'; END IF;
|
|
32
|
+
-- Serialize opposite writes even before the first row exists.
|
|
33
|
+
PERFORM pg_advisory_xact_lock(hashtextextended('artifact-pin:' || p_account || ':' || p_workspace || ':' || p_kind || ':' || p_id, 0));
|
|
34
|
+
IF p_kind = 'site' THEN
|
|
35
|
+
SELECT EXISTS (SELECT 1 FROM workspace_artifacts a WHERE a.account_id=p_account
|
|
36
|
+
AND a.workspace_id=p_workspace AND a.id::text=p_id) INTO v_visible;
|
|
37
|
+
ELSIF p_kind IN ('document','spreadsheet','presentation') THEN
|
|
38
|
+
SELECT EXISTS (SELECT 1 FROM editable_artifacts a WHERE a.account_id=p_account
|
|
39
|
+
AND a.workspace_id=p_workspace AND a.id=p_id AND a.modality=p_kind) INTO v_visible;
|
|
40
|
+
ELSE
|
|
41
|
+
SELECT EXISTS (SELECT 1 FROM files f WHERE f.account_id=p_account AND f.workspace_id=p_workspace
|
|
42
|
+
AND f.id::text=p_id AND f.status='ready'
|
|
43
|
+
AND (f.private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(f.private_owner_subject_ids))
|
|
44
|
+
AND ((p_kind='image' AND EXISTS (SELECT 1 FROM generated_image_artifacts g
|
|
45
|
+
WHERE g.account_id=p_account AND g.workspace_id=p_workspace AND g.artifact_id=f.id AND g.status='ready'))
|
|
46
|
+
OR (EXISTS (SELECT 1 FROM opengeni_private.sandbox_file_publications p
|
|
47
|
+
WHERE p.account_id=p_account AND p.workspace_id=p_workspace AND p.file_id=f.id)
|
|
48
|
+
AND p_kind=CASE WHEN f.content_type IN ('image/png','image/jpeg','image/gif','image/webp','image/avif','image/svg+xml')
|
|
49
|
+
THEN 'image' ELSE 'file' END))) INTO v_visible;
|
|
50
|
+
END IF;
|
|
51
|
+
-- Also validate unpin: no probe or write for missing, foreign or hidden targets.
|
|
52
|
+
IF NOT v_visible THEN RAISE EXCEPTION 'artifact pin target unavailable' USING ERRCODE = '42501'; END IF;
|
|
53
|
+
IF p_pinned THEN
|
|
54
|
+
INSERT INTO opengeni_private.artifact_catalog_pins(account_id,workspace_id,kind,artifact_id)
|
|
55
|
+
VALUES(p_account,p_workspace,p_kind,p_id) ON CONFLICT DO NOTHING;
|
|
56
|
+
ELSE
|
|
57
|
+
DELETE FROM opengeni_private.artifact_catalog_pins p WHERE p.account_id=p_account
|
|
58
|
+
AND p.workspace_id=p_workspace AND p.kind=p_kind AND p.artifact_id=p_id;
|
|
59
|
+
END IF;
|
|
60
|
+
END $body$;
|
|
61
|
+
|
|
62
|
+
CREATE FUNCTION opengeni_private.list_artifact_pins(p_account uuid, p_workspace uuid)
|
|
63
|
+
RETURNS TABLE(kind text, artifact_id text)
|
|
64
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $body$
|
|
65
|
+
BEGIN
|
|
66
|
+
IF p_account IS NULL OR p_workspace IS NULL
|
|
67
|
+
OR p_account IS DISTINCT FROM opengeni_private.current_account_id()
|
|
68
|
+
OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
69
|
+
THEN RAISE EXCEPTION 'artifact pin scope mismatch' USING ERRCODE = '42501'; END IF;
|
|
70
|
+
-- Only joined to independently authorized candidates. Private files remain
|
|
71
|
+
-- hidden even when the migration owner is a superuser rather than FORCE-RLS-bound.
|
|
72
|
+
RETURN QUERY SELECT p.kind,p.artifact_id FROM opengeni_private.artifact_catalog_pins p
|
|
73
|
+
WHERE p.account_id=p_account AND p.workspace_id=p_workspace
|
|
74
|
+
AND (p.kind NOT IN ('file','image') OR EXISTS (SELECT 1 FROM files f
|
|
75
|
+
WHERE f.account_id=p_account AND f.workspace_id=p_workspace AND f.id::text=p.artifact_id AND f.status='ready'
|
|
76
|
+
AND (f.private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(f.private_owner_subject_ids))));
|
|
77
|
+
END $body$;
|
|
78
|
+
|
|
79
|
+
-- A page-local re-sort is incorrect: this bounded branch must apply the same
|
|
80
|
+
-- leading pin key and frontier as the complete union before taking its LIMIT.
|
|
81
|
+
CREATE FUNCTION opengeni_private.list_sandbox_file_publications_pinned(
|
|
82
|
+
p_account uuid, p_workspace uuid, p_query jsonb
|
|
83
|
+
) RETURNS TABLE(file_id uuid, title text, kind text, source_session_id uuid, published_at timestamptz, pinned boolean)
|
|
84
|
+
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $body$
|
|
85
|
+
DECLARE
|
|
86
|
+
v_limit integer := (p_query->>'limit')::integer;
|
|
87
|
+
v_sort text := p_query->>'sort';
|
|
88
|
+
v_after text := p_query#>>'{after,key}';
|
|
89
|
+
v_after_pinned boolean := coalesce((p_query#>>'{after,pinned}')::boolean,false);
|
|
90
|
+
BEGIN
|
|
91
|
+
IF p_account IS NULL OR p_workspace IS NULL
|
|
92
|
+
OR p_account IS DISTINCT FROM opengeni_private.current_account_id()
|
|
93
|
+
OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
94
|
+
OR v_limit IS NULL OR v_limit NOT BETWEEN 1 AND 201
|
|
95
|
+
OR v_sort IS NULL OR v_sort NOT IN ('updated','newest','title')
|
|
96
|
+
OR jsonb_typeof(p_query) IS DISTINCT FROM 'object'
|
|
97
|
+
OR jsonb_typeof(p_query->'kinds') IS DISTINCT FROM 'array'
|
|
98
|
+
OR jsonb_array_length(p_query->'kinds') NOT BETWEEN 1 AND 2
|
|
99
|
+
OR length(coalesce(p_query->>'q',''))>200
|
|
100
|
+
THEN RAISE EXCEPTION 'publication list scope mismatch' USING ERRCODE = '42501'; END IF;
|
|
101
|
+
RETURN QUERY WITH candidates AS (
|
|
102
|
+
SELECT p.file_id, f.filename AS title,
|
|
103
|
+
CASE WHEN f.content_type IN ('image/png','image/jpeg','image/gif','image/webp','image/avif','image/svg+xml')
|
|
104
|
+
THEN 'image' ELSE 'file' END AS kind,
|
|
105
|
+
p.source_session_id, p.published_at,
|
|
106
|
+
CASE WHEN v_sort='title' THEN lower(f.filename)
|
|
107
|
+
ELSE to_char(p.published_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS.US') END AS sort_key
|
|
108
|
+
FROM opengeni_private.sandbox_file_publications p JOIN files f
|
|
109
|
+
ON f.account_id=p.account_id AND f.workspace_id=p.workspace_id AND f.id=p.file_id
|
|
110
|
+
WHERE p.account_id=p_account AND p.workspace_id=p_workspace AND f.status='ready'
|
|
111
|
+
AND (f.private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(f.private_owner_subject_ids))
|
|
112
|
+
AND (p_query->>'sourceSessionId' IS NULL OR p.source_session_id=(p_query->>'sourceSessionId')::uuid)
|
|
113
|
+
AND p.published_at<=(p_query->>'snapshotAt')::timestamptz
|
|
114
|
+
AND strpos(lower(f.filename),lower(coalesce(p_query->>'q','')))>0
|
|
115
|
+
), ordered AS (
|
|
116
|
+
SELECT c.*, (pins.artifact_id IS NOT NULL) AS pinned FROM candidates c
|
|
117
|
+
LEFT JOIN opengeni_private.list_artifact_pins(p_account,p_workspace) pins
|
|
118
|
+
ON pins.kind=c.kind AND pins.artifact_id=c.file_id::text
|
|
119
|
+
) SELECT c.file_id,c.title,c.kind,c.source_session_id,c.published_at,c.pinned FROM ordered c
|
|
120
|
+
WHERE (p_query->'kinds') ? c.kind AND (v_after IS NULL OR c.pinned<v_after_pinned OR
|
|
121
|
+
(c.pinned=v_after_pinned AND (
|
|
122
|
+
(v_sort='title' AND c.sort_key COLLATE "C">v_after COLLATE "C") OR
|
|
123
|
+
(v_sort<>'title' AND c.sort_key COLLATE "C"<v_after COLLATE "C") OR
|
|
124
|
+
(c.sort_key=v_after AND (c.kind COLLATE "C",c.file_id::text COLLATE "C")>
|
|
125
|
+
((p_query#>>'{after,kind}') COLLATE "C",(p_query#>>'{after,id}') COLLATE "C")))))
|
|
126
|
+
ORDER BY c.pinned DESC, CASE WHEN v_sort='title' THEN c.sort_key END COLLATE "C" ASC,
|
|
127
|
+
CASE WHEN v_sort<>'title' THEN c.sort_key END COLLATE "C" DESC,c.kind COLLATE "C",c.file_id::text COLLATE "C"
|
|
128
|
+
LIMIT v_limit;
|
|
129
|
+
END $body$;
|
|
130
|
+
REVOKE ALL ON TABLE opengeni_private.artifact_catalog_pins FROM PUBLIC;
|
|
131
|
+
REVOKE ALL ON FUNCTION opengeni_private.update_artifact_pin(uuid,uuid,text,text,boolean),
|
|
132
|
+
opengeni_private.list_artifact_pins(uuid,uuid),
|
|
133
|
+
opengeni_private.list_sandbox_file_publications_pinned(uuid,uuid,jsonb) FROM PUBLIC;
|
|
134
|
+
|
|
135
|
+
DO $grants$
|
|
136
|
+
DECLARE target_schema text := current_schema(); recipient record; routine text;
|
|
137
|
+
BEGIN
|
|
138
|
+
FOREACH routine IN ARRAY ARRAY['update_artifact_pin(uuid,uuid,text,text,boolean)',
|
|
139
|
+
'list_artifact_pins(uuid,uuid)','list_sandbox_file_publications_pinned(uuid,uuid,jsonb)']
|
|
140
|
+
LOOP
|
|
141
|
+
EXECUTE format('ALTER FUNCTION opengeni_private.%s SET search_path = pg_catalog, %I, pg_temp', routine, target_schema);
|
|
142
|
+
-- Strip inherited default function ACLs, then grant only existing runtimes.
|
|
143
|
+
FOR recipient IN SELECT DISTINCT acl.grantee,r.rolname FROM pg_proc p
|
|
144
|
+
CROSS JOIN LATERAL aclexplode(p.proacl) acl LEFT JOIN pg_roles r ON r.oid=acl.grantee
|
|
145
|
+
WHERE p.oid=to_regprocedure('opengeni_private.' || routine) AND acl.grantee<>p.proowner
|
|
146
|
+
LOOP
|
|
147
|
+
EXECUTE format('REVOKE ALL ON FUNCTION opengeni_private.%s FROM %s', routine,
|
|
148
|
+
CASE WHEN recipient.grantee=0 THEN 'PUBLIC' ELSE quote_ident(recipient.rolname) END);
|
|
149
|
+
END LOOP;
|
|
150
|
+
END LOOP;
|
|
151
|
+
FOR recipient IN SELECT DISTINCT acl.grantee,r.rolname FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
|
|
152
|
+
CROSS JOIN LATERAL aclexplode(c.relacl) acl LEFT JOIN pg_roles r ON r.oid=acl.grantee
|
|
153
|
+
WHERE n.nspname='opengeni_private' AND c.relname='artifact_catalog_pins' AND acl.grantee<>c.relowner
|
|
154
|
+
LOOP
|
|
155
|
+
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.artifact_catalog_pins FROM %s',
|
|
156
|
+
CASE WHEN recipient.grantee=0 THEN 'PUBLIC' ELSE quote_ident(recipient.rolname) END);
|
|
157
|
+
END LOOP;
|
|
158
|
+
FOR recipient IN SELECT DISTINCT r.rolname FROM pg_proc p
|
|
159
|
+
CROSS JOIN LATERAL aclexplode(p.proacl) acl JOIN pg_roles r ON r.oid=acl.grantee
|
|
160
|
+
WHERE p.oid=to_regprocedure('opengeni_private.record_sandbox_file_publication(uuid,uuid,uuid,uuid)')
|
|
161
|
+
AND acl.privilege_type='EXECUTE' AND acl.grantee<>p.proowner
|
|
162
|
+
LOOP
|
|
163
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION opengeni_private.update_artifact_pin(uuid,uuid,text,text,boolean), opengeni_private.list_artifact_pins(uuid,uuid), opengeni_private.list_sandbox_file_publications_pinned(uuid,uuid,jsonb) TO %I', recipient.rolname);
|
|
164
|
+
END LOOP;
|
|
165
|
+
END $grants$;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Retire per-organization session-tenancy activation. Every organization is
|
|
3
|
+
-- activated: private ("Only me") sessions, visibility transitions, forks and
|
|
4
|
+
-- personal-resource grants no longer require a version-1
|
|
5
|
+
-- session_tenancy_activations receipt, and the owner/admin Only-me setting
|
|
6
|
+
-- (organization_private_session_settings) now defaults to ENABLED when an
|
|
7
|
+
-- organization has no row. Owners/admins may still disable it; an explicit
|
|
8
|
+
-- disabled row keeps gating new Only-me chats in shared workspaces.
|
|
9
|
+
--
|
|
10
|
+
-- No receipts are written and no data is backfilled. The receipt keeps exactly
|
|
11
|
+
-- one meaning: opengeni_private.session_tenancy_account_activated (0340) still
|
|
12
|
+
-- retires the legacy_user connection and unattributed-writer compatibility
|
|
13
|
+
-- lanes only for organizations that already hold one. Organizations without a
|
|
14
|
+
-- receipt keep those lanes exactly as before this migration, so no stored
|
|
15
|
+
-- authority becomes invisible and no ownership is inferred.
|
|
16
|
+
--
|
|
17
|
+
-- Rolling: this only widens product predicates and flips a missing-row default.
|
|
18
|
+
-- A pre-0611 binary keeps working against the rewritten routines: its own
|
|
19
|
+
-- receipt probe (session_tenancy_product_activated) now answers true, its
|
|
20
|
+
-- startup interlock probe (session_tenancy_any_product_activation) answers
|
|
21
|
+
-- false regardless of the retired environment switch, and every routine keeps
|
|
22
|
+
-- its signature, owner, ACL, SECURITY DEFINER and search_path. No table is
|
|
23
|
+
-- locked or rewritten.
|
|
24
|
+
SET LOCAL lock_timeout = '5s';
|
|
25
|
+
SET LOCAL statement_timeout = '5min';
|
|
26
|
+
|
|
27
|
+
-- Remove the receipt prerequisite from every runtime predicate and default the
|
|
28
|
+
-- Only-me setting to enabled. Each rewrite replaces one exact, reviewed
|
|
29
|
+
-- fragment of the live definition; a changed shape aborts instead of silently
|
|
30
|
+
-- leaving a receipt gate behind. Owner, grants, SECURITY DEFINER and
|
|
31
|
+
-- search_path are preserved by CREATE OR REPLACE of the catalog's own
|
|
32
|
+
-- definition.
|
|
33
|
+
DO $universal_session_tenancy_predicates$
|
|
34
|
+
DECLARE
|
|
35
|
+
data_schema text := pg_catalog.current_schema();
|
|
36
|
+
target regprocedure;
|
|
37
|
+
definition text;
|
|
38
|
+
rewrite record;
|
|
39
|
+
occurrences integer;
|
|
40
|
+
-- Fragments below are routine SOURCE text matched against
|
|
41
|
+
-- pg_get_functiondef, never queries: this block reads only the catalog.
|
|
42
|
+
-- The table names are spliced in so the source is not mistaken for a
|
|
43
|
+
-- migration-time row read of a FORCE-RLS table.
|
|
44
|
+
receipts constant text := 'session_tenancy_activations';
|
|
45
|
+
settings constant text := 'organization_private_session_settings';
|
|
46
|
+
receipt_gate constant text :=
|
|
47
|
+
E' IF NOT EXISTS (\n'
|
|
48
|
+
|| E' SELECT 1 FROM ' || receipts || E' activation\n'
|
|
49
|
+
|| E' WHERE activation.account_id = p_account_id AND activation.activation_version = 1\n'
|
|
50
|
+
|| E' ) THEN\n'
|
|
51
|
+
|| E' RAISE EXCEPTION ''session tenancy product is not activated'' USING ERRCODE = ''42501'';\n'
|
|
52
|
+
|| E' END IF;\n';
|
|
53
|
+
receipt_gate_removed constant text :=
|
|
54
|
+
E' -- 0611: every organization is session-tenancy activated.\n';
|
|
55
|
+
setting_receipt_gate constant text :=
|
|
56
|
+
E' IF p_enabled AND NOT session_tenancy_product_activated(p_account_id, 1) THEN\n'
|
|
57
|
+
|| E' RAISE EXCEPTION ''session tenancy product surface is not available for this organization'' USING ERRCODE = ''55000'';\n'
|
|
58
|
+
|| E' END IF;\n';
|
|
59
|
+
BEGIN
|
|
60
|
+
FOR rewrite IN
|
|
61
|
+
SELECT * FROM (VALUES
|
|
62
|
+
(1, pg_catalog.format('%I.session_tenancy_product_activated(uuid,integer)', data_schema),
|
|
63
|
+
E' AND EXISTS (\n'
|
|
64
|
+
|| E' SELECT 1 FROM ' || receipts || E' activation\n'
|
|
65
|
+
|| E' WHERE activation.account_id = p_account_id\n'
|
|
66
|
+
|| E' AND activation.activation_version = p_activation_version\n'
|
|
67
|
+
|| E' )',
|
|
68
|
+
E' AND p_account_id IS NOT NULL\n'
|
|
69
|
+
|| E' AND p_activation_version IS NOT DISTINCT FROM 1'),
|
|
70
|
+
-- The value-free startup interlock is retired with the deployment switch.
|
|
71
|
+
-- Answering false keeps every earlier binary startable regardless of the
|
|
72
|
+
-- removed OPENGENI_ORGANIZATION_TENANCY_CANONICAL_ACTIVATION_ENABLED value.
|
|
73
|
+
(3, pg_catalog.format('%I.session_tenancy_any_product_activation()', data_schema),
|
|
74
|
+
E' SELECT EXISTS (\n'
|
|
75
|
+
|| E' SELECT 1 FROM ' || receipts || E'\n'
|
|
76
|
+
|| E' ) INTO activated;',
|
|
77
|
+
E' activated := false;'),
|
|
78
|
+
(4, pg_catalog.format(
|
|
79
|
+
'%I.list_self_user_resource_authorities(uuid,uuid,text,uuid,integer)', data_schema),
|
|
80
|
+
receipt_gate, receipt_gate_removed),
|
|
81
|
+
(5, pg_catalog.format(
|
|
82
|
+
'%I.issue_self_user_resource_grant(uuid,uuid,uuid,text,text,text,uuid,integer,boolean)',
|
|
83
|
+
data_schema),
|
|
84
|
+
receipt_gate, receipt_gate_removed),
|
|
85
|
+
(6, pg_catalog.format('%I.revoke_self_user_resource_grant(uuid,uuid,uuid)', data_schema),
|
|
86
|
+
receipt_gate, receipt_gate_removed),
|
|
87
|
+
(7, pg_catalog.format(
|
|
88
|
+
'%I.accept_turn_personal_resource_attachment(uuid,uuid,uuid,uuid,text,integer,boolean,integer)',
|
|
89
|
+
data_schema),
|
|
90
|
+
receipt_gate, receipt_gate_removed),
|
|
91
|
+
-- Only-me chats default to enabled: only an explicit disabled row gates.
|
|
92
|
+
(8, pg_catalog.format('%I.organization_private_sessions_enabled(uuid)', data_schema),
|
|
93
|
+
E' AND session_tenancy_product_activated(p_account_id, 1)\n'
|
|
94
|
+
|| E' AND EXISTS (\n'
|
|
95
|
+
|| E' SELECT 1 FROM ' || settings || E' setting\n'
|
|
96
|
+
|| E' WHERE setting.account_id = p_account_id AND setting.enabled\n'
|
|
97
|
+
|| E' )',
|
|
98
|
+
E' AND NOT EXISTS (\n'
|
|
99
|
+
|| E' SELECT 1 FROM ' || settings || E' setting\n'
|
|
100
|
+
|| E' WHERE setting.account_id = p_account_id AND NOT setting.enabled\n'
|
|
101
|
+
|| E' )'),
|
|
102
|
+
(9, pg_catalog.format(
|
|
103
|
+
'%I.get_organization_private_session_settings(uuid,text)', data_schema),
|
|
104
|
+
E' ''enabled'', coalesce(setting.enabled, false),\n',
|
|
105
|
+
E' ''enabled'', coalesce(setting.enabled, true),\n'),
|
|
106
|
+
(10, pg_catalog.format(
|
|
107
|
+
'%I.update_organization_private_session_settings(uuid,text,boolean,bigint,uuid)',
|
|
108
|
+
data_schema),
|
|
109
|
+
setting_receipt_gate, ''),
|
|
110
|
+
(11, pg_catalog.format(
|
|
111
|
+
'%I.update_organization_private_session_settings(uuid,text,boolean,bigint,uuid)',
|
|
112
|
+
data_schema),
|
|
113
|
+
E' did_change := coalesce(setting.enabled, false) IS DISTINCT FROM p_enabled;\n',
|
|
114
|
+
E' did_change := coalesce(setting.enabled, true) IS DISTINCT FROM p_enabled;\n')
|
|
115
|
+
) AS rewrites(ordinal, signature, old_fragment, new_fragment)
|
|
116
|
+
ORDER BY ordinal
|
|
117
|
+
LOOP
|
|
118
|
+
target := pg_catalog.to_regprocedure(rewrite.signature);
|
|
119
|
+
IF target IS NULL THEN
|
|
120
|
+
RAISE EXCEPTION 'universal session tenancy predicate is missing: %', rewrite.signature
|
|
121
|
+
USING ERRCODE = '55000';
|
|
122
|
+
END IF;
|
|
123
|
+
definition := pg_catalog.pg_get_functiondef(target);
|
|
124
|
+
occurrences := (
|
|
125
|
+
pg_catalog.length(definition)
|
|
126
|
+
- pg_catalog.length(pg_catalog.replace(definition, rewrite.old_fragment, ''))
|
|
127
|
+
) / pg_catalog.length(rewrite.old_fragment);
|
|
128
|
+
IF occurrences <> 1 THEN
|
|
129
|
+
RAISE EXCEPTION 'universal session tenancy predicate shape changed: % (% matches)',
|
|
130
|
+
rewrite.signature, occurrences USING ERRCODE = '55000';
|
|
131
|
+
END IF;
|
|
132
|
+
EXECUTE pg_catalog.replace(definition, rewrite.old_fragment, rewrite.new_fragment);
|
|
133
|
+
END LOOP;
|
|
134
|
+
|
|
135
|
+
-- Fail closed if any other routine still consults the receipt table. Only
|
|
136
|
+
-- the retained, now-dormant receipt writers (operator, greenfield and
|
|
137
|
+
-- additional-organization activation, plus the operator preference
|
|
138
|
+
-- enabler's lock) and the private legacy-lane retirement predicate may.
|
|
139
|
+
-- That predicate is not a product prerequisite: it closes the legacy_user
|
|
140
|
+
-- connection and unattributed-writer compatibility lanes only for an
|
|
141
|
+
-- organization that already holds a receipt.
|
|
142
|
+
IF EXISTS (
|
|
143
|
+
SELECT 1 FROM pg_catalog.pg_proc routine
|
|
144
|
+
JOIN pg_catalog.pg_namespace namespace ON namespace.oid = routine.pronamespace
|
|
145
|
+
WHERE namespace.nspname IN (data_schema, 'opengeni_private')
|
|
146
|
+
AND routine.prosrc LIKE '%session_tenancy_activations%'
|
|
147
|
+
AND routine.proname NOT IN (
|
|
148
|
+
'activate_session_tenancy_product',
|
|
149
|
+
'activate_greenfield_session_tenancy_from_setup',
|
|
150
|
+
'activate_session_tenancy_from_additional_organization',
|
|
151
|
+
'enable_organization_private_sessions_from_activation',
|
|
152
|
+
'session_tenancy_account_activated'
|
|
153
|
+
)
|
|
154
|
+
) THEN
|
|
155
|
+
RAISE EXCEPTION 'a runtime routine still requires a session tenancy activation receipt'
|
|
156
|
+
USING ERRCODE = '55000';
|
|
157
|
+
END IF;
|
|
158
|
+
END
|
|
159
|
+
$universal_session_tenancy_predicates$;
|
|
160
|
+
|
|
161
|
+
COMMENT ON FUNCTION session_tenancy_product_activated(uuid, integer) IS
|
|
162
|
+
'Universal since 0611: true for the transaction''s own organization at activation version 1. No per-organization receipt is required.';
|
|
163
|
+
COMMENT ON FUNCTION opengeni_private.session_tenancy_account_activated(uuid) IS
|
|
164
|
+
'Legacy-lane retirement predicate (0340): true when the transaction''s own organization holds a receipt. Since 0611 it gates no product surface; it only closes the legacy_user connection and unattributed-writer lanes.';
|
|
165
|
+
COMMENT ON FUNCTION session_tenancy_any_product_activation() IS
|
|
166
|
+
'Retired by 0611 with OPENGENI_ORGANIZATION_TENANCY_CANONICAL_ACTIVATION_ENABLED. Always false so no binary enforces the removed startup interlock.';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- A scheduled occurrence whose model is retired or no longer in the catalog is
|
|
3
|
+
-- refused as a visible terminal run (`scheduled_model_unavailable`) instead of
|
|
4
|
+
-- exhausting activity retries and leaving no run. Preserve the immutable 0539
|
|
5
|
+
-- refusal lifecycle (as extended by 0552), adding only this reason. Exact
|
|
6
|
+
-- anchoring refuses an unexpected deployed guard definition. Old binaries never
|
|
7
|
+
-- write the new reason, so this is rolling-safe.
|
|
8
|
+
SET LOCAL lock_timeout = '5s';
|
|
9
|
+
SET LOCAL statement_timeout = '10min';
|
|
10
|
+
|
|
11
|
+
DO $scheduled_model_refusal$
|
|
12
|
+
DECLARE
|
|
13
|
+
definition text:=pg_get_functiondef('opengeni_private.guard_scheduled_admission_refusal()'::regprocedure);
|
|
14
|
+
anchor text:='''monthly_agent_run_limit'', ''allowance_exhausted'')';
|
|
15
|
+
BEGIN
|
|
16
|
+
IF strpos(definition,anchor)=0 OR strpos(substr(definition,strpos(definition,anchor)+length(anchor)),anchor)>0 THEN
|
|
17
|
+
RAISE EXCEPTION '0612 unexpected scheduled refusal guard definition' USING ERRCODE='55000';
|
|
18
|
+
END IF;
|
|
19
|
+
EXECUTE replace(definition,anchor,
|
|
20
|
+
'''monthly_agent_run_limit'', ''allowance_exhausted'', ''scheduled_model_unavailable'')');
|
|
21
|
+
END $scheduled_model_refusal$;
|