@opengeni/db 0.36.1 → 3.1.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/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-GYO7ED2Q.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-VTPRIXKG.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-HSOWSYOX.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-5KTIEDX7.js → chunk-PR2WMLOQ.js} +7714 -4443
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-Z7VYMNPZ.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.d.ts +10 -0
- package/dist/editable-artifact-durable-export.js +18 -3
- package/dist/editable-artifact-durable-export.js.map +1 -1
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1633 -87
- package/dist/index.js +22004 -8109
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/knowledge-source-sync-schema.d.ts +730 -0
- package/dist/knowledge-source-sync.d.ts +60 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +23 -9
- package/dist/schema.d.ts +24925 -13697
- package/dist/schema.js +103 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +42 -5
- package/dist/session-realtime-ledger.d.ts +3 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +153 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +1 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0240_model_context_user_messages.sql +204 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0243_google_drive_object_acl_authority.sql +554 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-artifact-durable-export.ts +27 -0
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +12001 -1699
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/knowledge-source-sync-schema.ts +83 -0
- package/src/knowledge-source-sync.ts +530 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +931 -24
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1315 -52
- package/src/schema.ts +3336 -438
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +450 -98
- package/src/session-realtime-context.ts +19 -3
- package/src/session-realtime-ledger.ts +75 -12
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +17 -1
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-5KTIEDX7.js.map +0 -1
- package/dist/chunk-FPVAD4CO.js +0 -117
- package/dist/chunk-FPVAD4CO.js.map +0 -1
- package/dist/chunk-HSOWSYOX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-VKLB2ZGI.js +0 -2389
- package/dist/chunk-VKLB2ZGI.js.map +0 -1
- package/dist/chunk-Z7VYMNPZ.js.map +0 -1
- /package/dist/{chunk-GYO7ED2Q.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-VTPRIXKG.js.map → chunk-NJVZPWXY.js.map} +0 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Activates the bounded organization administration projection and the atomic
|
|
3
|
+
-- private-session create seam. Existing session creation remains workspace-
|
|
4
|
+
-- shared unless an exact managed-human caller explicitly requests privacy.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
CREATE TABLE organization_profile_events (
|
|
10
|
+
id uuid PRIMARY KEY,
|
|
11
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
12
|
+
actor_membership_id uuid NOT NULL,
|
|
13
|
+
previous_name text NOT NULL,
|
|
14
|
+
requested_name text NOT NULL,
|
|
15
|
+
expected_updated_at timestamptz NOT NULL,
|
|
16
|
+
result_updated_at timestamptz NOT NULL,
|
|
17
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
18
|
+
CONSTRAINT organization_profile_events_actor_fk
|
|
19
|
+
FOREIGN KEY (actor_membership_id, account_id)
|
|
20
|
+
REFERENCES organization_memberships(id, account_id) ON DELETE RESTRICT,
|
|
21
|
+
CONSTRAINT organization_profile_events_name_check CHECK (
|
|
22
|
+
octet_length(btrim(requested_name)) BETWEEN 1 AND 480
|
|
23
|
+
)
|
|
24
|
+
);
|
|
25
|
+
ALTER TABLE organization_profile_events ENABLE ROW LEVEL SECURITY;
|
|
26
|
+
ALTER TABLE organization_profile_events FORCE ROW LEVEL SECURITY;
|
|
27
|
+
REVOKE ALL ON TABLE organization_profile_events FROM PUBLIC;
|
|
28
|
+
CREATE POLICY organization_tenancy_lifecycle ON organization_profile_events
|
|
29
|
+
USING (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
30
|
+
= 'organization_membership_lifecycle')
|
|
31
|
+
WITH CHECK (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
32
|
+
= 'organization_membership_lifecycle');
|
|
33
|
+
|
|
34
|
+
ALTER TABLE sessions
|
|
35
|
+
ADD COLUMN create_requested_visibility text NOT NULL DEFAULT 'workspace_shared';
|
|
36
|
+
ALTER TABLE sessions
|
|
37
|
+
ADD CONSTRAINT sessions_create_requested_visibility_check
|
|
38
|
+
CHECK (create_requested_visibility IN ('user_private', 'workspace_shared'));
|
|
39
|
+
|
|
40
|
+
-- Private creation is intentionally separate from the generic visibility
|
|
41
|
+
-- lifecycle capability. The application role can ask the definer to mint one
|
|
42
|
+
-- exact, transaction-local INSERT authority, but cannot use it for UPDATE or
|
|
43
|
+
-- a second session.
|
|
44
|
+
CREATE TABLE private_session_create_capabilities (
|
|
45
|
+
backend_pid integer NOT NULL,
|
|
46
|
+
transaction_id xid8 NOT NULL,
|
|
47
|
+
capability_id uuid NOT NULL,
|
|
48
|
+
account_id uuid NOT NULL,
|
|
49
|
+
workspace_id uuid NOT NULL,
|
|
50
|
+
session_id uuid NOT NULL,
|
|
51
|
+
actor_subject_id text NOT NULL,
|
|
52
|
+
owner_membership_id uuid NOT NULL,
|
|
53
|
+
PRIMARY KEY (backend_pid, transaction_id, capability_id)
|
|
54
|
+
);
|
|
55
|
+
ALTER TABLE private_session_create_capabilities ENABLE ROW LEVEL SECURITY;
|
|
56
|
+
ALTER TABLE private_session_create_capabilities FORCE ROW LEVEL SECURITY;
|
|
57
|
+
REVOKE ALL ON TABLE private_session_create_capabilities FROM PUBLIC;
|
|
58
|
+
CREATE POLICY private_session_create_capability_owner
|
|
59
|
+
ON private_session_create_capabilities
|
|
60
|
+
USING (current_setting('opengeni.private_session_create_lifecycle', true)
|
|
61
|
+
= 'private_session_create')
|
|
62
|
+
WITH CHECK (current_setting('opengeni.private_session_create_lifecycle', true)
|
|
63
|
+
= 'private_session_create');
|
|
64
|
+
|
|
65
|
+
CREATE FUNCTION get_organization_administration_overview(
|
|
66
|
+
p_account_id uuid,
|
|
67
|
+
p_actor_subject_id text
|
|
68
|
+
) RETURNS jsonb
|
|
69
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
70
|
+
SET search_path FROM CURRENT
|
|
71
|
+
AS $body$
|
|
72
|
+
DECLARE
|
|
73
|
+
actor organization_memberships%ROWTYPE;
|
|
74
|
+
result jsonb;
|
|
75
|
+
workspace_count integer;
|
|
76
|
+
oversized_workspace boolean;
|
|
77
|
+
BEGIN
|
|
78
|
+
IF p_account_id IS NULL
|
|
79
|
+
OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
80
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
81
|
+
THEN
|
|
82
|
+
RAISE EXCEPTION 'organization administration authority required' USING ERRCODE = '42501';
|
|
83
|
+
END IF;
|
|
84
|
+
PERFORM pg_catalog.set_config(
|
|
85
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
86
|
+
'organization_membership_lifecycle', true
|
|
87
|
+
);
|
|
88
|
+
PERFORM 1 FROM managed_accounts account
|
|
89
|
+
WHERE account.id = p_account_id FOR SHARE;
|
|
90
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002'; END IF;
|
|
91
|
+
SELECT * INTO actor FROM organization_memberships membership
|
|
92
|
+
WHERE membership.account_id = p_account_id
|
|
93
|
+
AND membership.subject_id = p_actor_subject_id
|
|
94
|
+
FOR SHARE;
|
|
95
|
+
IF NOT FOUND OR actor.status <> 'active' OR actor.role NOT IN ('owner', 'admin') THEN
|
|
96
|
+
RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
|
|
97
|
+
END IF;
|
|
98
|
+
WITH shared_workspaces AS MATERIALIZED (
|
|
99
|
+
SELECT workspace.*,
|
|
100
|
+
(SELECT count(*)::integer FROM workspace_memberships access
|
|
101
|
+
WHERE access.workspace_id = workspace.id) AS member_count
|
|
102
|
+
FROM workspaces workspace
|
|
103
|
+
WHERE workspace.account_id = p_account_id
|
|
104
|
+
AND NOT EXISTS (
|
|
105
|
+
SELECT 1 FROM organization_memberships membership
|
|
106
|
+
WHERE membership.account_id = p_account_id
|
|
107
|
+
AND membership.personal_workspace_id = workspace.id
|
|
108
|
+
)
|
|
109
|
+
),
|
|
110
|
+
bounds AS (
|
|
111
|
+
SELECT count(*)::integer AS workspace_count,
|
|
112
|
+
coalesce(bool_or(shared.member_count > 1000), false) AS oversized_workspace
|
|
113
|
+
FROM shared_workspaces shared
|
|
114
|
+
)
|
|
115
|
+
SELECT bounds.workspace_count, bounds.oversized_workspace,
|
|
116
|
+
CASE
|
|
117
|
+
WHEN bounds.workspace_count <= 500 AND NOT bounds.oversized_workspace
|
|
118
|
+
THEN pg_catalog.jsonb_build_object(
|
|
119
|
+
'organization', pg_catalog.jsonb_build_object(
|
|
120
|
+
'id', account.id,
|
|
121
|
+
'name', account.name,
|
|
122
|
+
'createdAt', account.created_at,
|
|
123
|
+
'updatedAt', account.updated_at
|
|
124
|
+
),
|
|
125
|
+
'workspaces', coalesce((
|
|
126
|
+
SELECT pg_catalog.jsonb_agg(
|
|
127
|
+
pg_catalog.jsonb_build_object(
|
|
128
|
+
'id', workspace.id,
|
|
129
|
+
'name', workspace.name,
|
|
130
|
+
'slug', workspace.slug,
|
|
131
|
+
'createdAt', workspace.created_at,
|
|
132
|
+
'updatedAt', workspace.updated_at,
|
|
133
|
+
'members', coalesce((
|
|
134
|
+
SELECT pg_catalog.jsonb_agg(
|
|
135
|
+
pg_catalog.jsonb_build_object(
|
|
136
|
+
'membershipId', access.id,
|
|
137
|
+
'subjectId', access.subject_id,
|
|
138
|
+
'subjectLabel', access.subject_label,
|
|
139
|
+
'principalKind', CASE WHEN access.subject_id LIKE 'user:%'
|
|
140
|
+
THEN 'human' ELSE 'service' END,
|
|
141
|
+
'role', access.role,
|
|
142
|
+
'permissions', access.permissions,
|
|
143
|
+
'createdAt', access.created_at
|
|
144
|
+
) ORDER BY coalesce(access.subject_label, access.subject_id), access.id
|
|
145
|
+
) FROM workspace_memberships access
|
|
146
|
+
WHERE access.workspace_id = workspace.id
|
|
147
|
+
), '[]'::jsonb)
|
|
148
|
+
) ORDER BY lower(workspace.name), workspace.id
|
|
149
|
+
) FROM shared_workspaces workspace
|
|
150
|
+
), '[]'::jsonb)
|
|
151
|
+
)
|
|
152
|
+
ELSE NULL
|
|
153
|
+
END
|
|
154
|
+
INTO workspace_count, oversized_workspace, result
|
|
155
|
+
FROM managed_accounts account CROSS JOIN bounds
|
|
156
|
+
WHERE account.id = p_account_id;
|
|
157
|
+
IF workspace_count > 500 THEN
|
|
158
|
+
RAISE EXCEPTION 'organization workspace inventory exceeds the bounded projection'
|
|
159
|
+
USING ERRCODE = '54000';
|
|
160
|
+
END IF;
|
|
161
|
+
IF oversized_workspace THEN
|
|
162
|
+
RAISE EXCEPTION 'workspace access inventory exceeds the bounded projection'
|
|
163
|
+
USING ERRCODE = '54000';
|
|
164
|
+
END IF;
|
|
165
|
+
RETURN result;
|
|
166
|
+
END
|
|
167
|
+
$body$;
|
|
168
|
+
|
|
169
|
+
CREATE FUNCTION update_organization_name(
|
|
170
|
+
p_account_id uuid,
|
|
171
|
+
p_actor_subject_id text,
|
|
172
|
+
p_name text,
|
|
173
|
+
p_expected_updated_at timestamptz,
|
|
174
|
+
p_operation_id uuid
|
|
175
|
+
) RETURNS jsonb
|
|
176
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
177
|
+
SET search_path FROM CURRENT
|
|
178
|
+
AS $body$
|
|
179
|
+
DECLARE
|
|
180
|
+
actor organization_memberships%ROWTYPE;
|
|
181
|
+
account managed_accounts%ROWTYPE;
|
|
182
|
+
prior organization_profile_events%ROWTYPE;
|
|
183
|
+
normalized_name text := btrim(p_name);
|
|
184
|
+
previous_name_value text;
|
|
185
|
+
BEGIN
|
|
186
|
+
IF p_account_id IS NULL OR p_name IS NULL OR p_operation_id IS NULL
|
|
187
|
+
OR p_expected_updated_at IS NULL
|
|
188
|
+
OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
189
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
190
|
+
OR char_length(normalized_name) NOT BETWEEN 1 AND 120
|
|
191
|
+
OR octet_length(normalized_name) > 480
|
|
192
|
+
THEN
|
|
193
|
+
RAISE EXCEPTION 'organization name request is invalid' USING ERRCODE = '22023';
|
|
194
|
+
END IF;
|
|
195
|
+
PERFORM pg_catalog.set_config(
|
|
196
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
197
|
+
'organization_membership_lifecycle', true
|
|
198
|
+
);
|
|
199
|
+
SELECT * INTO account FROM managed_accounts candidate
|
|
200
|
+
WHERE candidate.id = p_account_id FOR UPDATE;
|
|
201
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002'; END IF;
|
|
202
|
+
SELECT * INTO actor FROM organization_memberships membership
|
|
203
|
+
WHERE membership.account_id = p_account_id
|
|
204
|
+
AND membership.subject_id = p_actor_subject_id
|
|
205
|
+
FOR SHARE;
|
|
206
|
+
IF NOT FOUND OR actor.status <> 'active' OR actor.role NOT IN ('owner', 'admin') THEN
|
|
207
|
+
RAISE EXCEPTION 'organization administration required' USING ERRCODE = '42501';
|
|
208
|
+
END IF;
|
|
209
|
+
SELECT * INTO prior FROM organization_profile_events event
|
|
210
|
+
WHERE event.id = p_operation_id;
|
|
211
|
+
IF FOUND THEN
|
|
212
|
+
IF prior.account_id IS DISTINCT FROM p_account_id
|
|
213
|
+
OR prior.requested_name IS DISTINCT FROM normalized_name
|
|
214
|
+
OR prior.expected_updated_at IS DISTINCT FROM p_expected_updated_at
|
|
215
|
+
THEN
|
|
216
|
+
RAISE EXCEPTION 'organization name operation key was reused'
|
|
217
|
+
USING ERRCODE = '23505';
|
|
218
|
+
END IF;
|
|
219
|
+
account.name := prior.requested_name;
|
|
220
|
+
account.updated_at := prior.result_updated_at;
|
|
221
|
+
ELSE
|
|
222
|
+
IF account.updated_at IS DISTINCT FROM p_expected_updated_at THEN
|
|
223
|
+
RAISE EXCEPTION 'organization changed before rename' USING ERRCODE = '40001';
|
|
224
|
+
END IF;
|
|
225
|
+
previous_name_value := account.name;
|
|
226
|
+
UPDATE managed_accounts SET name = normalized_name, updated_at = clock_timestamp()
|
|
227
|
+
WHERE id = p_account_id RETURNING * INTO account;
|
|
228
|
+
INSERT INTO organization_profile_events (
|
|
229
|
+
id, account_id, actor_membership_id, previous_name, requested_name,
|
|
230
|
+
expected_updated_at, result_updated_at
|
|
231
|
+
) VALUES (
|
|
232
|
+
p_operation_id, p_account_id, actor.id, previous_name_value, normalized_name,
|
|
233
|
+
p_expected_updated_at, account.updated_at
|
|
234
|
+
);
|
|
235
|
+
END IF;
|
|
236
|
+
RETURN pg_catalog.jsonb_build_object(
|
|
237
|
+
'id', account.id,
|
|
238
|
+
'name', account.name,
|
|
239
|
+
'createdAt', account.created_at,
|
|
240
|
+
'updatedAt', account.updated_at
|
|
241
|
+
);
|
|
242
|
+
END
|
|
243
|
+
$body$;
|
|
244
|
+
|
|
245
|
+
CREATE FUNCTION open_private_session_create_capability(
|
|
246
|
+
p_account_id uuid,
|
|
247
|
+
p_workspace_id uuid,
|
|
248
|
+
p_session_id uuid,
|
|
249
|
+
p_actor_subject_id text
|
|
250
|
+
) RETURNS TABLE (capability_id uuid, owner_membership_id uuid)
|
|
251
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
252
|
+
SET search_path FROM CURRENT
|
|
253
|
+
AS $body$
|
|
254
|
+
DECLARE
|
|
255
|
+
new_capability_id uuid := gen_random_uuid();
|
|
256
|
+
actor_membership_id uuid;
|
|
257
|
+
actor_personal_workspace boolean;
|
|
258
|
+
workspace_access_id uuid;
|
|
259
|
+
BEGIN
|
|
260
|
+
IF p_account_id IS NULL OR p_workspace_id IS NULL OR p_session_id IS NULL
|
|
261
|
+
OR p_actor_subject_id IS NULL OR p_actor_subject_id NOT LIKE 'user:%'
|
|
262
|
+
OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
263
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
264
|
+
OR p_workspace_id IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
265
|
+
OR NOT session_tenancy_product_activated(p_account_id, 1)
|
|
266
|
+
THEN
|
|
267
|
+
RAISE EXCEPTION 'private session create authority required' USING ERRCODE = '42501';
|
|
268
|
+
END IF;
|
|
269
|
+
-- Match the organization lifecycle's workspace -> membership prefix so
|
|
270
|
+
-- suspension/offboarding cannot pass between authorization and INSERT.
|
|
271
|
+
PERFORM 1 FROM workspace_inference_controls control
|
|
272
|
+
WHERE control.account_id = p_account_id AND control.workspace_id = p_workspace_id
|
|
273
|
+
FOR SHARE;
|
|
274
|
+
PERFORM 1 FROM workspaces workspace
|
|
275
|
+
WHERE workspace.account_id = p_account_id AND workspace.id = p_workspace_id
|
|
276
|
+
FOR KEY SHARE;
|
|
277
|
+
SELECT membership.id, membership.personal_workspace_id = p_workspace_id
|
|
278
|
+
INTO actor_membership_id, actor_personal_workspace
|
|
279
|
+
FROM organization_memberships membership
|
|
280
|
+
WHERE membership.account_id = p_account_id
|
|
281
|
+
AND membership.subject_id = p_actor_subject_id
|
|
282
|
+
AND membership.status = 'active'
|
|
283
|
+
FOR KEY SHARE;
|
|
284
|
+
IF NOT FOUND THEN
|
|
285
|
+
RAISE EXCEPTION 'private session create authority required' USING ERRCODE = '42501';
|
|
286
|
+
END IF;
|
|
287
|
+
-- An ordinary workspace's exact access row is authority, not just evidence.
|
|
288
|
+
-- Lock it after the canonical workspace + organization-membership prefix so
|
|
289
|
+
-- workspace-membership removal cannot miss an uncommitted private session,
|
|
290
|
+
-- delete access, and let this transaction commit from an older snapshot.
|
|
291
|
+
IF NOT actor_personal_workspace THEN
|
|
292
|
+
SELECT access.id INTO workspace_access_id
|
|
293
|
+
FROM workspace_memberships access
|
|
294
|
+
WHERE access.account_id = p_account_id
|
|
295
|
+
AND access.workspace_id = p_workspace_id
|
|
296
|
+
AND access.subject_id = p_actor_subject_id
|
|
297
|
+
FOR KEY SHARE;
|
|
298
|
+
IF NOT FOUND THEN
|
|
299
|
+
RAISE EXCEPTION 'private session create authority required' USING ERRCODE = '42501';
|
|
300
|
+
END IF;
|
|
301
|
+
END IF;
|
|
302
|
+
PERFORM pg_catalog.set_config(
|
|
303
|
+
'opengeni.private_session_create_lifecycle', 'private_session_create', true
|
|
304
|
+
);
|
|
305
|
+
INSERT INTO private_session_create_capabilities (
|
|
306
|
+
backend_pid, transaction_id, capability_id, account_id, workspace_id,
|
|
307
|
+
session_id, actor_subject_id, owner_membership_id
|
|
308
|
+
) VALUES (
|
|
309
|
+
pg_backend_pid(), pg_current_xact_id(), new_capability_id, p_account_id,
|
|
310
|
+
p_workspace_id, p_session_id, p_actor_subject_id, actor_membership_id
|
|
311
|
+
);
|
|
312
|
+
PERFORM pg_catalog.set_config(
|
|
313
|
+
'opengeni.private_session_create_capability', new_capability_id::text, true
|
|
314
|
+
);
|
|
315
|
+
capability_id := new_capability_id;
|
|
316
|
+
owner_membership_id := actor_membership_id;
|
|
317
|
+
RETURN NEXT;
|
|
318
|
+
END
|
|
319
|
+
$body$;
|
|
320
|
+
|
|
321
|
+
CREATE FUNCTION close_private_session_create_capability(p_capability_id uuid)
|
|
322
|
+
RETURNS void
|
|
323
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
324
|
+
SET search_path FROM CURRENT
|
|
325
|
+
AS $body$
|
|
326
|
+
BEGIN
|
|
327
|
+
PERFORM pg_catalog.set_config(
|
|
328
|
+
'opengeni.private_session_create_lifecycle', 'private_session_create', true
|
|
329
|
+
);
|
|
330
|
+
DELETE FROM private_session_create_capabilities capability
|
|
331
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
332
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
333
|
+
AND capability.capability_id = p_capability_id;
|
|
334
|
+
PERFORM pg_catalog.set_config('opengeni.private_session_create_capability', '', true);
|
|
335
|
+
END
|
|
336
|
+
$body$;
|
|
337
|
+
|
|
338
|
+
CREATE FUNCTION fence_session_create_requested_visibility_update()
|
|
339
|
+
RETURNS trigger
|
|
340
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
341
|
+
SET search_path FROM CURRENT
|
|
342
|
+
AS $body$
|
|
343
|
+
BEGIN
|
|
344
|
+
IF NEW.create_requested_visibility IS DISTINCT FROM OLD.create_requested_visibility THEN
|
|
345
|
+
RAISE EXCEPTION 'session requested visibility is immutable' USING ERRCODE = '42501';
|
|
346
|
+
END IF;
|
|
347
|
+
RETURN NEW;
|
|
348
|
+
END
|
|
349
|
+
$body$;
|
|
350
|
+
|
|
351
|
+
CREATE TRIGGER sessions_create_requested_visibility_immutable
|
|
352
|
+
BEFORE UPDATE ON sessions
|
|
353
|
+
FOR EACH ROW EXECUTE FUNCTION fence_session_create_requested_visibility_update();
|
|
354
|
+
|
|
355
|
+
REVOKE ALL ON FUNCTION get_organization_administration_overview(uuid,text) FROM PUBLIC;
|
|
356
|
+
|
|
357
|
+
DO $private_session_create_guard$
|
|
358
|
+
DECLARE
|
|
359
|
+
data_schema text := current_schema();
|
|
360
|
+
BEGIN
|
|
361
|
+
EXECUTE format($ddl$
|
|
362
|
+
CREATE OR REPLACE FUNCTION %1$I.guard_session_authority_write()
|
|
363
|
+
RETURNS trigger
|
|
364
|
+
LANGUAGE plpgsql
|
|
365
|
+
SECURITY DEFINER
|
|
366
|
+
SET search_path = %1$I, pg_catalog
|
|
367
|
+
AS $body$
|
|
368
|
+
DECLARE
|
|
369
|
+
resolved_owner_id uuid;
|
|
370
|
+
resolved_owner_subject_id text;
|
|
371
|
+
private_create_capability_id uuid;
|
|
372
|
+
creator_has_active_membership boolean;
|
|
373
|
+
owner_provenance_supplied boolean :=
|
|
374
|
+
NEW.owner_organization_membership_id IS NOT NULL
|
|
375
|
+
OR NEW.owner_subject_id IS NOT NULL;
|
|
376
|
+
previous_lifecycle_marker text := pg_catalog.current_setting(
|
|
377
|
+
'opengeni.organization_tenancy_lifecycle', true
|
|
378
|
+
);
|
|
379
|
+
BEGIN
|
|
380
|
+
IF TG_OP = 'INSERT'
|
|
381
|
+
AND NEW.owner_organization_membership_id IS NULL
|
|
382
|
+
AND NEW.owner_subject_id IS NULL
|
|
383
|
+
THEN
|
|
384
|
+
IF NEW.parent_session_id IS NOT NULL THEN
|
|
385
|
+
SELECT parent.owner_organization_membership_id, parent.owner_subject_id
|
|
386
|
+
INTO resolved_owner_id, resolved_owner_subject_id
|
|
387
|
+
FROM sessions parent
|
|
388
|
+
WHERE parent.account_id = NEW.account_id
|
|
389
|
+
AND parent.workspace_id = NEW.workspace_id
|
|
390
|
+
AND parent.id = NEW.parent_session_id;
|
|
391
|
+
ELSIF NEW.created_by_kind = 'subject'
|
|
392
|
+
AND NEW.created_by_subject_id IS NOT NULL
|
|
393
|
+
THEN
|
|
394
|
+
PERFORM pg_catalog.set_config(
|
|
395
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
396
|
+
'session_visibility_activation',
|
|
397
|
+
true
|
|
398
|
+
);
|
|
399
|
+
SELECT membership.id, membership.subject_id
|
|
400
|
+
INTO resolved_owner_id, resolved_owner_subject_id
|
|
401
|
+
FROM organization_memberships membership
|
|
402
|
+
WHERE membership.account_id = NEW.account_id
|
|
403
|
+
AND membership.subject_id = NEW.created_by_subject_id
|
|
404
|
+
AND membership.status = 'active'
|
|
405
|
+
AND (
|
|
406
|
+
-- Stated authority, read from the authority row itself: a managed
|
|
407
|
+
-- human's personal workspace has no workspace_memberships row by
|
|
408
|
+
-- design (0219), so the membership's own personal-workspace
|
|
409
|
+
-- pointer is the authority for exactly that one workspace. Same
|
|
410
|
+
-- shape as 0258's personal Document authority.
|
|
411
|
+
--
|
|
412
|
+
-- Restricted to `user:%%` because 0219 and 0263 gate the ENTIRE
|
|
413
|
+
-- managed-human lifecycle - provisioning, invitation, acceptance
|
|
414
|
+
-- - to that prefix. A personal-workspace pointer on an
|
|
415
|
+
-- `api_key:`/`configured:` membership is not a shape any
|
|
416
|
+
-- lifecycle can produce, and 0297's classifier calls that lane
|
|
417
|
+
-- (`external_lane_owns_row`) permanently unrepairable. The write
|
|
418
|
+
-- path must not attribute what the classifier refuses.
|
|
419
|
+
(
|
|
420
|
+
NEW.created_by_subject_id LIKE 'user:%%'
|
|
421
|
+
AND membership.personal_workspace_id = NEW.workspace_id
|
|
422
|
+
)
|
|
423
|
+
OR EXISTS (
|
|
424
|
+
SELECT 1
|
|
425
|
+
FROM workspace_memberships workspace_membership
|
|
426
|
+
WHERE workspace_membership.account_id = NEW.account_id
|
|
427
|
+
AND workspace_membership.workspace_id = NEW.workspace_id
|
|
428
|
+
AND workspace_membership.subject_id = membership.subject_id
|
|
429
|
+
)
|
|
430
|
+
);
|
|
431
|
+
IF NOT FOUND THEN
|
|
432
|
+
-- Classify the miss instead of writing an indistinguishable NULL.
|
|
433
|
+
SELECT EXISTS (
|
|
434
|
+
SELECT 1
|
|
435
|
+
FROM organization_memberships creator_membership
|
|
436
|
+
WHERE creator_membership.account_id = NEW.account_id
|
|
437
|
+
AND creator_membership.subject_id = NEW.created_by_subject_id
|
|
438
|
+
AND creator_membership.status = 'active'
|
|
439
|
+
AND NEW.created_by_subject_id LIKE 'user:%%'
|
|
440
|
+
) INTO creator_has_active_membership;
|
|
441
|
+
IF creator_has_active_membership
|
|
442
|
+
AND EXISTS (
|
|
443
|
+
SELECT 1
|
|
444
|
+
FROM organization_memberships personal_owner
|
|
445
|
+
WHERE personal_owner.account_id = NEW.account_id
|
|
446
|
+
AND personal_owner.personal_workspace_id = NEW.workspace_id
|
|
447
|
+
AND personal_owner.status = 'active'
|
|
448
|
+
AND personal_owner.subject_id LIKE 'user:%%'
|
|
449
|
+
)
|
|
450
|
+
THEN
|
|
451
|
+
RAISE EXCEPTION
|
|
452
|
+
'session owner authority is unresolved in a personal workspace'
|
|
453
|
+
USING ERRCODE = '55000';
|
|
454
|
+
END IF;
|
|
455
|
+
-- Otherwise genuinely ownerless: no active organization membership,
|
|
456
|
+
-- or no stated authority over an ordinary shared workspace.
|
|
457
|
+
END IF;
|
|
458
|
+
END IF;
|
|
459
|
+
NEW.owner_organization_membership_id := resolved_owner_id;
|
|
460
|
+
NEW.owner_subject_id := resolved_owner_subject_id;
|
|
461
|
+
PERFORM pg_catalog.set_config(
|
|
462
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
463
|
+
CASE WHEN previous_lifecycle_marker IS NULL THEN '' ELSE previous_lifecycle_marker END,
|
|
464
|
+
true
|
|
465
|
+
);
|
|
466
|
+
END IF;
|
|
467
|
+
|
|
468
|
+
IF (NEW.owner_organization_membership_id IS NULL)
|
|
469
|
+
<> (NEW.owner_subject_id IS NULL)
|
|
470
|
+
OR (NEW.visibility = 'user_private'
|
|
471
|
+
AND NEW.owner_organization_membership_id IS NULL)
|
|
472
|
+
THEN
|
|
473
|
+
RAISE EXCEPTION 'session authority owner provenance is incomplete'
|
|
474
|
+
USING ERRCODE = '23514';
|
|
475
|
+
END IF;
|
|
476
|
+
|
|
477
|
+
IF TG_OP = 'UPDATE'
|
|
478
|
+
AND (
|
|
479
|
+
NEW.visibility IS DISTINCT FROM OLD.visibility
|
|
480
|
+
OR NEW.owner_organization_membership_id
|
|
481
|
+
IS DISTINCT FROM OLD.owner_organization_membership_id
|
|
482
|
+
OR NEW.owner_subject_id IS DISTINCT FROM OLD.owner_subject_id
|
|
483
|
+
OR NEW.authority_epoch IS DISTINCT FROM OLD.authority_epoch
|
|
484
|
+
OR NEW.forked_from_session_id IS DISTINCT FROM OLD.forked_from_session_id
|
|
485
|
+
OR NEW.forked_from_authority_epoch
|
|
486
|
+
IS DISTINCT FROM OLD.forked_from_authority_epoch
|
|
487
|
+
OR NEW.forked_from_visibility IS DISTINCT FROM OLD.forked_from_visibility
|
|
488
|
+
OR NEW.forked_at IS DISTINCT FROM OLD.forked_at
|
|
489
|
+
OR NEW.forked_by_organization_membership_id
|
|
490
|
+
IS DISTINCT FROM OLD.forked_by_organization_membership_id
|
|
491
|
+
)
|
|
492
|
+
AND NOT EXISTS (
|
|
493
|
+
SELECT 1
|
|
494
|
+
FROM session_visibility_write_capabilities capability
|
|
495
|
+
WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
|
|
496
|
+
AND capability.transaction_id = pg_catalog.pg_current_xact_id()
|
|
497
|
+
AND capability.capability_id = nullif(
|
|
498
|
+
pg_catalog.current_setting(
|
|
499
|
+
'opengeni.session_visibility_write_capability', true
|
|
500
|
+
),
|
|
501
|
+
''
|
|
502
|
+
)::uuid
|
|
503
|
+
)
|
|
504
|
+
THEN
|
|
505
|
+
RAISE EXCEPTION 'session authority changes require the lifecycle capability'
|
|
506
|
+
USING ERRCODE = '42501';
|
|
507
|
+
END IF;
|
|
508
|
+
|
|
509
|
+
IF TG_OP = 'INSERT' THEN
|
|
510
|
+
PERFORM pg_catalog.set_config(
|
|
511
|
+
'opengeni.private_session_create_lifecycle', 'private_session_create', true
|
|
512
|
+
);
|
|
513
|
+
DELETE FROM private_session_create_capabilities capability
|
|
514
|
+
WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
|
|
515
|
+
AND capability.transaction_id = pg_catalog.pg_current_xact_id()
|
|
516
|
+
AND capability.capability_id = nullif(
|
|
517
|
+
pg_catalog.current_setting(
|
|
518
|
+
'opengeni.private_session_create_capability', true
|
|
519
|
+
),
|
|
520
|
+
''
|
|
521
|
+
)::uuid
|
|
522
|
+
AND capability.account_id = NEW.account_id
|
|
523
|
+
AND capability.workspace_id = NEW.workspace_id
|
|
524
|
+
AND capability.session_id = NEW.id
|
|
525
|
+
AND capability.actor_subject_id = NEW.created_by_subject_id
|
|
526
|
+
AND capability.owner_membership_id = NEW.owner_organization_membership_id
|
|
527
|
+
AND NEW.created_by_kind = 'subject'
|
|
528
|
+
AND NEW.owner_subject_id = capability.actor_subject_id
|
|
529
|
+
AND NEW.visibility = 'user_private'
|
|
530
|
+
AND NEW.create_requested_visibility = 'user_private'
|
|
531
|
+
AND NEW.authority_epoch = 1
|
|
532
|
+
AND NEW.parent_session_id IS NULL
|
|
533
|
+
AND NEW.sandbox_group_id = NEW.id
|
|
534
|
+
AND NEW.forked_from_session_id IS NULL
|
|
535
|
+
AND NEW.forked_from_authority_epoch IS NULL
|
|
536
|
+
AND NEW.forked_from_visibility IS NULL
|
|
537
|
+
AND NEW.forked_at IS NULL
|
|
538
|
+
AND NEW.forked_by_organization_membership_id IS NULL
|
|
539
|
+
RETURNING capability.capability_id INTO private_create_capability_id;
|
|
540
|
+
IF private_create_capability_id IS NOT NULL THEN
|
|
541
|
+
PERFORM pg_catalog.set_config('opengeni.private_session_create_capability', '', true);
|
|
542
|
+
END IF;
|
|
543
|
+
END IF;
|
|
544
|
+
|
|
545
|
+
IF TG_OP = 'INSERT'
|
|
546
|
+
AND (
|
|
547
|
+
NEW.visibility <> 'workspace_shared'
|
|
548
|
+
OR NEW.authority_epoch <> 1
|
|
549
|
+
OR NEW.forked_from_session_id IS NOT NULL
|
|
550
|
+
OR NEW.forked_from_authority_epoch IS NOT NULL
|
|
551
|
+
OR NEW.forked_from_visibility IS NOT NULL
|
|
552
|
+
OR NEW.forked_at IS NOT NULL
|
|
553
|
+
OR NEW.forked_by_organization_membership_id IS NOT NULL
|
|
554
|
+
OR owner_provenance_supplied
|
|
555
|
+
)
|
|
556
|
+
AND private_create_capability_id IS NULL
|
|
557
|
+
AND NOT EXISTS (
|
|
558
|
+
SELECT 1
|
|
559
|
+
FROM session_visibility_write_capabilities capability
|
|
560
|
+
WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
|
|
561
|
+
AND capability.transaction_id = pg_catalog.pg_current_xact_id()
|
|
562
|
+
AND capability.capability_id = nullif(
|
|
563
|
+
pg_catalog.current_setting(
|
|
564
|
+
'opengeni.session_visibility_write_capability', true
|
|
565
|
+
),
|
|
566
|
+
''
|
|
567
|
+
)::uuid
|
|
568
|
+
)
|
|
569
|
+
THEN
|
|
570
|
+
RAISE EXCEPTION 'non-default session authority requires the lifecycle capability'
|
|
571
|
+
USING ERRCODE = '42501';
|
|
572
|
+
END IF;
|
|
573
|
+
RETURN NEW;
|
|
574
|
+
EXCEPTION WHEN OTHERS THEN
|
|
575
|
+
PERFORM pg_catalog.set_config(
|
|
576
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
577
|
+
CASE WHEN previous_lifecycle_marker IS NULL THEN '' ELSE previous_lifecycle_marker END,
|
|
578
|
+
true
|
|
579
|
+
);
|
|
580
|
+
RAISE;
|
|
581
|
+
END;
|
|
582
|
+
$body$;
|
|
583
|
+
$ddl$, data_schema);
|
|
584
|
+
|
|
585
|
+
EXECUTE format(
|
|
586
|
+
'ALTER FUNCTION %I.guard_session_authority_write() SET search_path = pg_catalog, %I, pg_temp',
|
|
587
|
+
data_schema, data_schema
|
|
588
|
+
);
|
|
589
|
+
EXECUTE format(
|
|
590
|
+
'COMMENT ON FUNCTION %I.guard_session_authority_write() IS %L',
|
|
591
|
+
data_schema,
|
|
592
|
+
'Session authority write fence (0225, repaired by 0302 and 0311). '
|
|
593
|
+
|| 'Private creation accepts one exact account/workspace/session/subject/owner capability '
|
|
594
|
+
|| 'on INSERT only and consumes it before the write; existing visibility lifecycle '
|
|
595
|
+
|| 'capabilities remain unchanged.'
|
|
596
|
+
);
|
|
597
|
+
END
|
|
598
|
+
$private_session_create_guard$;
|
|
599
|
+
|
|
600
|
+
REVOKE ALL ON FUNCTION update_organization_name(uuid,text,text,timestamptz,uuid) FROM PUBLIC;
|
|
601
|
+
REVOKE ALL ON FUNCTION open_private_session_create_capability(uuid,uuid,uuid,text) FROM PUBLIC;
|
|
602
|
+
REVOKE ALL ON FUNCTION close_private_session_create_capability(uuid) FROM PUBLIC;
|
|
603
|
+
REVOKE ALL ON FUNCTION fence_session_create_requested_visibility_update() FROM PUBLIC;
|
|
604
|
+
|
|
605
|
+
DO $pin_and_grant$
|
|
606
|
+
DECLARE data_schema text := current_schema();
|
|
607
|
+
BEGIN
|
|
608
|
+
EXECUTE format(
|
|
609
|
+
'ALTER FUNCTION %I.get_organization_administration_overview(uuid,text) SET search_path = pg_catalog, %I, pg_temp',
|
|
610
|
+
data_schema, data_schema
|
|
611
|
+
);
|
|
612
|
+
EXECUTE format(
|
|
613
|
+
'ALTER FUNCTION %I.update_organization_name(uuid,text,text,timestamptz,uuid) SET search_path = pg_catalog, %I, pg_temp',
|
|
614
|
+
data_schema, data_schema
|
|
615
|
+
);
|
|
616
|
+
EXECUTE format(
|
|
617
|
+
'ALTER FUNCTION %I.open_private_session_create_capability(uuid,uuid,uuid,text) SET search_path = pg_catalog, %I, pg_temp',
|
|
618
|
+
data_schema, data_schema
|
|
619
|
+
);
|
|
620
|
+
EXECUTE format(
|
|
621
|
+
'ALTER FUNCTION %I.close_private_session_create_capability(uuid) SET search_path = pg_catalog, %I, pg_temp',
|
|
622
|
+
data_schema, data_schema
|
|
623
|
+
);
|
|
624
|
+
EXECUTE format(
|
|
625
|
+
'ALTER FUNCTION %I.fence_session_create_requested_visibility_update() SET search_path = pg_catalog, %I, pg_temp',
|
|
626
|
+
data_schema, data_schema
|
|
627
|
+
);
|
|
628
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
629
|
+
EXECUTE format(
|
|
630
|
+
'REVOKE ALL ON TABLE %I.private_session_create_capabilities FROM opengeni_app',
|
|
631
|
+
data_schema
|
|
632
|
+
);
|
|
633
|
+
EXECUTE format(
|
|
634
|
+
'GRANT EXECUTE ON FUNCTION %I.get_organization_administration_overview(uuid,text) TO opengeni_app',
|
|
635
|
+
data_schema
|
|
636
|
+
);
|
|
637
|
+
EXECUTE format(
|
|
638
|
+
'GRANT EXECUTE ON FUNCTION %I.update_organization_name(uuid,text,text,timestamptz,uuid) TO opengeni_app',
|
|
639
|
+
data_schema
|
|
640
|
+
);
|
|
641
|
+
EXECUTE format(
|
|
642
|
+
'GRANT EXECUTE ON FUNCTION %I.open_private_session_create_capability(uuid,uuid,uuid,text) TO opengeni_app',
|
|
643
|
+
data_schema
|
|
644
|
+
);
|
|
645
|
+
EXECUTE format(
|
|
646
|
+
'GRANT EXECUTE ON FUNCTION %I.close_private_session_create_capability(uuid) TO opengeni_app',
|
|
647
|
+
data_schema
|
|
648
|
+
);
|
|
649
|
+
END IF;
|
|
650
|
+
END
|
|
651
|
+
$pin_and_grant$;
|
|
652
|
+
|
|
653
|
+
COMMENT ON FUNCTION get_organization_administration_overview(uuid,text) IS
|
|
654
|
+
'Bounded organization-admin projection of canonical organization identity and shared workspace access; personal workspaces are excluded in the database.';
|
|
655
|
+
COMMENT ON FUNCTION open_private_session_create_capability(uuid,uuid,uuid,text) IS
|
|
656
|
+
'Transaction-local capability for one activated managed-human private session insert.';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Allow the explicit quiescent session-tree deletion lifecycle to remove the
|
|
3
|
+
-- rows that are owned exclusively by that tree. Cross-session and workspace
|
|
4
|
+
-- provenance keeps its existing RESTRICT fence, so published artifacts,
|
|
5
|
+
-- governed learning evidence, forks, and other durable external references
|
|
6
|
+
-- fail closed instead of being silently orphaned.
|
|
7
|
+
|
|
8
|
+
SET lock_timeout = '5s';
|
|
9
|
+
SET statement_timeout = '10min';
|
|
10
|
+
|
|
11
|
+
ALTER TABLE sessions
|
|
12
|
+
DROP CONSTRAINT sessions_workspace_parent_fk,
|
|
13
|
+
ADD CONSTRAINT sessions_workspace_parent_fk
|
|
14
|
+
FOREIGN KEY (workspace_id, parent_session_id)
|
|
15
|
+
REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
|
|
16
|
+
|
|
17
|
+
ALTER TABLE session_turn_attempts
|
|
18
|
+
DROP CONSTRAINT session_turn_attempts_workspace_session_fk,
|
|
19
|
+
ADD CONSTRAINT session_turn_attempts_workspace_session_fk
|
|
20
|
+
FOREIGN KEY (workspace_id, session_id)
|
|
21
|
+
REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
|
|
22
|
+
|
|
23
|
+
ALTER TABLE session_command_receipts
|
|
24
|
+
DROP CONSTRAINT session_command_receipts_target_session_fk,
|
|
25
|
+
ADD CONSTRAINT session_command_receipts_target_session_fk
|
|
26
|
+
FOREIGN KEY (workspace_id, target_session_id)
|
|
27
|
+
REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
|
|
28
|
+
|
|
29
|
+
ALTER TABLE workspace_control_events
|
|
30
|
+
DROP CONSTRAINT workspace_control_events_root_session_fk,
|
|
31
|
+
ADD CONSTRAINT workspace_control_events_root_session_fk
|
|
32
|
+
FOREIGN KEY (workspace_id, root_session_id)
|
|
33
|
+
REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
|
|
34
|
+
|
|
35
|
+
ALTER TABLE session_attempt_interruptions
|
|
36
|
+
DROP CONSTRAINT session_attempt_interruptions_workspace_session_fk,
|
|
37
|
+
ADD CONSTRAINT session_attempt_interruptions_workspace_session_fk
|
|
38
|
+
FOREIGN KEY (workspace_id, session_id)
|
|
39
|
+
REFERENCES sessions(workspace_id, id) ON DELETE CASCADE;
|