@opengeni/db 3.6.0 → 3.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-744DFC65.js → chunk-4NAR7FBA.js} +92 -6
- package/dist/chunk-4NAR7FBA.js.map +1 -0
- package/dist/{chunk-IW6O6G7R.js → chunk-5NNCGFQY.js} +2 -2
- package/dist/{chunk-G3732M3T.js → chunk-DZCYW2WT.js} +2 -2
- package/dist/{chunk-WW7AJ5IC.js → chunk-F6PBS6WE.js} +5 -7
- package/dist/chunk-F6PBS6WE.js.map +1 -0
- package/dist/{chunk-UJ2EW3QI.js → chunk-O56DM5GZ.js} +340 -85
- package/dist/chunk-O56DM5GZ.js.map +1 -0
- package/dist/{chunk-CDMMSUWJ.js → chunk-S3TDDCS2.js} +3 -3
- package/dist/{chunk-Q5YUGQVK.js → chunk-SZAWDXQC.js} +3 -3
- package/dist/{chunk-UGDVSQ6U.js → chunk-U4WPPEBH.js} +2 -2
- package/dist/{chunk-2NT476HD.js → chunk-US4IS6IV.js} +2 -2
- package/dist/codex-token-resolver.d.ts +12 -1
- package/dist/company-profile-agent-admin.d.ts +4 -2
- package/dist/company-profile-agent-policy.d.ts +20 -0
- package/dist/company-profile-schema.d.ts +573 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +382 -18
- package/dist/index.js +4513 -2065
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/managed-human-provisioning.d.ts +3 -3
- package/dist/memory-domain.d.ts +5 -3
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -5
- package/dist/schema.d.ts +725 -4
- package/dist/schema.js +13 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +40 -5
- package/dist/session-queue-commands.d.ts +2 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/user-resource-authority.d.ts +8 -0
- package/dist/video-generation.js +3 -3
- package/drizzle/0369_local_human_personal_authority.sql +350 -0
- package/drizzle/0370_workspace_member_management_scope.sql +98 -0
- package/drizzle/0371_workspace_member_candidate_inventory.sql +128 -0
- package/drizzle/0372_ordered_variable_set_runtime_authority.sql +287 -0
- package/drizzle/0373_sandbox_shared_preparation.sql +59 -0
- package/drizzle/0374_session_event_cursors.sql +315 -0
- package/drizzle/0375_session_recovery_observability.sql +73 -0
- package/drizzle/0376_organization_workspace_inventory.sql +88 -0
- package/drizzle/0377_scoped_rig_health_projection.sql +80 -0
- package/drizzle/0378_scoped_rig_health_audit_timestamp.sql +77 -0
- package/drizzle/0379_session_event_raw_lane_activation.sql +518 -0
- package/drizzle/0380_autonomous_company_profile_agent_policy.sql +829 -0
- package/drizzle/0381_organization_codex_subscription_inheritance.sql +720 -0
- package/drizzle/0382_organization_api_key_provenance.sql +91 -0
- package/drizzle/0383_codex_cooldown_reconciliation.sql +51 -0
- package/drizzle/0384_codex_cooldown_revision_guard_privileges.sql +6 -0
- package/drizzle/0385_connected_machine_legacy_tilde_workdirs.sql +25 -0
- package/drizzle/0386_local_organization_administration.sql +157 -0
- package/package.json +5 -5
- package/src/codex-token-resolver.ts +26 -1
- package/src/company-brain-governed-writes.ts +30 -1
- package/src/company-profile-agent-admin.ts +68 -1
- package/src/company-profile-agent-policy.ts +126 -0
- package/src/company-profile-schema.ts +115 -1
- package/src/database.ts +3 -5
- package/src/index.ts +3626 -456
- package/src/managed-human-provisioning.ts +3 -3
- package/src/memory-domain.ts +9 -4
- package/src/organization-membership-lifecycle.ts +0 -1
- package/src/provision-roles.ts +83 -4
- package/src/runtime-posture.ts +22 -0
- package/src/schema.ts +161 -8
- package/src/session-control.ts +177 -59
- package/src/session-queue-commands.ts +146 -40
- package/src/session-tool-call-settlement.ts +34 -7
- package/src/user-resource-authority.ts +56 -0
- package/dist/chunk-744DFC65.js.map +0 -1
- package/dist/chunk-UJ2EW3QI.js.map +0 -1
- package/dist/chunk-WW7AJ5IC.js.map +0 -1
- /package/dist/{chunk-IW6O6G7R.js.map → chunk-5NNCGFQY.js.map} +0 -0
- /package/dist/{chunk-G3732M3T.js.map → chunk-DZCYW2WT.js.map} +0 -0
- /package/dist/{chunk-CDMMSUWJ.js.map → chunk-S3TDDCS2.js.map} +0 -0
- /package/dist/{chunk-Q5YUGQVK.js.map → chunk-SZAWDXQC.js.map} +0 -0
- /package/dist/{chunk-UGDVSQ6U.js.map → chunk-U4WPPEBH.js.map} +0 -0
- /package/dist/{chunk-2NT476HD.js.map → chunk-US4IS6IV.js.map} +0 -0
package/dist/schema.js
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
companyBrainContextSelectionReceipts,
|
|
47
47
|
companyBrainTurnContextSnapshots,
|
|
48
48
|
companyProfileActivationEvents,
|
|
49
|
+
companyProfileAgentAutomaticActivationReceipts,
|
|
49
50
|
companyProfileAgentConfirmationReceipts,
|
|
50
51
|
companyProfileAgentProposalReceipts,
|
|
51
52
|
companyProfileHeads,
|
|
@@ -160,6 +161,9 @@ import {
|
|
|
160
161
|
nestedAgentDepthConfiguration,
|
|
161
162
|
networkRoutes,
|
|
162
163
|
newSessionDrafts,
|
|
164
|
+
organizationCodexRotationSettings,
|
|
165
|
+
organizationCompanyProfileAgentPolicies,
|
|
166
|
+
organizationCompanyProfileAgentPolicyEvents,
|
|
163
167
|
organizationInvitationBindingEvents,
|
|
164
168
|
organizationMembershipInvitations,
|
|
165
169
|
organizationMembershipLifecycleEvents,
|
|
@@ -224,6 +228,7 @@ import {
|
|
|
224
228
|
sessionAttemptToolCatalogs,
|
|
225
229
|
sessionBackgroundCommands,
|
|
226
230
|
sessionCommandReceipts,
|
|
231
|
+
sessionEventCursors,
|
|
227
232
|
sessionEvents,
|
|
228
233
|
sessionGoalRevisions,
|
|
229
234
|
sessionGoals,
|
|
@@ -303,6 +308,7 @@ import {
|
|
|
303
308
|
workspaceArtifactVersions,
|
|
304
309
|
workspaceArtifacts,
|
|
305
310
|
workspaceCaptures,
|
|
311
|
+
workspaceCodexSubscriptionPreferences,
|
|
306
312
|
workspaceControlEvents,
|
|
307
313
|
workspaceInferenceControls,
|
|
308
314
|
workspaceInstructionPolicyActivationEvents,
|
|
@@ -331,7 +337,7 @@ import {
|
|
|
331
337
|
xaiRotationSettings,
|
|
332
338
|
xaiSessionAccountPins,
|
|
333
339
|
xaiSubscriptionCredentials
|
|
334
|
-
} from "./chunk-
|
|
340
|
+
} from "./chunk-O56DM5GZ.js";
|
|
335
341
|
import "./chunk-PZ5AY32C.js";
|
|
336
342
|
export {
|
|
337
343
|
agentRunStates,
|
|
@@ -381,6 +387,7 @@ export {
|
|
|
381
387
|
companyBrainContextSelectionReceipts,
|
|
382
388
|
companyBrainTurnContextSnapshots,
|
|
383
389
|
companyProfileActivationEvents,
|
|
390
|
+
companyProfileAgentAutomaticActivationReceipts,
|
|
384
391
|
companyProfileAgentConfirmationReceipts,
|
|
385
392
|
companyProfileAgentProposalReceipts,
|
|
386
393
|
companyProfileHeads,
|
|
@@ -495,6 +502,9 @@ export {
|
|
|
495
502
|
nestedAgentDepthConfiguration,
|
|
496
503
|
networkRoutes,
|
|
497
504
|
newSessionDrafts,
|
|
505
|
+
organizationCodexRotationSettings,
|
|
506
|
+
organizationCompanyProfileAgentPolicies,
|
|
507
|
+
organizationCompanyProfileAgentPolicyEvents,
|
|
498
508
|
organizationInvitationBindingEvents,
|
|
499
509
|
organizationMembershipInvitations,
|
|
500
510
|
organizationMembershipLifecycleEvents,
|
|
@@ -559,6 +569,7 @@ export {
|
|
|
559
569
|
sessionAttemptToolCatalogs,
|
|
560
570
|
sessionBackgroundCommands,
|
|
561
571
|
sessionCommandReceipts,
|
|
572
|
+
sessionEventCursors,
|
|
562
573
|
sessionEvents,
|
|
563
574
|
sessionGoalRevisions,
|
|
564
575
|
sessionGoals,
|
|
@@ -638,6 +649,7 @@ export {
|
|
|
638
649
|
workspaceArtifactVersions,
|
|
639
650
|
workspaceArtifacts,
|
|
640
651
|
workspaceCaptures,
|
|
652
|
+
workspaceCodexSubscriptionPreferences,
|
|
641
653
|
workspaceControlEvents,
|
|
642
654
|
workspaceInferenceControls,
|
|
643
655
|
workspaceInstructionPolicyActivationEvents,
|
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
settleClaimedConnectedMachineBackgroundCommand,
|
|
13
13
|
settleConnectedMachineSessionBackgroundCommand,
|
|
14
14
|
settleSessionBackgroundCommandForRetainedProcess
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-S3TDDCS2.js";
|
|
16
|
+
import "./chunk-F6PBS6WE.js";
|
|
17
|
+
import "./chunk-O56DM5GZ.js";
|
|
18
18
|
import "./chunk-PZ5AY32C.js";
|
|
19
19
|
export {
|
|
20
20
|
backgroundCommandActivityForSessions,
|
|
@@ -60,6 +60,17 @@ export type EffectiveSessionControl = {
|
|
|
60
60
|
commandCount: number;
|
|
61
61
|
} | null;
|
|
62
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* The bounded control projection required by a locked activity-write fence.
|
|
65
|
+
* Settlement and background-command summaries are read-model concerns and must
|
|
66
|
+
* not extend the session-event critical section when admission only needs the
|
|
67
|
+
* effective run state and blocker scope.
|
|
68
|
+
*/
|
|
69
|
+
export type SessionWriteAdmissionControl = {
|
|
70
|
+
state: EffectiveControlState;
|
|
71
|
+
controlVersion: number;
|
|
72
|
+
primaryBlockerKind: EffectiveControlBlocker["kind"] | null;
|
|
73
|
+
};
|
|
63
74
|
export declare const SESSION_DISCOVERY_CONTROL_TITLE_MAX_CHARS = 200;
|
|
64
75
|
export declare const SESSION_DISCOVERY_CONTROL_TARGET_LIMIT = 100;
|
|
65
76
|
/**
|
|
@@ -272,6 +283,13 @@ export type SessionEventWriteLockInput = {
|
|
|
272
283
|
controlLock: WorkspaceControlLockMode | "already_locked" | "none";
|
|
273
284
|
/** Used only when a staged caller already established the workspace prefix. */
|
|
274
285
|
workspaceLock?: "key_share" | "already_locked";
|
|
286
|
+
/**
|
|
287
|
+
* Semantic writers retain FOR NO KEY UPDATE. Cursor-authoritative raw-only
|
|
288
|
+
* appends need only the FK-compatible identity hold; KEY SHARE is compatible
|
|
289
|
+
* with semantic NO KEY UPDATE and therefore removes the wide session row as
|
|
290
|
+
* their serialization mutex without inverting the rolling lock order.
|
|
291
|
+
*/
|
|
292
|
+
sessionLock?: "no_key_update" | "key_share";
|
|
275
293
|
sessionIds?: string[];
|
|
276
294
|
turnIds?: string[];
|
|
277
295
|
attemptIds?: string[];
|
|
@@ -280,6 +298,7 @@ export type SessionEventWriteLocks = {
|
|
|
280
298
|
control: WorkspaceControlRow | null;
|
|
281
299
|
workspace: typeof schema.workspaces.$inferSelect | null;
|
|
282
300
|
sessions: Array<typeof schema.sessions.$inferSelect>;
|
|
301
|
+
cursors: Array<typeof schema.sessionEventCursors.$inferSelect>;
|
|
283
302
|
turns: Array<typeof schema.sessionTurns.$inferSelect>;
|
|
284
303
|
attempts: Array<typeof schema.sessionTurnAttempts.$inferSelect>;
|
|
285
304
|
};
|
|
@@ -289,7 +308,8 @@ export type SessionEventWriteLocks = {
|
|
|
289
308
|
* workspace-control advisory lock + workspace_inference_controls row
|
|
290
309
|
* (when control-aware; see `lockWorkspaceInferenceControl`)
|
|
291
310
|
* -> actual workspaces row FOR KEY SHARE
|
|
292
|
-
* -> session rows FOR NO KEY UPDATE, UUID ordered
|
|
311
|
+
* -> session rows FOR NO KEY UPDATE (semantic) or KEY SHARE (raw), UUID ordered
|
|
312
|
+
* -> session event cursor rows FOR UPDATE, UUID ordered
|
|
293
313
|
* -> exact turn rows FOR UPDATE, UUID ordered
|
|
294
314
|
* -> exact attempt rows FOR UPDATE, UUID ordered
|
|
295
315
|
*
|
|
@@ -304,10 +324,12 @@ export type SessionEventWriteLocks = {
|
|
|
304
324
|
* `FOR UPDATE` lock serialized unrelated sessions and inverted the activity
|
|
305
325
|
* path's session -> implicit workspace-FK edge.
|
|
306
326
|
*
|
|
307
|
-
* `FOR NO KEY UPDATE` is equally deliberate for
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
327
|
+
* `FOR NO KEY UPDATE` is equally deliberate for semantic session writers.
|
|
328
|
+
* Cursor-authoritative raw-only writers use `FOR KEY SHARE`: it preserves the
|
|
329
|
+
* session identity against deletion while remaining compatible with semantic
|
|
330
|
+
* `FOR NO KEY UPDATE`. Keeping this rolling session-before-cursor order avoids
|
|
331
|
+
* an inversion with old binaries and quiescent cascade deletion; the compact
|
|
332
|
+
* cursor row, not the wide session row, is the raw ordering mutex.
|
|
311
333
|
*
|
|
312
334
|
* Complex lifecycle transactions may acquire allocator/control locks before
|
|
313
335
|
* this helper and may discover exact turn IDs only after locking the session.
|
|
@@ -379,6 +401,19 @@ export declare function evaluateSessionControls(db: Database, workspaceId: strin
|
|
|
379
401
|
/** Reuse a control row already locked before workspace/session/turn rows. */
|
|
380
402
|
workspaceControl?: WorkspaceControlRow | undefined;
|
|
381
403
|
}): Promise<Map<string, EffectiveSessionControl>>;
|
|
404
|
+
/**
|
|
405
|
+
* Evaluate only the control facts needed to admit an already locked session
|
|
406
|
+
* write. The workspace-control row must be reused when the caller established
|
|
407
|
+
* the canonical prefix before session/turn/attempt locks.
|
|
408
|
+
*
|
|
409
|
+
* Deliberately omit settlementAttemptCounts and
|
|
410
|
+
* stoppingBackgroundCommandCounts: those recursive summaries do not affect
|
|
411
|
+
* effective pause state and their results are discarded by the write fence.
|
|
412
|
+
*/
|
|
413
|
+
export declare function evaluateSessionWriteAdmissionControl(db: Database, workspaceId: string, sessionId: string, options?: {
|
|
414
|
+
lock?: WorkspaceControlLockMode;
|
|
415
|
+
workspaceControl?: WorkspaceControlRow | undefined;
|
|
416
|
+
}): Promise<SessionWriteAdmissionControl>;
|
|
382
417
|
/**
|
|
383
418
|
* Return one compact aggregate row per target for `sessions_list`.
|
|
384
419
|
*
|
|
@@ -67,8 +67,10 @@ export type AgentInternalUpdateCommandResult = {
|
|
|
67
67
|
};
|
|
68
68
|
type SteerSupersessionResult = {
|
|
69
69
|
interruptionCount: number;
|
|
70
|
+
steerTargetTurn: typeof schema.sessionTurns.$inferSelect | null;
|
|
70
71
|
replacedTurn: typeof schema.sessionTurns.$inferSelect | null;
|
|
71
72
|
liveCurrentTurnId: string | null;
|
|
73
|
+
deferredUntilCompaction: boolean;
|
|
72
74
|
lastSequence: number;
|
|
73
75
|
};
|
|
74
76
|
/**
|
package/dist/session-tenancy.js
CHANGED
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
replayAppliedSessionFork,
|
|
15
15
|
sessionTenancyProductActivated,
|
|
16
16
|
transitionSessionVisibility
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-5NNCGFQY.js";
|
|
18
|
+
import "./chunk-F6PBS6WE.js";
|
|
19
|
+
import "./chunk-O56DM5GZ.js";
|
|
20
20
|
import "./chunk-PZ5AY32C.js";
|
|
21
21
|
export {
|
|
22
22
|
SessionTenancyAccessError,
|
|
@@ -71,6 +71,14 @@ export declare function issueSelfUserResourceGrant(db: Database, input: {
|
|
|
71
71
|
expectedAuthorityEpoch?: number | null;
|
|
72
72
|
workspaceSharedAcknowledged: boolean;
|
|
73
73
|
}): Promise<UserResourceAuthorityGrant>;
|
|
74
|
+
export declare function issueSelfLocalConnectionUseGrant(db: Database, input: {
|
|
75
|
+
accountId: string;
|
|
76
|
+
workspaceId: string;
|
|
77
|
+
subjectId: string;
|
|
78
|
+
authorityId: string;
|
|
79
|
+
context: "user_private" | "workspace_shared";
|
|
80
|
+
workspaceSharedAcknowledged: boolean;
|
|
81
|
+
}): Promise<UserResourceAuthorityGrant>;
|
|
74
82
|
export declare function revokeSelfUserResourceGrant(db: Database, input: {
|
|
75
83
|
accountId: string;
|
|
76
84
|
workspaceId: string;
|
package/dist/video-generation.js
CHANGED
|
@@ -29,9 +29,9 @@ import {
|
|
|
29
29
|
settleVideoGenerationFailure,
|
|
30
30
|
settleVideoGenerationReady,
|
|
31
31
|
updateWorkspaceVideoGenerationPolicy
|
|
32
|
-
} from "./chunk-
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
32
|
+
} from "./chunk-SZAWDXQC.js";
|
|
33
|
+
import "./chunk-F6PBS6WE.js";
|
|
34
|
+
import "./chunk-O56DM5GZ.js";
|
|
35
35
|
import "./chunk-PZ5AY32C.js";
|
|
36
36
|
export {
|
|
37
37
|
ACTIVE_VIDEO_GENERATION_STATUSES,
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Give the explicit single-user local identity the same private authority
|
|
3
|
+
-- anchor as a managed human. This does not add runtime access to the hidden
|
|
4
|
+
-- personal workspace and does not authorize configured keys or host services.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
DO $local_human_personal_authority$
|
|
10
|
+
DECLARE
|
|
11
|
+
data_schema text := current_schema();
|
|
12
|
+
BEGIN
|
|
13
|
+
EXECUTE format($ddl$
|
|
14
|
+
CREATE OR REPLACE FUNCTION %1$I.ensure_managed_human_personal_workspace(
|
|
15
|
+
p_account_id uuid,
|
|
16
|
+
p_subject_id text,
|
|
17
|
+
p_personal_workspace_id uuid
|
|
18
|
+
)
|
|
19
|
+
RETURNS TABLE (
|
|
20
|
+
organization_membership_id uuid,
|
|
21
|
+
personal_workspace_id uuid
|
|
22
|
+
)
|
|
23
|
+
LANGUAGE plpgsql
|
|
24
|
+
SECURITY DEFINER
|
|
25
|
+
SET search_path = %1$I, pg_catalog
|
|
26
|
+
AS $body$
|
|
27
|
+
DECLARE
|
|
28
|
+
expected_external_id text;
|
|
29
|
+
managed_identity boolean := false;
|
|
30
|
+
local_identity boolean := false;
|
|
31
|
+
membership_row organization_memberships%%ROWTYPE;
|
|
32
|
+
workspace_row workspaces%%ROWTYPE;
|
|
33
|
+
previous_workspace_id text := pg_catalog.current_setting(
|
|
34
|
+
'opengeni.workspace_id', true
|
|
35
|
+
);
|
|
36
|
+
previous_lifecycle_marker text := pg_catalog.current_setting(
|
|
37
|
+
'opengeni.organization_tenancy_lifecycle', true
|
|
38
|
+
);
|
|
39
|
+
BEGIN
|
|
40
|
+
IF p_account_id IS NULL
|
|
41
|
+
OR p_subject_id IS NULL
|
|
42
|
+
OR p_personal_workspace_id IS NULL
|
|
43
|
+
OR p_subject_id <> pg_catalog.btrim(p_subject_id)
|
|
44
|
+
OR pg_catalog.length(p_subject_id) NOT BETWEEN 1 AND 1024
|
|
45
|
+
THEN
|
|
46
|
+
RAISE EXCEPTION 'human provisioning requires a valid complete identity'
|
|
47
|
+
USING ERRCODE = '42501';
|
|
48
|
+
END IF;
|
|
49
|
+
|
|
50
|
+
IF p_account_id IS DISTINCT FROM opengeni_private.current_account_id() THEN
|
|
51
|
+
RAISE EXCEPTION 'human provisioning account authority is invalid'
|
|
52
|
+
USING ERRCODE = '42501';
|
|
53
|
+
END IF;
|
|
54
|
+
|
|
55
|
+
IF p_subject_id LIKE 'user:%%' THEN
|
|
56
|
+
expected_external_id := pg_catalog.substr(p_subject_id, 6);
|
|
57
|
+
SELECT EXISTS (
|
|
58
|
+
SELECT 1
|
|
59
|
+
FROM managed_accounts account_row
|
|
60
|
+
WHERE account_row.id = p_account_id
|
|
61
|
+
AND account_row.external_source = 'better-auth:user'
|
|
62
|
+
AND account_row.external_id = expected_external_id
|
|
63
|
+
) INTO managed_identity;
|
|
64
|
+
END IF;
|
|
65
|
+
SELECT EXISTS (
|
|
66
|
+
SELECT 1
|
|
67
|
+
FROM managed_accounts account_row
|
|
68
|
+
WHERE account_row.id = p_account_id
|
|
69
|
+
AND account_row.external_source = 'opengeni:local'
|
|
70
|
+
AND account_row.external_id = 'default'
|
|
71
|
+
AND p_subject_id = 'dev'
|
|
72
|
+
AND opengeni_private.current_subject_id() = p_subject_id
|
|
73
|
+
) INTO local_identity;
|
|
74
|
+
IF NOT managed_identity AND NOT local_identity THEN
|
|
75
|
+
RAISE EXCEPTION 'human provisioning account identity is invalid'
|
|
76
|
+
USING ERRCODE = '42501';
|
|
77
|
+
END IF;
|
|
78
|
+
|
|
79
|
+
IF NOT EXISTS (
|
|
80
|
+
SELECT 1
|
|
81
|
+
FROM workspace_memberships owner_membership
|
|
82
|
+
INNER JOIN workspaces owner_workspace
|
|
83
|
+
ON owner_workspace.id = owner_membership.workspace_id
|
|
84
|
+
AND owner_workspace.account_id = owner_membership.account_id
|
|
85
|
+
WHERE owner_membership.account_id = p_account_id
|
|
86
|
+
AND owner_membership.subject_id = p_subject_id
|
|
87
|
+
AND owner_membership.role = 'owner'
|
|
88
|
+
AND (
|
|
89
|
+
NOT local_identity
|
|
90
|
+
OR (
|
|
91
|
+
owner_workspace.external_source = 'opengeni:local'
|
|
92
|
+
AND owner_workspace.external_id = 'default'
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
) THEN
|
|
96
|
+
RAISE EXCEPTION 'human provisioning owner membership is invalid'
|
|
97
|
+
USING ERRCODE = '42501';
|
|
98
|
+
END IF;
|
|
99
|
+
|
|
100
|
+
SELECT * INTO workspace_row
|
|
101
|
+
FROM workspaces candidate_workspace
|
|
102
|
+
WHERE candidate_workspace.id = p_personal_workspace_id
|
|
103
|
+
FOR UPDATE;
|
|
104
|
+
IF NOT FOUND
|
|
105
|
+
OR workspace_row.account_id IS DISTINCT FROM p_account_id
|
|
106
|
+
OR workspace_row.external_source IS DISTINCT FROM 'opengeni:organization-membership'
|
|
107
|
+
OR workspace_row.external_id IS DISTINCT FROM (
|
|
108
|
+
p_account_id::text || ':' || p_subject_id
|
|
109
|
+
)
|
|
110
|
+
THEN
|
|
111
|
+
RAISE EXCEPTION 'human provisioning personal workspace identity is invalid'
|
|
112
|
+
USING ERRCODE = '42501';
|
|
113
|
+
END IF;
|
|
114
|
+
|
|
115
|
+
IF EXISTS (
|
|
116
|
+
SELECT 1 FROM workspaces conflicting_workspace
|
|
117
|
+
WHERE conflicting_workspace.external_source = 'opengeni:organization-membership'
|
|
118
|
+
AND conflicting_workspace.external_id = p_account_id::text || ':' || p_subject_id
|
|
119
|
+
AND conflicting_workspace.id <> p_personal_workspace_id
|
|
120
|
+
) THEN
|
|
121
|
+
RAISE EXCEPTION 'human provisioning personal workspace identity conflicts'
|
|
122
|
+
USING ERRCODE = '23505';
|
|
123
|
+
END IF;
|
|
124
|
+
|
|
125
|
+
IF EXISTS (
|
|
126
|
+
SELECT 1 FROM workspace_memberships personal_access
|
|
127
|
+
WHERE personal_access.workspace_id = p_personal_workspace_id
|
|
128
|
+
) THEN
|
|
129
|
+
RAISE EXCEPTION 'human personal workspace already has runtime access'
|
|
130
|
+
USING ERRCODE = '42501';
|
|
131
|
+
END IF;
|
|
132
|
+
|
|
133
|
+
PERFORM pg_catalog.set_config(
|
|
134
|
+
'opengeni.workspace_id', p_personal_workspace_id::text, true
|
|
135
|
+
);
|
|
136
|
+
IF NOT EXISTS (
|
|
137
|
+
SELECT 1 FROM workspace_inference_controls control_row
|
|
138
|
+
WHERE control_row.workspace_id = p_personal_workspace_id
|
|
139
|
+
AND control_row.account_id = p_account_id
|
|
140
|
+
) THEN
|
|
141
|
+
RAISE EXCEPTION 'human personal workspace control is missing'
|
|
142
|
+
USING ERRCODE = '42501';
|
|
143
|
+
END IF;
|
|
144
|
+
|
|
145
|
+
PERFORM pg_catalog.set_config(
|
|
146
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
147
|
+
'managed_human_provisioning',
|
|
148
|
+
true
|
|
149
|
+
);
|
|
150
|
+
INSERT INTO organization_memberships (
|
|
151
|
+
account_id, subject_id, status, personal_workspace_id
|
|
152
|
+
) VALUES (
|
|
153
|
+
p_account_id, p_subject_id, 'active', p_personal_workspace_id
|
|
154
|
+
) ON CONFLICT (account_id, subject_id) DO NOTHING;
|
|
155
|
+
|
|
156
|
+
SELECT * INTO membership_row
|
|
157
|
+
FROM organization_memberships candidate_membership
|
|
158
|
+
WHERE candidate_membership.account_id = p_account_id
|
|
159
|
+
AND candidate_membership.subject_id = p_subject_id
|
|
160
|
+
FOR UPDATE;
|
|
161
|
+
IF NOT FOUND THEN
|
|
162
|
+
RAISE EXCEPTION 'human organization membership was not created'
|
|
163
|
+
USING ERRCODE = 'P0002';
|
|
164
|
+
END IF;
|
|
165
|
+
IF membership_row.status IN ('suspended', 'revoked') THEN
|
|
166
|
+
RAISE EXCEPTION 'human organization membership is not active'
|
|
167
|
+
USING ERRCODE = '42501';
|
|
168
|
+
END IF;
|
|
169
|
+
IF membership_row.personal_workspace_id IS NOT NULL
|
|
170
|
+
AND membership_row.personal_workspace_id IS DISTINCT FROM p_personal_workspace_id
|
|
171
|
+
THEN
|
|
172
|
+
RAISE EXCEPTION 'human organization membership personal workspace conflicts'
|
|
173
|
+
USING ERRCODE = '23505';
|
|
174
|
+
END IF;
|
|
175
|
+
IF membership_row.status = 'provisioning' THEN
|
|
176
|
+
UPDATE organization_memberships
|
|
177
|
+
SET status = 'active',
|
|
178
|
+
personal_workspace_id = p_personal_workspace_id,
|
|
179
|
+
updated_at = pg_catalog.clock_timestamp()
|
|
180
|
+
WHERE id = membership_row.id;
|
|
181
|
+
membership_row.status := 'active';
|
|
182
|
+
membership_row.personal_workspace_id := p_personal_workspace_id;
|
|
183
|
+
END IF;
|
|
184
|
+
IF membership_row.status <> 'active'
|
|
185
|
+
OR membership_row.personal_workspace_id IS DISTINCT FROM p_personal_workspace_id
|
|
186
|
+
THEN
|
|
187
|
+
RAISE EXCEPTION 'human organization membership did not converge'
|
|
188
|
+
USING ERRCODE = '42501';
|
|
189
|
+
END IF;
|
|
190
|
+
|
|
191
|
+
PERFORM pg_catalog.set_config(
|
|
192
|
+
'opengeni.workspace_id', coalesce(previous_workspace_id, ''), true
|
|
193
|
+
);
|
|
194
|
+
PERFORM pg_catalog.set_config(
|
|
195
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
196
|
+
coalesce(previous_lifecycle_marker, ''),
|
|
197
|
+
true
|
|
198
|
+
);
|
|
199
|
+
organization_membership_id := membership_row.id;
|
|
200
|
+
personal_workspace_id := membership_row.personal_workspace_id;
|
|
201
|
+
RETURN NEXT;
|
|
202
|
+
EXCEPTION WHEN OTHERS THEN
|
|
203
|
+
PERFORM pg_catalog.set_config(
|
|
204
|
+
'opengeni.workspace_id', coalesce(previous_workspace_id, ''), true
|
|
205
|
+
);
|
|
206
|
+
PERFORM pg_catalog.set_config(
|
|
207
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
208
|
+
coalesce(previous_lifecycle_marker, ''),
|
|
209
|
+
true
|
|
210
|
+
);
|
|
211
|
+
RAISE;
|
|
212
|
+
END;
|
|
213
|
+
$body$;
|
|
214
|
+
$ddl$, data_schema);
|
|
215
|
+
|
|
216
|
+
EXECUTE format(
|
|
217
|
+
'REVOKE ALL ON FUNCTION %I.ensure_managed_human_personal_workspace(uuid,text,uuid) FROM PUBLIC',
|
|
218
|
+
data_schema
|
|
219
|
+
);
|
|
220
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
221
|
+
EXECUTE format(
|
|
222
|
+
'GRANT EXECUTE ON FUNCTION %I.ensure_managed_human_personal_workspace(uuid,text,uuid) TO opengeni_app',
|
|
223
|
+
data_schema
|
|
224
|
+
);
|
|
225
|
+
END IF;
|
|
226
|
+
END
|
|
227
|
+
$local_human_personal_authority$;
|
|
228
|
+
|
|
229
|
+
COMMENT ON FUNCTION ensure_managed_human_personal_workspace(uuid, text, uuid) IS
|
|
230
|
+
'Lifecycle-only managed or explicit local-human organization membership and personal-workspace provisioning; no workspace access grant.';
|
|
231
|
+
|
|
232
|
+
CREATE FUNCTION issue_self_local_connection_use_grant(
|
|
233
|
+
p_account_id uuid,
|
|
234
|
+
p_authority_id uuid,
|
|
235
|
+
p_workspace_id uuid,
|
|
236
|
+
p_context text,
|
|
237
|
+
p_workspace_shared_acknowledged boolean DEFAULT false
|
|
238
|
+
) RETURNS TABLE (
|
|
239
|
+
grant_id uuid, organization_id uuid, authority_generation bigint,
|
|
240
|
+
target_workspace_id uuid, target_session_id uuid, action text,
|
|
241
|
+
grant_mode text, grant_context text, authority_epoch integer,
|
|
242
|
+
grant_generation bigint, grant_status text, expires_at timestamptz
|
|
243
|
+
)
|
|
244
|
+
LANGUAGE plpgsql
|
|
245
|
+
SECURITY DEFINER
|
|
246
|
+
SET search_path FROM CURRENT
|
|
247
|
+
AS $body$
|
|
248
|
+
#variable_conflict use_column
|
|
249
|
+
DECLARE
|
|
250
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
251
|
+
owner_membership_id uuid;
|
|
252
|
+
BEGIN
|
|
253
|
+
PERFORM set_config(
|
|
254
|
+
'opengeni.organization_tenancy_lifecycle', 'personal_resource_grant_management', true
|
|
255
|
+
);
|
|
256
|
+
IF p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
257
|
+
OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
258
|
+
OR caller_subject IS DISTINCT FROM 'dev'
|
|
259
|
+
OR p_context NOT IN ('user_private', 'workspace_shared')
|
|
260
|
+
OR (p_context = 'workspace_shared' AND p_workspace_shared_acknowledged IS NOT TRUE)
|
|
261
|
+
OR NOT EXISTS (
|
|
262
|
+
SELECT 1 FROM managed_accounts account_row
|
|
263
|
+
WHERE account_row.id = p_account_id
|
|
264
|
+
AND account_row.external_source = 'opengeni:local'
|
|
265
|
+
AND account_row.external_id = 'default'
|
|
266
|
+
)
|
|
267
|
+
THEN
|
|
268
|
+
RAISE EXCEPTION 'local connection grant authority is invalid'
|
|
269
|
+
USING ERRCODE = '42501';
|
|
270
|
+
END IF;
|
|
271
|
+
|
|
272
|
+
SELECT membership.id INTO owner_membership_id
|
|
273
|
+
FROM organization_memberships membership
|
|
274
|
+
WHERE membership.account_id = p_account_id
|
|
275
|
+
AND membership.subject_id = caller_subject
|
|
276
|
+
AND membership.status = 'active'
|
|
277
|
+
AND membership.revoked_at IS NULL
|
|
278
|
+
FOR SHARE;
|
|
279
|
+
IF NOT FOUND OR NOT EXISTS (
|
|
280
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
281
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
282
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
283
|
+
AND workspace_membership.subject_id = caller_subject
|
|
284
|
+
AND workspace_membership.role = 'owner'
|
|
285
|
+
) THEN
|
|
286
|
+
RAISE EXCEPTION 'local connection grant owner is invalid'
|
|
287
|
+
USING ERRCODE = '42501';
|
|
288
|
+
END IF;
|
|
289
|
+
|
|
290
|
+
SELECT authority.generation INTO authority_generation
|
|
291
|
+
FROM organization_user_resource_authorities authority
|
|
292
|
+
WHERE authority.id = p_authority_id
|
|
293
|
+
AND authority.account_id = p_account_id
|
|
294
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
295
|
+
AND authority.resource_kind = 'connection'
|
|
296
|
+
AND authority.status = 'active'
|
|
297
|
+
AND authority.revoked_at IS NULL
|
|
298
|
+
FOR UPDATE;
|
|
299
|
+
IF NOT FOUND THEN
|
|
300
|
+
RAISE EXCEPTION 'local connection authority is unavailable'
|
|
301
|
+
USING ERRCODE = '42501';
|
|
302
|
+
END IF;
|
|
303
|
+
|
|
304
|
+
INSERT INTO organization_user_resource_grants (
|
|
305
|
+
account_id, authority_id, owner_organization_membership_id, workspace_id,
|
|
306
|
+
session_id, action, mode, context, authority_epoch, generation, status
|
|
307
|
+
) VALUES (
|
|
308
|
+
p_account_id, p_authority_id, owner_membership_id, p_workspace_id,
|
|
309
|
+
NULL, 'connection.use', 'always', p_context, NULL, 1, 'active'
|
|
310
|
+
)
|
|
311
|
+
ON CONFLICT (account_id, authority_id, workspace_id, action, mode, context,
|
|
312
|
+
(coalesce(session_id, '00000000-0000-0000-0000-000000000000'::uuid)),
|
|
313
|
+
(coalesce(authority_epoch, 0))) WHERE status = 'active'
|
|
314
|
+
DO UPDATE SET updated_at = organization_user_resource_grants.updated_at
|
|
315
|
+
RETURNING id, workspace_id, session_id, organization_user_resource_grants.action,
|
|
316
|
+
mode, context, organization_user_resource_grants.authority_epoch, generation,
|
|
317
|
+
status, organization_user_resource_grants.expires_at
|
|
318
|
+
INTO grant_id, target_workspace_id, target_session_id, action, grant_mode,
|
|
319
|
+
grant_context, authority_epoch, grant_generation, grant_status, expires_at;
|
|
320
|
+
organization_id := p_account_id;
|
|
321
|
+
RETURN NEXT;
|
|
322
|
+
END
|
|
323
|
+
$body$;
|
|
324
|
+
|
|
325
|
+
DO $local_connection_grant_search_path$
|
|
326
|
+
DECLARE
|
|
327
|
+
data_schema text := current_schema();
|
|
328
|
+
BEGIN
|
|
329
|
+
EXECUTE format(
|
|
330
|
+
'ALTER FUNCTION %I.issue_self_local_connection_use_grant(uuid,uuid,uuid,text,boolean) SET search_path = pg_catalog, %I, pg_temp',
|
|
331
|
+
data_schema, data_schema
|
|
332
|
+
);
|
|
333
|
+
END
|
|
334
|
+
$local_connection_grant_search_path$;
|
|
335
|
+
REVOKE ALL ON FUNCTION issue_self_local_connection_use_grant(
|
|
336
|
+
uuid, uuid, uuid, text, boolean
|
|
337
|
+
) FROM PUBLIC;
|
|
338
|
+
DO $local_connection_grant_role$
|
|
339
|
+
BEGIN
|
|
340
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
341
|
+
GRANT EXECUTE ON FUNCTION issue_self_local_connection_use_grant(
|
|
342
|
+
uuid, uuid, uuid, text, boolean
|
|
343
|
+
) TO opengeni_app;
|
|
344
|
+
END IF;
|
|
345
|
+
END
|
|
346
|
+
$local_connection_grant_role$;
|
|
347
|
+
|
|
348
|
+
COMMENT ON FUNCTION issue_self_local_connection_use_grant(
|
|
349
|
+
uuid, uuid, uuid, text, boolean
|
|
350
|
+
) IS 'Idempotently issues standing connection.use only for the explicit local dev human and exact local owner workspace.';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Workspace administrators may manage access to their exact shared workspace,
|
|
3
|
+
-- but they may only add active human members of the owning organization.
|
|
4
|
+
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '10min';
|
|
7
|
+
|
|
8
|
+
CREATE FUNCTION assert_workspace_member_management_candidate(
|
|
9
|
+
p_account_id uuid,
|
|
10
|
+
p_workspace_id uuid,
|
|
11
|
+
p_actor_subject_id text,
|
|
12
|
+
p_target_subject_id text
|
|
13
|
+
) RETURNS void
|
|
14
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
15
|
+
SET search_path FROM CURRENT
|
|
16
|
+
AS $body$
|
|
17
|
+
DECLARE
|
|
18
|
+
previous_lifecycle text := current_setting('opengeni.organization_tenancy_lifecycle', true);
|
|
19
|
+
BEGIN
|
|
20
|
+
IF p_account_id IS NULL OR p_workspace_id IS NULL
|
|
21
|
+
OR nullif(pg_catalog.btrim(p_actor_subject_id), '') IS NULL
|
|
22
|
+
OR nullif(pg_catalog.btrim(p_target_subject_id), '') IS NULL
|
|
23
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
24
|
+
OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
25
|
+
OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
26
|
+
THEN
|
|
27
|
+
RAISE EXCEPTION 'workspace member management authority required' USING ERRCODE = '42501';
|
|
28
|
+
END IF;
|
|
29
|
+
|
|
30
|
+
PERFORM pg_catalog.set_config(
|
|
31
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
32
|
+
'organization_membership_lifecycle', true
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
IF NOT EXISTS (
|
|
36
|
+
SELECT 1 FROM workspaces workspace
|
|
37
|
+
WHERE workspace.id = p_workspace_id AND workspace.account_id = p_account_id
|
|
38
|
+
) THEN
|
|
39
|
+
RAISE EXCEPTION 'workspace not found' USING ERRCODE = 'P0002';
|
|
40
|
+
END IF;
|
|
41
|
+
|
|
42
|
+
IF EXISTS (
|
|
43
|
+
SELECT 1 FROM organization_memberships membership
|
|
44
|
+
WHERE membership.account_id = p_account_id
|
|
45
|
+
AND membership.personal_workspace_id = p_workspace_id
|
|
46
|
+
) THEN
|
|
47
|
+
RAISE EXCEPTION 'Personal workspace membership is owner-only' USING ERRCODE = '42501';
|
|
48
|
+
END IF;
|
|
49
|
+
|
|
50
|
+
IF NOT EXISTS (
|
|
51
|
+
SELECT 1
|
|
52
|
+
FROM organization_memberships organization_membership
|
|
53
|
+
JOIN workspace_memberships workspace_membership
|
|
54
|
+
ON workspace_membership.account_id = organization_membership.account_id
|
|
55
|
+
AND workspace_membership.subject_id = organization_membership.subject_id
|
|
56
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
57
|
+
WHERE organization_membership.account_id = p_account_id
|
|
58
|
+
AND organization_membership.subject_id = p_actor_subject_id
|
|
59
|
+
AND organization_membership.status = 'active'
|
|
60
|
+
AND (
|
|
61
|
+
workspace_membership.permissions ? 'members:manage'
|
|
62
|
+
OR workspace_membership.permissions ? 'workspace:admin'
|
|
63
|
+
)
|
|
64
|
+
) THEN
|
|
65
|
+
RAISE EXCEPTION 'workspace member management authority required' USING ERRCODE = '42501';
|
|
66
|
+
END IF;
|
|
67
|
+
|
|
68
|
+
IF NOT EXISTS (
|
|
69
|
+
SELECT 1 FROM organization_memberships target
|
|
70
|
+
WHERE target.account_id = p_account_id
|
|
71
|
+
AND target.subject_id = p_target_subject_id
|
|
72
|
+
AND target.status = 'active'
|
|
73
|
+
) THEN
|
|
74
|
+
RAISE EXCEPTION 'active organization member not found' USING ERRCODE = 'P0002';
|
|
75
|
+
END IF;
|
|
76
|
+
|
|
77
|
+
PERFORM pg_catalog.set_config(
|
|
78
|
+
'opengeni.organization_tenancy_lifecycle', COALESCE(previous_lifecycle, ''), true
|
|
79
|
+
);
|
|
80
|
+
EXCEPTION WHEN OTHERS THEN
|
|
81
|
+
PERFORM pg_catalog.set_config(
|
|
82
|
+
'opengeni.organization_tenancy_lifecycle', COALESCE(previous_lifecycle, ''), true
|
|
83
|
+
);
|
|
84
|
+
RAISE;
|
|
85
|
+
END
|
|
86
|
+
$body$;
|
|
87
|
+
|
|
88
|
+
REVOKE ALL ON FUNCTION assert_workspace_member_management_candidate(uuid, uuid, text, text)
|
|
89
|
+
FROM PUBLIC;
|
|
90
|
+
DO $workspace_member_management_role$
|
|
91
|
+
BEGIN
|
|
92
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
93
|
+
GRANT EXECUTE ON FUNCTION assert_workspace_member_management_candidate(
|
|
94
|
+
uuid, uuid, text, text
|
|
95
|
+
) TO opengeni_app;
|
|
96
|
+
END IF;
|
|
97
|
+
END
|
|
98
|
+
$workspace_member_management_role$;
|