@opengeni/db 1.0.1 → 3.2.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-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
- package/dist/chunk-2EH4WRHL.js.map +1 -0
- package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
- package/dist/chunk-6HMM2R5T.js +390 -0
- package/dist/chunk-6HMM2R5T.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
- package/dist/chunk-AZHW7R5V.js.map +1 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
- package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
- package/dist/chunk-KRLPEMLH.js.map +1 -0
- package/dist/chunk-NAB6FKFN.js +516 -0
- package/dist/chunk-NAB6FKFN.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-UZNXG44K.js +4508 -0
- package/dist/chunk-UZNXG44K.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 +85 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +23 -4
- package/dist/editable-artifact-durable-export.js +2 -2
- 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 +1606 -81
- package/dist/index.js +21898 -8147
- 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/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 +29 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -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 +43 -4
- package/dist/session-realtime-ledger.d.ts +2 -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 +268 -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 +28 -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/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -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/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
- package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
- package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
- package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
- package/drizzle/0346_document_migration_audit_surface.sql +454 -0
- package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -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 +167 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +87 -2
- 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 +11676 -1550
- 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/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 +995 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +905 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1317 -72
- package/src/schema.ts +3316 -428
- 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 +441 -85
- package/src/session-realtime-context.ts +4 -6
- package/src/session-realtime-ledger.ts +28 -14
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +410 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +867 -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 +138 -11
- 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-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- This is a drained runtime cutover. Old workers neither materialize accepted
|
|
3
|
+
-- connection authority nor carry exact attempt/use fences and must not restart.
|
|
4
|
+
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '10min';
|
|
7
|
+
|
|
8
|
+
DO $connection_authority_writer_drain_before_lock$
|
|
9
|
+
BEGIN
|
|
10
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
|
|
11
|
+
AND EXISTS (
|
|
12
|
+
SELECT 1 FROM pg_stat_activity
|
|
13
|
+
WHERE datname = current_database()
|
|
14
|
+
AND usename = 'opengeni_app'
|
|
15
|
+
AND pid <> pg_backend_pid()
|
|
16
|
+
)
|
|
17
|
+
THEN
|
|
18
|
+
RAISE EXCEPTION
|
|
19
|
+
'0264 connection authority activation requires all opengeni_app sessions to be stopped'
|
|
20
|
+
USING ERRCODE = '55000';
|
|
21
|
+
END IF;
|
|
22
|
+
END
|
|
23
|
+
$connection_authority_writer_drain_before_lock$;
|
|
24
|
+
|
|
25
|
+
LOCK TABLE sessions IN ACCESS EXCLUSIVE MODE;
|
|
26
|
+
LOCK TABLE session_turns IN ACCESS EXCLUSIVE MODE;
|
|
27
|
+
LOCK TABLE session_system_updates IN ACCESS EXCLUSIVE MODE;
|
|
28
|
+
LOCK TABLE session_system_update_outbox IN ACCESS EXCLUSIVE MODE;
|
|
29
|
+
LOCK TABLE scheduled_tasks IN ACCESS EXCLUSIVE MODE;
|
|
30
|
+
LOCK TABLE connections IN ACCESS EXCLUSIVE MODE;
|
|
31
|
+
|
|
32
|
+
DO $connection_authority_writer_drain_after_lock$
|
|
33
|
+
BEGIN
|
|
34
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
|
|
35
|
+
AND EXISTS (
|
|
36
|
+
SELECT 1 FROM pg_stat_activity
|
|
37
|
+
WHERE datname = current_database()
|
|
38
|
+
AND usename = 'opengeni_app'
|
|
39
|
+
AND pid <> pg_backend_pid()
|
|
40
|
+
)
|
|
41
|
+
THEN
|
|
42
|
+
RAISE EXCEPTION
|
|
43
|
+
'0264 connection authority activation requires all opengeni_app sessions to be stopped'
|
|
44
|
+
USING ERRCODE = '55000';
|
|
45
|
+
END IF;
|
|
46
|
+
END
|
|
47
|
+
$connection_authority_writer_drain_after_lock$;
|
|
48
|
+
|
|
49
|
+
-- The maintenance label is operational guidance; this data precondition is
|
|
50
|
+
-- the durable cutover fence. Migration 0256 activated common-user Connection
|
|
51
|
+
-- rows before accepted-work snapshots existed. Do not install the new runtime
|
|
52
|
+
-- while any executable frozen source could still carry one of those rows by
|
|
53
|
+
-- the legacy owner tuple without an explicit grant.
|
|
54
|
+
DO $accepted_work_drain$
|
|
55
|
+
BEGIN
|
|
56
|
+
IF EXISTS (
|
|
57
|
+
SELECT 1
|
|
58
|
+
FROM sessions session_value
|
|
59
|
+
CROSS JOIN LATERAL jsonb_array_elements(
|
|
60
|
+
session_value.initial_personal_connection_delegations
|
|
61
|
+
) delegation
|
|
62
|
+
JOIN connections connection_value
|
|
63
|
+
ON connection_value.id::text = delegation ->> 'connectionId'
|
|
64
|
+
AND connection_value.account_id = session_value.account_id
|
|
65
|
+
WHERE session_value.status <> 'cancelled'
|
|
66
|
+
AND NOT EXISTS (
|
|
67
|
+
SELECT 1 FROM session_turns existing_turn
|
|
68
|
+
WHERE existing_turn.workspace_id = session_value.workspace_id
|
|
69
|
+
AND existing_turn.session_id = session_value.id
|
|
70
|
+
)
|
|
71
|
+
AND connection_value.authority_scope = 'user'
|
|
72
|
+
) OR EXISTS (
|
|
73
|
+
SELECT 1
|
|
74
|
+
FROM session_turns turn_value
|
|
75
|
+
CROSS JOIN LATERAL jsonb_array_elements(
|
|
76
|
+
turn_value.personal_connection_delegations
|
|
77
|
+
) delegation
|
|
78
|
+
JOIN connections connection_value
|
|
79
|
+
ON connection_value.id::text = delegation ->> 'connectionId'
|
|
80
|
+
AND connection_value.account_id = turn_value.account_id
|
|
81
|
+
WHERE turn_value.status IN (
|
|
82
|
+
'queued', 'running', 'requires_action', 'recovering', 'waiting_capacity'
|
|
83
|
+
)
|
|
84
|
+
AND connection_value.authority_scope = 'user'
|
|
85
|
+
) OR EXISTS (
|
|
86
|
+
SELECT 1
|
|
87
|
+
FROM session_system_updates update_value
|
|
88
|
+
CROSS JOIN LATERAL jsonb_array_elements(
|
|
89
|
+
update_value.personal_connection_delegations
|
|
90
|
+
) delegation
|
|
91
|
+
JOIN connections connection_value
|
|
92
|
+
ON connection_value.id::text = delegation ->> 'connectionId'
|
|
93
|
+
AND connection_value.account_id = update_value.account_id
|
|
94
|
+
WHERE update_value.state = 'pending'
|
|
95
|
+
AND connection_value.authority_scope = 'user'
|
|
96
|
+
) OR EXISTS (
|
|
97
|
+
SELECT 1
|
|
98
|
+
FROM session_system_update_outbox outbox
|
|
99
|
+
CROSS JOIN LATERAL jsonb_array_elements(
|
|
100
|
+
outbox.personal_connection_delegations
|
|
101
|
+
) delegation
|
|
102
|
+
JOIN connections connection_value
|
|
103
|
+
ON connection_value.id::text = delegation ->> 'connectionId'
|
|
104
|
+
AND connection_value.account_id = outbox.account_id
|
|
105
|
+
WHERE outbox.status = 'pending'
|
|
106
|
+
AND connection_value.authority_scope = 'user'
|
|
107
|
+
) OR EXISTS (
|
|
108
|
+
SELECT 1
|
|
109
|
+
FROM scheduled_tasks task
|
|
110
|
+
CROSS JOIN LATERAL jsonb_array_elements(
|
|
111
|
+
task.personal_connection_delegations
|
|
112
|
+
) delegation
|
|
113
|
+
JOIN connections connection_value
|
|
114
|
+
ON connection_value.id::text = delegation ->> 'connectionId'
|
|
115
|
+
AND connection_value.account_id = task.account_id
|
|
116
|
+
WHERE task.status = 'active'
|
|
117
|
+
AND connection_value.authority_scope = 'user'
|
|
118
|
+
) THEN
|
|
119
|
+
RAISE EXCEPTION
|
|
120
|
+
'0264 requires draining or superseding executable pre-activation common-user connection work'
|
|
121
|
+
USING ERRCODE = '55000';
|
|
122
|
+
END IF;
|
|
123
|
+
END
|
|
124
|
+
$accepted_work_drain$;
|
|
125
|
+
|
|
126
|
+
CREATE TABLE "turn_connection_authority_snapshots" (
|
|
127
|
+
"account_id" uuid NOT NULL,
|
|
128
|
+
"workspace_id" uuid NOT NULL,
|
|
129
|
+
"session_id" uuid NOT NULL,
|
|
130
|
+
"turn_id" uuid NOT NULL,
|
|
131
|
+
"server_id" text NOT NULL,
|
|
132
|
+
"connection_id" uuid NOT NULL,
|
|
133
|
+
"connection_generation" bigint NOT NULL,
|
|
134
|
+
"origin_workspace_id" uuid NOT NULL,
|
|
135
|
+
"provider_domain" text NOT NULL,
|
|
136
|
+
"connection_kind" text NOT NULL,
|
|
137
|
+
"authority_scope" text NOT NULL,
|
|
138
|
+
"authority_source" text NOT NULL,
|
|
139
|
+
"owner_subject_id" text,
|
|
140
|
+
"owner_organization_membership_id" uuid,
|
|
141
|
+
"membership_authorization_revision" bigint,
|
|
142
|
+
"authority_id" uuid,
|
|
143
|
+
"authority_generation" bigint,
|
|
144
|
+
"grant_id" uuid,
|
|
145
|
+
"grant_generation" bigint,
|
|
146
|
+
"grant_mode" text,
|
|
147
|
+
"grant_context" text,
|
|
148
|
+
"grant_session_id" uuid,
|
|
149
|
+
"grant_authority_epoch" integer,
|
|
150
|
+
"session_visibility" text NOT NULL,
|
|
151
|
+
"session_authority_epoch" integer NOT NULL,
|
|
152
|
+
"canonical_snapshot" jsonb NOT NULL,
|
|
153
|
+
"snapshot_digest" bytea NOT NULL,
|
|
154
|
+
"created_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
155
|
+
PRIMARY KEY ("turn_id", "server_id"),
|
|
156
|
+
CONSTRAINT "turn_connection_authority_server_check" CHECK (
|
|
157
|
+
octet_length("server_id") BETWEEN 1 AND 256
|
|
158
|
+
),
|
|
159
|
+
CONSTRAINT "turn_connection_authority_generation_check" CHECK (
|
|
160
|
+
"connection_generation" > 0
|
|
161
|
+
AND "session_authority_epoch" > 0
|
|
162
|
+
AND ("membership_authorization_revision" IS NULL
|
|
163
|
+
OR "membership_authorization_revision" > 0)
|
|
164
|
+
AND ("authority_generation" IS NULL OR "authority_generation" > 0)
|
|
165
|
+
AND ("grant_generation" IS NULL OR "grant_generation" > 0)
|
|
166
|
+
),
|
|
167
|
+
CONSTRAINT "turn_connection_authority_grant_check" CHECK (
|
|
168
|
+
("authority_scope" = 'user'
|
|
169
|
+
AND "authority_source" = 'user_delegation'
|
|
170
|
+
AND "owner_subject_id" IS NOT NULL
|
|
171
|
+
AND "owner_organization_membership_id" IS NOT NULL
|
|
172
|
+
AND "membership_authorization_revision" > 0
|
|
173
|
+
AND "authority_id" IS NOT NULL AND "authority_generation" > 0
|
|
174
|
+
AND "grant_id" IS NOT NULL AND "grant_generation" > 0
|
|
175
|
+
AND "grant_mode" IN ('once', 'session', 'always')
|
|
176
|
+
AND "grant_context" IN ('user_private', 'workspace_shared')
|
|
177
|
+
AND (("grant_mode" = 'always' AND "grant_session_id" IS NULL
|
|
178
|
+
AND "grant_authority_epoch" IS NULL)
|
|
179
|
+
OR ("grant_mode" IN ('once', 'session') AND "grant_session_id" = "session_id"
|
|
180
|
+
AND "grant_authority_epoch" > 0)))
|
|
181
|
+
OR ("authority_scope" = 'workspace'
|
|
182
|
+
AND "authority_source" IN ('explicit_workspace', 'legacy_workspace_omission')
|
|
183
|
+
AND "owner_subject_id" IS NULL
|
|
184
|
+
AND "owner_organization_membership_id" IS NULL
|
|
185
|
+
AND "membership_authorization_revision" IS NULL
|
|
186
|
+
AND "authority_id" IS NULL AND "authority_generation" IS NULL
|
|
187
|
+
AND "grant_id" IS NULL AND "grant_generation" IS NULL
|
|
188
|
+
AND "grant_mode" IS NULL AND "grant_context" IS NULL
|
|
189
|
+
AND "grant_session_id" IS NULL AND "grant_authority_epoch" IS NULL)
|
|
190
|
+
OR ("authority_scope" = 'legacy_user'
|
|
191
|
+
AND "authority_source" = 'legacy_user_compatibility'
|
|
192
|
+
AND "owner_subject_id" IS NOT NULL
|
|
193
|
+
AND "owner_organization_membership_id" IS NULL
|
|
194
|
+
AND "membership_authorization_revision" IS NULL
|
|
195
|
+
AND "authority_id" IS NULL AND "authority_generation" IS NULL
|
|
196
|
+
AND "grant_id" IS NULL AND "grant_generation" IS NULL
|
|
197
|
+
AND "grant_mode" IS NULL AND "grant_context" IS NULL
|
|
198
|
+
AND "grant_session_id" IS NULL AND "grant_authority_epoch" IS NULL)
|
|
199
|
+
),
|
|
200
|
+
CONSTRAINT "turn_connection_authority_turn_fk" FOREIGN KEY (
|
|
201
|
+
"workspace_id", "turn_id"
|
|
202
|
+
) REFERENCES "session_turns"("workspace_id", "id") ON DELETE CASCADE,
|
|
203
|
+
CONSTRAINT "turn_connection_authority_connection_fk" FOREIGN KEY ("connection_id")
|
|
204
|
+
REFERENCES "connections"("id") ON DELETE RESTRICT,
|
|
205
|
+
CONSTRAINT "turn_connection_authority_membership_fk" FOREIGN KEY (
|
|
206
|
+
"owner_organization_membership_id", "account_id"
|
|
207
|
+
) REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT,
|
|
208
|
+
CONSTRAINT "turn_connection_authority_authority_fk" FOREIGN KEY (
|
|
209
|
+
"authority_id", "account_id"
|
|
210
|
+
) REFERENCES "organization_user_resource_authorities"("id", "account_id")
|
|
211
|
+
ON DELETE RESTRICT,
|
|
212
|
+
CONSTRAINT "turn_connection_authority_grant_fk" FOREIGN KEY (
|
|
213
|
+
"grant_id", "account_id"
|
|
214
|
+
) REFERENCES "organization_user_resource_grants"("id", "account_id") ON DELETE RESTRICT
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
CREATE INDEX "turn_connection_authority_connection_idx"
|
|
218
|
+
ON "turn_connection_authority_snapshots" (
|
|
219
|
+
"account_id", "connection_id", "turn_id", "server_id"
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
ALTER TABLE "turn_connection_authority_snapshots" ENABLE ROW LEVEL SECURITY;
|
|
223
|
+
ALTER TABLE "turn_connection_authority_snapshots" FORCE ROW LEVEL SECURITY;
|
|
224
|
+
CREATE POLICY "organization_isolation" ON "turn_connection_authority_snapshots"
|
|
225
|
+
USING (
|
|
226
|
+
"account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
227
|
+
AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
|
|
228
|
+
) WITH CHECK (
|
|
229
|
+
"account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
230
|
+
AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
CREATE TABLE "connection_use_audit_facts" (
|
|
234
|
+
"physical_request_id" uuid PRIMARY KEY,
|
|
235
|
+
"use_phase" text NOT NULL,
|
|
236
|
+
"request_digest" bytea NOT NULL,
|
|
237
|
+
"account_id" uuid NOT NULL,
|
|
238
|
+
"workspace_id" uuid NOT NULL,
|
|
239
|
+
"session_id" uuid NOT NULL,
|
|
240
|
+
"turn_id" uuid NOT NULL,
|
|
241
|
+
"attempt_id" uuid NOT NULL,
|
|
242
|
+
"execution_generation" integer NOT NULL,
|
|
243
|
+
"server_id" text NOT NULL,
|
|
244
|
+
"connection_id" uuid,
|
|
245
|
+
"connection_generation" bigint,
|
|
246
|
+
"authority_scope" text,
|
|
247
|
+
"owner_subject_id" text,
|
|
248
|
+
"authority_id" uuid,
|
|
249
|
+
"grant_id" uuid,
|
|
250
|
+
"outcome" text NOT NULL,
|
|
251
|
+
"denial_reason" text,
|
|
252
|
+
"occurred_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
253
|
+
CONSTRAINT "connection_use_audit_shape_check" CHECK (
|
|
254
|
+
octet_length("server_id") BETWEEN 1 AND 256
|
|
255
|
+
AND octet_length("request_digest") = 32
|
|
256
|
+
AND "execution_generation" > 0
|
|
257
|
+
AND "use_phase" IN ('credential_resolution', 'provider_request')
|
|
258
|
+
AND "outcome" IN ('authorized', 'denied')
|
|
259
|
+
AND (("outcome" = 'authorized' AND "denial_reason" IS NULL
|
|
260
|
+
AND "connection_id" IS NOT NULL AND "connection_generation" > 0
|
|
261
|
+
AND "authority_scope" IN ('workspace', 'user', 'legacy_user'))
|
|
262
|
+
OR ("outcome" = 'denied' AND octet_length("denial_reason") BETWEEN 1 AND 128))
|
|
263
|
+
)
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
ALTER TABLE "connection_use_audit_facts" ENABLE ROW LEVEL SECURITY;
|
|
267
|
+
ALTER TABLE "connection_use_audit_facts" FORCE ROW LEVEL SECURITY;
|
|
268
|
+
CREATE POLICY "organization_isolation" ON "connection_use_audit_facts"
|
|
269
|
+
USING (
|
|
270
|
+
"account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
271
|
+
AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
|
|
272
|
+
) WITH CHECK (
|
|
273
|
+
"account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
274
|
+
AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
CREATE INDEX "connection_use_audit_attempt_idx"
|
|
278
|
+
ON "connection_use_audit_facts" ("workspace_id", "attempt_id", "occurred_at");
|
|
279
|
+
|
|
280
|
+
DO $connection_authority_runtime$
|
|
281
|
+
DECLARE
|
|
282
|
+
data_schema text := current_schema();
|
|
283
|
+
BEGIN
|
|
284
|
+
EXECUTE format($ddl$
|
|
285
|
+
CREATE OR REPLACE FUNCTION %1$I.resolve_personal_connection_authority_selection(
|
|
286
|
+
p_account_id uuid, p_target_workspace_id uuid, p_subject_id text,
|
|
287
|
+
p_connection_id uuid, p_delegation jsonb
|
|
288
|
+
) RETURNS TABLE (origin_workspace_id uuid)
|
|
289
|
+
LANGUAGE plpgsql
|
|
290
|
+
SECURITY DEFINER
|
|
291
|
+
SET search_path = %1$I, pg_catalog, pg_temp
|
|
292
|
+
AS $body$
|
|
293
|
+
DECLARE
|
|
294
|
+
membership_row organization_memberships%%ROWTYPE;
|
|
295
|
+
connection_row connections%%ROWTYPE;
|
|
296
|
+
authority_row organization_user_resource_authorities%%ROWTYPE;
|
|
297
|
+
grant_row organization_user_resource_grants%%ROWTYPE;
|
|
298
|
+
BEGIN
|
|
299
|
+
IF p_account_id IS NULL OR p_target_workspace_id IS NULL
|
|
300
|
+
OR p_connection_id IS NULL OR nullif(btrim(p_subject_id), '') IS NULL
|
|
301
|
+
OR nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
302
|
+
IS DISTINCT FROM p_account_id
|
|
303
|
+
OR nullif(current_setting('opengeni.workspace_id', true), '')::uuid
|
|
304
|
+
IS DISTINCT FROM p_target_workspace_id
|
|
305
|
+
OR nullif(current_setting('opengeni.subject_id', true), '')
|
|
306
|
+
IS DISTINCT FROM p_subject_id
|
|
307
|
+
OR p_delegation ->> 'organizationId' IS DISTINCT FROM p_account_id::text
|
|
308
|
+
OR p_delegation ->> 'workspaceId' IS DISTINCT FROM p_target_workspace_id::text
|
|
309
|
+
OR p_delegation ->> 'action' IS DISTINCT FROM 'connection.use'
|
|
310
|
+
THEN
|
|
311
|
+
RAISE EXCEPTION 'personal connection authority selection context is invalid'
|
|
312
|
+
USING ERRCODE = '42501';
|
|
313
|
+
END IF;
|
|
314
|
+
|
|
315
|
+
PERFORM 1 FROM workspaces workspace_value
|
|
316
|
+
WHERE workspace_value.id = p_target_workspace_id
|
|
317
|
+
AND workspace_value.account_id = p_account_id
|
|
318
|
+
FOR SHARE;
|
|
319
|
+
IF NOT FOUND THEN
|
|
320
|
+
RAISE EXCEPTION 'connection authority target workspace is unavailable'
|
|
321
|
+
USING ERRCODE = '42501';
|
|
322
|
+
END IF;
|
|
323
|
+
|
|
324
|
+
SELECT membership.* INTO STRICT membership_row
|
|
325
|
+
FROM organization_memberships membership
|
|
326
|
+
WHERE membership.account_id = p_account_id
|
|
327
|
+
AND membership.subject_id = p_subject_id
|
|
328
|
+
AND membership.status = 'active'
|
|
329
|
+
AND membership.revoked_at IS NULL
|
|
330
|
+
FOR SHARE;
|
|
331
|
+
IF membership_row.personal_workspace_id IS DISTINCT FROM p_target_workspace_id
|
|
332
|
+
AND NOT EXISTS (
|
|
333
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
334
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
335
|
+
AND workspace_membership.workspace_id = p_target_workspace_id
|
|
336
|
+
AND workspace_membership.subject_id = p_subject_id
|
|
337
|
+
)
|
|
338
|
+
THEN
|
|
339
|
+
RAISE EXCEPTION 'personal connection owner lacks target workspace access'
|
|
340
|
+
USING ERRCODE = '42501';
|
|
341
|
+
END IF;
|
|
342
|
+
|
|
343
|
+
SELECT connection_value.* INTO STRICT connection_row
|
|
344
|
+
FROM connections connection_value
|
|
345
|
+
WHERE connection_value.id = p_connection_id
|
|
346
|
+
AND connection_value.account_id = p_account_id
|
|
347
|
+
AND connection_value.subject_id = p_subject_id
|
|
348
|
+
AND connection_value.owner_organization_membership_id = membership_row.id
|
|
349
|
+
AND connection_value.authority_scope = 'user'
|
|
350
|
+
AND connection_value.status = 'active'
|
|
351
|
+
FOR SHARE;
|
|
352
|
+
|
|
353
|
+
SELECT authority.* INTO STRICT authority_row
|
|
354
|
+
FROM organization_user_resource_authorities authority
|
|
355
|
+
WHERE authority.id = nullif(p_delegation ->> 'authorityId', '')::uuid
|
|
356
|
+
AND authority.account_id = p_account_id
|
|
357
|
+
AND authority.organization_membership_id = membership_row.id
|
|
358
|
+
AND authority.resource_kind = 'connection'
|
|
359
|
+
AND authority.resource_id = connection_row.id
|
|
360
|
+
AND authority.origin_workspace_id = connection_row.origin_workspace_id
|
|
361
|
+
AND authority.generation = (p_delegation ->> 'authorityGeneration')::bigint
|
|
362
|
+
AND authority.status = 'active'
|
|
363
|
+
AND authority.revoked_at IS NULL
|
|
364
|
+
FOR SHARE;
|
|
365
|
+
IF connection_row.authority_id IS DISTINCT FROM authority_row.id THEN
|
|
366
|
+
RAISE EXCEPTION 'personal connection authority selection is unavailable'
|
|
367
|
+
USING ERRCODE = '42501';
|
|
368
|
+
END IF;
|
|
369
|
+
|
|
370
|
+
SELECT grant_value.* INTO STRICT grant_row
|
|
371
|
+
FROM organization_user_resource_grants grant_value
|
|
372
|
+
WHERE grant_value.id = nullif(p_delegation ->> 'grantId', '')::uuid
|
|
373
|
+
AND grant_value.account_id = p_account_id
|
|
374
|
+
AND grant_value.authority_id = authority_row.id
|
|
375
|
+
AND grant_value.owner_organization_membership_id = membership_row.id
|
|
376
|
+
AND grant_value.workspace_id = p_target_workspace_id
|
|
377
|
+
AND grant_value.action = 'connection.use'
|
|
378
|
+
AND grant_value.mode = p_delegation ->> 'mode'
|
|
379
|
+
AND grant_value.context = p_delegation ->> 'context'
|
|
380
|
+
AND grant_value.generation = (p_delegation ->> 'grantGeneration')::bigint
|
|
381
|
+
AND (
|
|
382
|
+
(grant_value.status = 'active'
|
|
383
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp()))
|
|
384
|
+
OR (grant_value.status = 'consumed' AND grant_value.mode = 'once' AND EXISTS (
|
|
385
|
+
SELECT 1 FROM connection_use_once_consumption_receipts receipt
|
|
386
|
+
WHERE receipt.grant_id = grant_value.id
|
|
387
|
+
AND receipt.account_id = grant_value.account_id
|
|
388
|
+
AND receipt.authority_id = grant_value.authority_id
|
|
389
|
+
AND receipt.authority_generation = authority_row.generation
|
|
390
|
+
AND receipt.grant_generation = grant_value.generation
|
|
391
|
+
AND receipt.accepted_work_kind = 'turn'
|
|
392
|
+
))
|
|
393
|
+
)
|
|
394
|
+
AND nullif(p_delegation ->> 'sessionId', '')::uuid
|
|
395
|
+
IS NOT DISTINCT FROM grant_value.session_id
|
|
396
|
+
AND nullif(p_delegation ->> 'authorityEpoch', '')::integer
|
|
397
|
+
IS NOT DISTINCT FROM grant_value.authority_epoch
|
|
398
|
+
FOR SHARE;
|
|
399
|
+
|
|
400
|
+
origin_workspace_id := connection_row.origin_workspace_id;
|
|
401
|
+
RETURN NEXT;
|
|
402
|
+
END
|
|
403
|
+
$body$;
|
|
404
|
+
|
|
405
|
+
CREATE OR REPLACE FUNCTION opengeni_private.capture_accepted_turn_connection_authorities()
|
|
406
|
+
RETURNS trigger
|
|
407
|
+
LANGUAGE plpgsql
|
|
408
|
+
SECURITY DEFINER
|
|
409
|
+
SET search_path = %1$I, pg_catalog, public, pg_temp
|
|
410
|
+
AS $body$
|
|
411
|
+
DECLARE
|
|
412
|
+
session_row sessions%%ROWTYPE;
|
|
413
|
+
item jsonb;
|
|
414
|
+
delegation jsonb;
|
|
415
|
+
connection_row connections%%ROWTYPE;
|
|
416
|
+
membership_row organization_memberships%%ROWTYPE;
|
|
417
|
+
authority_row organization_user_resource_authorities%%ROWTYPE;
|
|
418
|
+
grant_row organization_user_resource_grants%%ROWTYPE;
|
|
419
|
+
canonical jsonb;
|
|
420
|
+
existing_digest bytea;
|
|
421
|
+
existing_snapshot jsonb;
|
|
422
|
+
initiating_subject text;
|
|
423
|
+
verified_causal_subject text := coalesce(
|
|
424
|
+
nullif(current_setting('opengeni.initiating_human_subject_id', true), ''),
|
|
425
|
+
nullif(current_setting('opengeni.subject_id', true), '')
|
|
426
|
+
);
|
|
427
|
+
BEGIN
|
|
428
|
+
IF TG_OP = 'UPDATE' THEN
|
|
429
|
+
IF NEW.personal_connection_delegations IS NOT DISTINCT FROM OLD.personal_connection_delegations
|
|
430
|
+
THEN RETURN NEW;
|
|
431
|
+
END IF;
|
|
432
|
+
IF NEW.status <> 'queued' OR EXISTS (
|
|
433
|
+
SELECT 1 FROM session_turn_attempts attempt
|
|
434
|
+
WHERE attempt.turn_id = NEW.id AND attempt.workspace_id = NEW.workspace_id
|
|
435
|
+
) THEN
|
|
436
|
+
RAISE EXCEPTION 'accepted connection authority cannot change after claim'
|
|
437
|
+
USING ERRCODE = '42501';
|
|
438
|
+
END IF;
|
|
439
|
+
DELETE FROM turn_connection_authority_snapshots snapshot
|
|
440
|
+
WHERE snapshot.turn_id = NEW.id AND snapshot.workspace_id = NEW.workspace_id;
|
|
441
|
+
END IF;
|
|
442
|
+
SELECT session_value.* INTO STRICT session_row
|
|
443
|
+
FROM sessions session_value
|
|
444
|
+
WHERE session_value.id = NEW.session_id
|
|
445
|
+
AND session_value.account_id = NEW.account_id
|
|
446
|
+
AND session_value.workspace_id = NEW.workspace_id
|
|
447
|
+
FOR SHARE;
|
|
448
|
+
initiating_subject := coalesce(
|
|
449
|
+
nullif(btrim(NEW.initiating_human_subject_id), ''),
|
|
450
|
+
CASE WHEN NEW.initiator_kind = 'subject'
|
|
451
|
+
THEN nullif(btrim(NEW.initiator_subject_id), '') END
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
FOR item IN
|
|
455
|
+
SELECT value FROM jsonb_array_elements(NEW.personal_connection_delegations)
|
|
456
|
+
LOOP
|
|
457
|
+
-- Social uses a distinct store. It is not activated here and must never
|
|
458
|
+
-- smuggle the common connection-authority envelope past this boundary.
|
|
459
|
+
IF item ->> 'connectionType' = 'social' THEN
|
|
460
|
+
IF item -> 'userDelegation' IS NOT NULL THEN
|
|
461
|
+
RAISE EXCEPTION 'social connection authority is not activated'
|
|
462
|
+
USING ERRCODE = '42501';
|
|
463
|
+
END IF;
|
|
464
|
+
CONTINUE;
|
|
465
|
+
END IF;
|
|
466
|
+
IF nullif(item ->> 'serverId', '') IS NULL
|
|
467
|
+
OR octet_length(item ->> 'serverId') > 256
|
|
468
|
+
OR nullif(item ->> 'connectionId', '') IS NULL
|
|
469
|
+
THEN
|
|
470
|
+
RAISE EXCEPTION 'invalid accepted connection selection' USING ERRCODE = '22023';
|
|
471
|
+
END IF;
|
|
472
|
+
SELECT connection_value.* INTO connection_row
|
|
473
|
+
FROM connections connection_value
|
|
474
|
+
WHERE connection_value.id = (item ->> 'connectionId')::uuid
|
|
475
|
+
AND connection_value.account_id = NEW.account_id
|
|
476
|
+
FOR SHARE;
|
|
477
|
+
|
|
478
|
+
-- First-party Atlassian remains a named successor. Legacy rows retain
|
|
479
|
+
-- their bounded compatibility behavior, but a common `user` row cannot be
|
|
480
|
+
-- relabeled to bypass snapshot, once, and causal-human admission.
|
|
481
|
+
IF item ->> 'connectionType' = 'atlassian' THEN
|
|
482
|
+
IF item -> 'userDelegation' IS NOT NULL
|
|
483
|
+
OR (FOUND AND connection_row.authority_scope = 'user')
|
|
484
|
+
THEN
|
|
485
|
+
RAISE EXCEPTION 'Atlassian connection authority is not activated'
|
|
486
|
+
USING ERRCODE = '42501';
|
|
487
|
+
END IF;
|
|
488
|
+
CONTINUE;
|
|
489
|
+
END IF;
|
|
490
|
+
|
|
491
|
+
IF NOT FOUND THEN
|
|
492
|
+
IF item -> 'userDelegation' IS NULL THEN
|
|
493
|
+
-- Bounded legacy rows are revalidated by the existing credential
|
|
494
|
+
-- resolver. A missing historical row remains unavailable there; it
|
|
495
|
+
-- must not make unrelated turn acceptance fail during the cutover.
|
|
496
|
+
CONTINUE;
|
|
497
|
+
END IF;
|
|
498
|
+
RAISE EXCEPTION 'accepted connection selection is unavailable'
|
|
499
|
+
USING ERRCODE = '42501';
|
|
500
|
+
END IF;
|
|
501
|
+
|
|
502
|
+
IF connection_row.authority_scope = 'legacy_user' AND item -> 'userDelegation' IS NULL THEN
|
|
503
|
+
CONTINUE;
|
|
504
|
+
END IF;
|
|
505
|
+
IF connection_row.authority_scope <> 'user' THEN
|
|
506
|
+
RAISE EXCEPTION 'accepted personal connection scope is unavailable'
|
|
507
|
+
USING ERRCODE = '42501';
|
|
508
|
+
END IF;
|
|
509
|
+
IF initiating_subject IS NULL OR connection_row.subject_id IS DISTINCT FROM initiating_subject
|
|
510
|
+
OR connection_row.status <> 'active'
|
|
511
|
+
OR lower(connection_row.provider_domain) IS DISTINCT FROM lower(item ->> 'providerDomain')
|
|
512
|
+
OR (item ? 'kind' AND connection_row.kind IS DISTINCT FROM item ->> 'kind')
|
|
513
|
+
THEN
|
|
514
|
+
RAISE EXCEPTION 'accepted personal connection identity is unavailable'
|
|
515
|
+
USING ERRCODE = '42501';
|
|
516
|
+
END IF;
|
|
517
|
+
delegation := item -> 'userDelegation';
|
|
518
|
+
IF delegation IS NULL THEN
|
|
519
|
+
RAISE EXCEPTION 'activated personal connection requires an explicit grant'
|
|
520
|
+
USING ERRCODE = '42501';
|
|
521
|
+
END IF;
|
|
522
|
+
IF verified_causal_subject IS DISTINCT FROM initiating_subject THEN
|
|
523
|
+
RAISE EXCEPTION 'accepted connection authority causal human is unverified'
|
|
524
|
+
USING ERRCODE = '42501';
|
|
525
|
+
END IF;
|
|
526
|
+
|
|
527
|
+
SELECT membership.* INTO STRICT membership_row
|
|
528
|
+
FROM organization_memberships membership
|
|
529
|
+
WHERE membership.id = connection_row.owner_organization_membership_id
|
|
530
|
+
AND membership.account_id = NEW.account_id
|
|
531
|
+
AND membership.subject_id = initiating_subject
|
|
532
|
+
AND membership.status = 'active'
|
|
533
|
+
AND membership.revoked_at IS NULL
|
|
534
|
+
FOR SHARE;
|
|
535
|
+
IF membership_row.personal_workspace_id IS DISTINCT FROM NEW.workspace_id
|
|
536
|
+
AND NOT EXISTS (
|
|
537
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
538
|
+
WHERE workspace_membership.account_id = NEW.account_id
|
|
539
|
+
AND workspace_membership.workspace_id = NEW.workspace_id
|
|
540
|
+
AND workspace_membership.subject_id = initiating_subject
|
|
541
|
+
)
|
|
542
|
+
THEN
|
|
543
|
+
RAISE EXCEPTION 'personal connection owner lacks target workspace access'
|
|
544
|
+
USING ERRCODE = '42501';
|
|
545
|
+
END IF;
|
|
546
|
+
|
|
547
|
+
SELECT authority.* INTO STRICT authority_row
|
|
548
|
+
FROM organization_user_resource_authorities authority
|
|
549
|
+
WHERE authority.id = nullif(delegation ->> 'authorityId', '')::uuid
|
|
550
|
+
AND authority.account_id = NEW.account_id
|
|
551
|
+
AND authority.organization_membership_id = membership_row.id
|
|
552
|
+
AND authority.resource_kind = 'connection'
|
|
553
|
+
AND authority.resource_id = connection_row.id
|
|
554
|
+
AND authority.origin_workspace_id = connection_row.origin_workspace_id
|
|
555
|
+
AND authority.generation = (delegation ->> 'authorityGeneration')::bigint
|
|
556
|
+
AND authority.status = 'active'
|
|
557
|
+
AND authority.revoked_at IS NULL
|
|
558
|
+
FOR SHARE;
|
|
559
|
+
|
|
560
|
+
SELECT grant_value.* INTO STRICT grant_row
|
|
561
|
+
FROM organization_user_resource_grants grant_value
|
|
562
|
+
WHERE grant_value.id = nullif(delegation ->> 'grantId', '')::uuid
|
|
563
|
+
AND grant_value.account_id = NEW.account_id
|
|
564
|
+
AND grant_value.authority_id = authority_row.id
|
|
565
|
+
AND grant_value.owner_organization_membership_id = membership_row.id
|
|
566
|
+
AND grant_value.workspace_id = NEW.workspace_id
|
|
567
|
+
AND grant_value.action = 'connection.use'
|
|
568
|
+
AND grant_value.mode = delegation ->> 'mode'
|
|
569
|
+
AND grant_value.context = session_row.visibility
|
|
570
|
+
AND grant_value.generation = (delegation ->> 'grantGeneration')::bigint
|
|
571
|
+
AND grant_value.status IN ('active', 'consumed')
|
|
572
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
573
|
+
AND ((grant_value.mode = 'always' AND grant_value.session_id IS NULL
|
|
574
|
+
AND grant_value.authority_epoch IS NULL)
|
|
575
|
+
OR (grant_value.mode IN ('once', 'session')
|
|
576
|
+
AND grant_value.session_id = NEW.session_id
|
|
577
|
+
AND grant_value.authority_epoch = session_row.authority_epoch))
|
|
578
|
+
FOR UPDATE;
|
|
579
|
+
IF grant_row.mode = 'once' THEN
|
|
580
|
+
IF grant_row.status = 'active' THEN
|
|
581
|
+
UPDATE organization_user_resource_grants grant_value
|
|
582
|
+
SET status = 'consumed', updated_at = clock_timestamp()
|
|
583
|
+
WHERE grant_value.id = grant_row.id
|
|
584
|
+
AND grant_value.account_id = NEW.account_id
|
|
585
|
+
AND grant_value.generation = grant_row.generation
|
|
586
|
+
AND grant_value.status = 'active';
|
|
587
|
+
IF NOT FOUND THEN
|
|
588
|
+
RAISE EXCEPTION 'connection use grant is no longer admissible'
|
|
589
|
+
USING ERRCODE = '42501';
|
|
590
|
+
END IF;
|
|
591
|
+
INSERT INTO connection_use_once_consumption_receipts (
|
|
592
|
+
grant_id, account_id, authority_id, authority_generation,
|
|
593
|
+
grant_generation, accepted_work_kind, accepted_work_id
|
|
594
|
+
) VALUES (
|
|
595
|
+
grant_row.id, NEW.account_id, authority_row.id,
|
|
596
|
+
authority_row.generation, grant_row.generation, 'turn', NEW.id
|
|
597
|
+
) ON CONFLICT (grant_id) DO NOTHING;
|
|
598
|
+
IF NOT FOUND THEN
|
|
599
|
+
RAISE EXCEPTION 'connection use grant is already bound to other work'
|
|
600
|
+
USING ERRCODE = '42501';
|
|
601
|
+
END IF;
|
|
602
|
+
grant_row.status := 'consumed';
|
|
603
|
+
ELSIF grant_row.status <> 'consumed' OR NOT EXISTS (
|
|
604
|
+
SELECT 1 FROM connection_use_once_consumption_receipts receipt
|
|
605
|
+
WHERE receipt.grant_id = grant_row.id
|
|
606
|
+
AND receipt.accepted_work_kind = 'turn'
|
|
607
|
+
AND receipt.accepted_work_id = NEW.id
|
|
608
|
+
) THEN
|
|
609
|
+
RAISE EXCEPTION 'connection use grant is no longer admissible'
|
|
610
|
+
USING ERRCODE = '42501';
|
|
611
|
+
END IF;
|
|
612
|
+
ELSIF grant_row.status <> 'active' THEN
|
|
613
|
+
RAISE EXCEPTION 'connection use grant is no longer admissible' USING ERRCODE = '42501';
|
|
614
|
+
END IF;
|
|
615
|
+
IF delegation ->> 'organizationId' IS DISTINCT FROM NEW.account_id::text
|
|
616
|
+
OR delegation ->> 'workspaceId' IS DISTINCT FROM NEW.workspace_id::text
|
|
617
|
+
OR delegation ->> 'action' IS DISTINCT FROM 'connection.use'
|
|
618
|
+
OR delegation ->> 'context' IS DISTINCT FROM session_row.visibility
|
|
619
|
+
OR nullif(delegation ->> 'sessionId', '')::uuid IS DISTINCT FROM grant_row.session_id
|
|
620
|
+
OR nullif(delegation ->> 'authorityEpoch', '')::integer
|
|
621
|
+
IS DISTINCT FROM grant_row.authority_epoch
|
|
622
|
+
THEN
|
|
623
|
+
RAISE EXCEPTION 'accepted connection grant metadata is contradictory'
|
|
624
|
+
USING ERRCODE = '42501';
|
|
625
|
+
END IF;
|
|
626
|
+
IF nullif(item ->> 'originWorkspaceId', '')::uuid
|
|
627
|
+
IS DISTINCT FROM connection_row.origin_workspace_id
|
|
628
|
+
THEN
|
|
629
|
+
RAISE EXCEPTION 'accepted connection origin is not server-resolved'
|
|
630
|
+
USING ERRCODE = '42501';
|
|
631
|
+
END IF;
|
|
632
|
+
|
|
633
|
+
canonical := jsonb_build_object(
|
|
634
|
+
'organizationId', NEW.account_id,
|
|
635
|
+
'originWorkspaceId', connection_row.origin_workspace_id,
|
|
636
|
+
'targetWorkspaceId', NEW.workspace_id,
|
|
637
|
+
'targetSessionId', NEW.session_id,
|
|
638
|
+
'targetSessionVisibility', session_row.visibility,
|
|
639
|
+
'targetSessionAuthorityEpoch', session_row.authority_epoch,
|
|
640
|
+
'acceptedWork', jsonb_build_object('kind', 'turn', 'turnId', NEW.id),
|
|
641
|
+
'connectionId', connection_row.id,
|
|
642
|
+
'connectionGeneration', connection_row.authority_generation,
|
|
643
|
+
'connectionStatus', 'active',
|
|
644
|
+
'providerDomain', lower(connection_row.provider_domain),
|
|
645
|
+
'connectionKind', connection_row.kind,
|
|
646
|
+
'scope', 'user',
|
|
647
|
+
'ownerSubjectId', initiating_subject,
|
|
648
|
+
'ownerOrganizationMembershipId', membership_row.id,
|
|
649
|
+
'ownerMembershipAuthorizationRevision', membership_row.authorization_revision,
|
|
650
|
+
'authoritySource', 'user_delegation',
|
|
651
|
+
'selectionSources', jsonb_build_array('mcp:' || (item ->> 'serverId')),
|
|
652
|
+
'userDelegation', jsonb_build_object(
|
|
653
|
+
'organizationId', NEW.account_id,
|
|
654
|
+
'authorityId', authority_row.id,
|
|
655
|
+
'authorityGeneration', authority_row.generation,
|
|
656
|
+
'workspaceId', NEW.workspace_id,
|
|
657
|
+
'sessionId', grant_row.session_id,
|
|
658
|
+
'action', 'connection.use',
|
|
659
|
+
'mode', grant_row.mode,
|
|
660
|
+
'context', grant_row.context,
|
|
661
|
+
'authorityEpoch', grant_row.authority_epoch,
|
|
662
|
+
'grantId', grant_row.id,
|
|
663
|
+
'grantGeneration', grant_row.generation
|
|
664
|
+
)
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
SELECT snapshot.snapshot_digest, snapshot.canonical_snapshot
|
|
668
|
+
INTO existing_digest, existing_snapshot
|
|
669
|
+
FROM turn_connection_authority_snapshots snapshot
|
|
670
|
+
WHERE snapshot.turn_id = NEW.id AND snapshot.server_id = item ->> 'serverId';
|
|
671
|
+
IF FOUND THEN
|
|
672
|
+
IF existing_snapshot IS DISTINCT FROM canonical
|
|
673
|
+
OR existing_digest IS DISTINCT FROM digest(convert_to(canonical::text, 'UTF8'), 'sha256')
|
|
674
|
+
THEN
|
|
675
|
+
RAISE EXCEPTION 'accepted connection authority changed across recovery'
|
|
676
|
+
USING ERRCODE = '42501';
|
|
677
|
+
END IF;
|
|
678
|
+
CONTINUE;
|
|
679
|
+
END IF;
|
|
680
|
+
|
|
681
|
+
INSERT INTO turn_connection_authority_snapshots (
|
|
682
|
+
account_id, workspace_id, session_id, turn_id, server_id,
|
|
683
|
+
connection_id, connection_generation, origin_workspace_id,
|
|
684
|
+
provider_domain, connection_kind, authority_scope, authority_source, owner_subject_id,
|
|
685
|
+
owner_organization_membership_id, membership_authorization_revision,
|
|
686
|
+
authority_id, authority_generation, grant_id, grant_generation,
|
|
687
|
+
grant_mode, grant_context, grant_session_id, grant_authority_epoch,
|
|
688
|
+
session_visibility, session_authority_epoch, canonical_snapshot, snapshot_digest
|
|
689
|
+
) VALUES (
|
|
690
|
+
NEW.account_id, NEW.workspace_id, NEW.session_id, NEW.id, item ->> 'serverId',
|
|
691
|
+
connection_row.id, connection_row.authority_generation,
|
|
692
|
+
connection_row.origin_workspace_id, lower(connection_row.provider_domain),
|
|
693
|
+
connection_row.kind, 'user', 'user_delegation', initiating_subject, membership_row.id,
|
|
694
|
+
membership_row.authorization_revision, authority_row.id, authority_row.generation,
|
|
695
|
+
grant_row.id, grant_row.generation, grant_row.mode, grant_row.context,
|
|
696
|
+
grant_row.session_id, grant_row.authority_epoch,
|
|
697
|
+
session_row.visibility, session_row.authority_epoch, canonical,
|
|
698
|
+
digest(convert_to(canonical::text, 'UTF8'), 'sha256')
|
|
699
|
+
);
|
|
700
|
+
END LOOP;
|
|
701
|
+
RETURN NEW;
|
|
702
|
+
END
|
|
703
|
+
$body$
|
|
704
|
+
$ddl$, data_schema);
|
|
705
|
+
|
|
706
|
+
EXECUTE format($ddl$
|
|
707
|
+
CREATE TRIGGER accepted_turn_connection_authority_capture
|
|
708
|
+
AFTER INSERT OR UPDATE OF personal_connection_delegations ON %1$I.session_turns
|
|
709
|
+
FOR EACH ROW EXECUTE FUNCTION opengeni_private.capture_accepted_turn_connection_authorities()
|
|
710
|
+
$ddl$, data_schema);
|
|
711
|
+
|
|
712
|
+
-- Keep the 0256 ABI only for the bounded workspace/legacy compatibility lane.
|
|
713
|
+
-- A drained old worker that tries to execute an activated common-user snapshot
|
|
714
|
+
-- is denied even though the function signature still exists.
|
|
715
|
+
EXECUTE format(
|
|
716
|
+
'ALTER FUNCTION %I.resolve_connection_use_authority(uuid, uuid, uuid, jsonb) '
|
|
717
|
+
|| 'RENAME TO resolve_connection_use_authority_legacy_0256',
|
|
718
|
+
data_schema
|
|
719
|
+
);
|
|
720
|
+
EXECUTE format($ddl$
|
|
721
|
+
CREATE FUNCTION %1$I.resolve_connection_use_authority(
|
|
722
|
+
p_account_id uuid, p_workspace_id uuid, p_session_id uuid, p_snapshot jsonb
|
|
723
|
+
) RETURNS TABLE (
|
|
724
|
+
authorization_status text, denial_reason text, connection_id uuid,
|
|
725
|
+
connection_generation bigint, authority_scope text, owner_subject_id text,
|
|
726
|
+
authority_id uuid, grant_id uuid
|
|
727
|
+
)
|
|
728
|
+
LANGUAGE plpgsql
|
|
729
|
+
SECURITY DEFINER
|
|
730
|
+
SET search_path = %1$I, pg_catalog, pg_temp
|
|
731
|
+
AS $body$
|
|
732
|
+
BEGIN
|
|
733
|
+
IF p_snapshot ->> 'scope' = 'user' THEN
|
|
734
|
+
authorization_status := 'denied';
|
|
735
|
+
denial_reason := 'accepted_attempt_authority_required';
|
|
736
|
+
RETURN NEXT; RETURN;
|
|
737
|
+
END IF;
|
|
738
|
+
RETURN QUERY SELECT * FROM resolve_connection_use_authority_legacy_0256(
|
|
739
|
+
p_account_id, p_workspace_id, p_session_id, p_snapshot
|
|
740
|
+
);
|
|
741
|
+
END
|
|
742
|
+
$body$
|
|
743
|
+
$ddl$, data_schema);
|
|
744
|
+
|
|
745
|
+
EXECUTE format($ddl$
|
|
746
|
+
CREATE OR REPLACE FUNCTION %1$I.resolve_accepted_connection_use(
|
|
747
|
+
p_account_id uuid,
|
|
748
|
+
p_workspace_id uuid,
|
|
749
|
+
p_session_id uuid,
|
|
750
|
+
p_turn_id uuid,
|
|
751
|
+
p_attempt_id uuid,
|
|
752
|
+
p_execution_generation integer,
|
|
753
|
+
p_physical_request_id uuid,
|
|
754
|
+
p_use_phase text,
|
|
755
|
+
p_server_id text,
|
|
756
|
+
p_connection_id uuid,
|
|
757
|
+
p_provider_domain text,
|
|
758
|
+
p_connection_kind text,
|
|
759
|
+
p_subject_scope text,
|
|
760
|
+
p_owner_subject_id text DEFAULT NULL
|
|
761
|
+
) RETURNS TABLE (
|
|
762
|
+
authorization_status text,
|
|
763
|
+
denial_reason text,
|
|
764
|
+
resolved_connection_id uuid,
|
|
765
|
+
connection_generation bigint,
|
|
766
|
+
origin_workspace_id uuid,
|
|
767
|
+
resolved_connection_kind text,
|
|
768
|
+
authority_scope text,
|
|
769
|
+
owner_subject_id text,
|
|
770
|
+
authority_id uuid,
|
|
771
|
+
grant_id uuid
|
|
772
|
+
)
|
|
773
|
+
LANGUAGE plpgsql
|
|
774
|
+
SECURITY DEFINER
|
|
775
|
+
SET search_path = %1$I, pg_catalog, public, pg_temp
|
|
776
|
+
AS $body$
|
|
777
|
+
#variable_conflict use_column
|
|
778
|
+
DECLARE
|
|
779
|
+
prior connection_use_audit_facts%%ROWTYPE;
|
|
780
|
+
connection_row connections%%ROWTYPE;
|
|
781
|
+
snapshot turn_connection_authority_snapshots%%ROWTYPE;
|
|
782
|
+
session_row sessions%%ROWTYPE;
|
|
783
|
+
grant_row organization_user_resource_grants%%ROWTYPE;
|
|
784
|
+
request_digest bytea;
|
|
785
|
+
has_prior boolean := false;
|
|
786
|
+
reason text;
|
|
787
|
+
BEGIN
|
|
788
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
789
|
+
OR p_workspace_id IS DISTINCT FROM nullif(current_setting('opengeni.workspace_id', true), '')::uuid
|
|
790
|
+
OR p_execution_generation <= 0
|
|
791
|
+
OR p_use_phase NOT IN ('credential_resolution', 'provider_request')
|
|
792
|
+
OR nullif(btrim(p_server_id), '') IS NULL
|
|
793
|
+
OR octet_length(p_server_id) > 256
|
|
794
|
+
THEN
|
|
795
|
+
RAISE EXCEPTION 'connection use scope mismatch' USING ERRCODE = '42501';
|
|
796
|
+
END IF;
|
|
797
|
+
request_digest := digest(convert_to(jsonb_build_object(
|
|
798
|
+
'accountId', p_account_id, 'workspaceId', p_workspace_id,
|
|
799
|
+
'sessionId', p_session_id, 'turnId', p_turn_id, 'attemptId', p_attempt_id,
|
|
800
|
+
'executionGeneration', p_execution_generation, 'usePhase', p_use_phase,
|
|
801
|
+
'serverId', p_server_id,
|
|
802
|
+
'connectionId', p_connection_id, 'providerDomain', lower(p_provider_domain),
|
|
803
|
+
'connectionKind', p_connection_kind, 'subjectScope', p_subject_scope,
|
|
804
|
+
'ownerSubjectId', p_owner_subject_id
|
|
805
|
+
)::text, 'UTF8'), 'sha256');
|
|
806
|
+
|
|
807
|
+
-- Match the canonical lifecycle lock prefix before inspecting mutable
|
|
808
|
+
-- attempt authority: control -> workspace -> session -> turn -> attempt.
|
|
809
|
+
PERFORM 1 FROM workspace_inference_controls control_row
|
|
810
|
+
WHERE control_row.account_id = p_account_id
|
|
811
|
+
AND control_row.workspace_id = p_workspace_id
|
|
812
|
+
FOR SHARE;
|
|
813
|
+
IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
|
|
814
|
+
IF reason IS NULL THEN
|
|
815
|
+
PERFORM 1 FROM workspaces workspace_value
|
|
816
|
+
WHERE workspace_value.account_id = p_account_id AND workspace_value.id = p_workspace_id
|
|
817
|
+
FOR KEY SHARE;
|
|
818
|
+
IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
|
|
819
|
+
END IF;
|
|
820
|
+
IF reason IS NULL THEN
|
|
821
|
+
SELECT session_value.* INTO session_row FROM sessions session_value
|
|
822
|
+
WHERE session_value.id = p_session_id
|
|
823
|
+
AND session_value.account_id = p_account_id
|
|
824
|
+
AND session_value.workspace_id = p_workspace_id
|
|
825
|
+
FOR NO KEY UPDATE;
|
|
826
|
+
IF NOT FOUND OR session_row.active_turn_id IS DISTINCT FROM p_turn_id
|
|
827
|
+
OR session_row.status = 'cancelled'
|
|
828
|
+
THEN reason := 'session_identity_changed'; END IF;
|
|
829
|
+
END IF;
|
|
830
|
+
IF reason IS NULL THEN
|
|
831
|
+
PERFORM 1 FROM session_turns turn_value
|
|
832
|
+
WHERE turn_value.id = p_turn_id
|
|
833
|
+
AND turn_value.account_id = p_account_id
|
|
834
|
+
AND turn_value.workspace_id = p_workspace_id
|
|
835
|
+
AND turn_value.session_id = p_session_id
|
|
836
|
+
AND turn_value.active_attempt_id = p_attempt_id
|
|
837
|
+
AND turn_value.execution_generation = p_execution_generation
|
|
838
|
+
AND turn_value.status = 'running'
|
|
839
|
+
FOR UPDATE;
|
|
840
|
+
IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
|
|
841
|
+
END IF;
|
|
842
|
+
IF reason IS NULL THEN
|
|
843
|
+
PERFORM 1 FROM session_turn_attempts attempt
|
|
844
|
+
WHERE attempt.id = p_attempt_id
|
|
845
|
+
AND attempt.account_id = p_account_id
|
|
846
|
+
AND attempt.workspace_id = p_workspace_id
|
|
847
|
+
AND attempt.session_id = p_session_id
|
|
848
|
+
AND attempt.turn_id = p_turn_id
|
|
849
|
+
AND attempt.execution_generation = p_execution_generation
|
|
850
|
+
AND attempt.state IN ('claimed', 'running')
|
|
851
|
+
AND attempt.closed_at IS NULL AND attempt.quiesced_at IS NULL
|
|
852
|
+
AND attempt.authority_visibility = session_row.visibility
|
|
853
|
+
AND attempt.authority_epoch = session_row.authority_epoch
|
|
854
|
+
AND attempt.authority_owner_organization_membership_id
|
|
855
|
+
IS NOT DISTINCT FROM session_row.owner_organization_membership_id
|
|
856
|
+
FOR UPDATE;
|
|
857
|
+
IF NOT FOUND OR EXISTS (
|
|
858
|
+
SELECT 1 FROM session_attempt_interruptions interruption
|
|
859
|
+
WHERE interruption.account_id = p_account_id
|
|
860
|
+
AND interruption.workspace_id = p_workspace_id
|
|
861
|
+
AND interruption.session_id = p_session_id
|
|
862
|
+
AND interruption.attempt_id = p_attempt_id
|
|
863
|
+
AND interruption.state IN ('pending', 'delivered', 'acknowledged')
|
|
864
|
+
) THEN reason := 'session_identity_changed'; END IF;
|
|
865
|
+
END IF;
|
|
866
|
+
|
|
867
|
+
-- Serialize and verify one caller-preallocated physical request identity
|
|
868
|
+
-- after the canonical lifecycle locks even when those live fences now deny.
|
|
869
|
+
-- A stale attempt cannot use that denial to hide conflicting UUID reuse.
|
|
870
|
+
PERFORM pg_advisory_xact_lock(hashtextextended(p_physical_request_id::text, 0));
|
|
871
|
+
SELECT EXISTS (
|
|
872
|
+
SELECT 1 FROM connection_use_audit_facts audit
|
|
873
|
+
WHERE audit.physical_request_id = p_physical_request_id
|
|
874
|
+
) INTO has_prior;
|
|
875
|
+
IF has_prior THEN
|
|
876
|
+
SELECT audit.* INTO STRICT prior FROM connection_use_audit_facts audit
|
|
877
|
+
WHERE audit.physical_request_id = p_physical_request_id;
|
|
878
|
+
END IF;
|
|
879
|
+
IF has_prior AND prior.request_digest IS DISTINCT FROM request_digest THEN
|
|
880
|
+
RAISE EXCEPTION 'physical connection request id was reused for different work'
|
|
881
|
+
USING ERRCODE = '23505';
|
|
882
|
+
END IF;
|
|
883
|
+
IF reason IS NULL AND has_prior AND prior.outcome = 'denied' THEN
|
|
884
|
+
authorization_status := prior.outcome;
|
|
885
|
+
denial_reason := prior.denial_reason;
|
|
886
|
+
RETURN NEXT; RETURN;
|
|
887
|
+
END IF;
|
|
888
|
+
|
|
889
|
+
IF reason IS NULL THEN
|
|
890
|
+
SELECT authority_snapshot.* INTO snapshot
|
|
891
|
+
FROM turn_connection_authority_snapshots authority_snapshot
|
|
892
|
+
WHERE authority_snapshot.turn_id = p_turn_id
|
|
893
|
+
AND authority_snapshot.server_id = p_server_id
|
|
894
|
+
AND authority_snapshot.account_id = p_account_id
|
|
895
|
+
AND authority_snapshot.workspace_id = p_workspace_id
|
|
896
|
+
AND authority_snapshot.session_id = p_session_id;
|
|
897
|
+
|
|
898
|
+
IF FOUND THEN
|
|
899
|
+
SELECT connection_value.* INTO connection_row FROM connections connection_value
|
|
900
|
+
WHERE connection_value.id = snapshot.connection_id
|
|
901
|
+
AND connection_value.account_id = p_account_id;
|
|
902
|
+
IF snapshot.snapshot_digest IS DISTINCT FROM digest(
|
|
903
|
+
convert_to(snapshot.canonical_snapshot::text, 'UTF8'), 'sha256'
|
|
904
|
+
) THEN reason := 'accepted_snapshot_digest_changed';
|
|
905
|
+
ELSIF NOT FOUND THEN reason := 'connection_missing';
|
|
906
|
+
ELSIF snapshot.authority_scope IS DISTINCT FROM 'user'
|
|
907
|
+
OR p_subject_scope IS DISTINCT FROM 'subject'
|
|
908
|
+
OR p_connection_id IS DISTINCT FROM snapshot.connection_id
|
|
909
|
+
OR lower(p_provider_domain) IS DISTINCT FROM snapshot.provider_domain
|
|
910
|
+
OR (p_connection_kind IS NOT NULL AND p_connection_kind IS DISTINCT FROM snapshot.connection_kind)
|
|
911
|
+
THEN reason := 'connection_identity_changed';
|
|
912
|
+
ELSIF connection_row.status <> 'active' THEN reason := 'connection_status_inactive';
|
|
913
|
+
ELSIF connection_row.authority_scope IS DISTINCT FROM 'user'
|
|
914
|
+
OR connection_row.subject_id IS DISTINCT FROM snapshot.owner_subject_id
|
|
915
|
+
OR connection_row.owner_organization_membership_id
|
|
916
|
+
IS DISTINCT FROM snapshot.owner_organization_membership_id
|
|
917
|
+
OR connection_row.origin_workspace_id IS DISTINCT FROM snapshot.origin_workspace_id
|
|
918
|
+
THEN reason := 'connection_owner_changed';
|
|
919
|
+
ELSIF connection_row.authority_generation IS DISTINCT FROM snapshot.connection_generation
|
|
920
|
+
THEN reason := 'connection_generation_changed';
|
|
921
|
+
ELSIF session_row.visibility IS DISTINCT FROM snapshot.session_visibility
|
|
922
|
+
OR session_row.authority_epoch IS DISTINCT FROM snapshot.session_authority_epoch
|
|
923
|
+
THEN reason := 'session_authority_epoch_changed';
|
|
924
|
+
ELSIF NOT EXISTS (
|
|
925
|
+
SELECT 1 FROM organization_memberships membership
|
|
926
|
+
WHERE membership.id = snapshot.owner_organization_membership_id
|
|
927
|
+
AND membership.account_id = p_account_id
|
|
928
|
+
AND membership.subject_id = snapshot.owner_subject_id
|
|
929
|
+
AND membership.status = 'active' AND membership.revoked_at IS NULL
|
|
930
|
+
AND membership.authorization_revision = snapshot.membership_authorization_revision
|
|
931
|
+
AND (membership.personal_workspace_id = p_workspace_id OR EXISTS (
|
|
932
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
933
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
934
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
935
|
+
AND workspace_membership.subject_id = snapshot.owner_subject_id
|
|
936
|
+
))
|
|
937
|
+
) THEN reason := 'owner_membership_inactive';
|
|
938
|
+
ELSIF NOT EXISTS (
|
|
939
|
+
SELECT 1 FROM organization_user_resource_authorities authority
|
|
940
|
+
WHERE authority.id = snapshot.authority_id
|
|
941
|
+
AND authority.account_id = p_account_id
|
|
942
|
+
AND authority.organization_membership_id = snapshot.owner_organization_membership_id
|
|
943
|
+
AND authority.resource_kind = 'connection'
|
|
944
|
+
AND authority.resource_id = snapshot.connection_id
|
|
945
|
+
AND authority.origin_workspace_id = snapshot.origin_workspace_id
|
|
946
|
+
AND authority.generation = snapshot.authority_generation
|
|
947
|
+
AND authority.status = 'active' AND authority.revoked_at IS NULL
|
|
948
|
+
) THEN reason := 'authority_status_inactive';
|
|
949
|
+
ELSE
|
|
950
|
+
SELECT grant_value.* INTO grant_row
|
|
951
|
+
FROM organization_user_resource_grants grant_value
|
|
952
|
+
WHERE grant_value.id = snapshot.grant_id
|
|
953
|
+
AND grant_value.account_id = p_account_id
|
|
954
|
+
FOR UPDATE;
|
|
955
|
+
IF NOT FOUND THEN reason := 'grant_missing';
|
|
956
|
+
ELSIF grant_row.authority_id IS DISTINCT FROM snapshot.authority_id
|
|
957
|
+
OR grant_row.owner_organization_membership_id
|
|
958
|
+
IS DISTINCT FROM snapshot.owner_organization_membership_id
|
|
959
|
+
OR grant_row.workspace_id IS DISTINCT FROM p_workspace_id
|
|
960
|
+
OR grant_row.action <> 'connection.use'
|
|
961
|
+
OR grant_row.mode IS DISTINCT FROM snapshot.grant_mode
|
|
962
|
+
OR grant_row.context IS DISTINCT FROM snapshot.grant_context
|
|
963
|
+
OR grant_row.session_id IS DISTINCT FROM snapshot.grant_session_id
|
|
964
|
+
OR grant_row.authority_epoch IS DISTINCT FROM snapshot.grant_authority_epoch
|
|
965
|
+
THEN reason := 'grant_identity_changed';
|
|
966
|
+
ELSIF grant_row.generation IS DISTINCT FROM snapshot.grant_generation
|
|
967
|
+
THEN reason := 'grant_generation_changed';
|
|
968
|
+
ELSIF grant_row.expires_at IS NOT NULL AND grant_row.expires_at <= clock_timestamp()
|
|
969
|
+
THEN reason := 'grant_expired';
|
|
970
|
+
ELSIF grant_row.mode = 'once' THEN
|
|
971
|
+
IF grant_row.status <> 'consumed' OR NOT EXISTS (
|
|
972
|
+
SELECT 1 FROM connection_use_once_consumption_receipts receipt
|
|
973
|
+
WHERE receipt.grant_id = grant_row.id
|
|
974
|
+
AND receipt.accepted_work_kind = 'turn'
|
|
975
|
+
AND receipt.accepted_work_id = p_turn_id
|
|
976
|
+
AND receipt.authority_id = snapshot.authority_id
|
|
977
|
+
AND receipt.authority_generation = snapshot.authority_generation
|
|
978
|
+
AND receipt.grant_generation = snapshot.grant_generation
|
|
979
|
+
) THEN reason := 'grant_already_consumed';
|
|
980
|
+
END IF;
|
|
981
|
+
ELSIF grant_row.status <> 'active' THEN reason := 'grant_status_inactive';
|
|
982
|
+
END IF;
|
|
983
|
+
END IF;
|
|
984
|
+
resolved_connection_id := snapshot.connection_id;
|
|
985
|
+
connection_generation := snapshot.connection_generation;
|
|
986
|
+
origin_workspace_id := snapshot.origin_workspace_id;
|
|
987
|
+
resolved_connection_kind := snapshot.connection_kind;
|
|
988
|
+
authority_scope := snapshot.authority_scope;
|
|
989
|
+
owner_subject_id := snapshot.owner_subject_id;
|
|
990
|
+
authority_id := snapshot.authority_id;
|
|
991
|
+
grant_id := snapshot.grant_id;
|
|
992
|
+
ELSE
|
|
993
|
+
-- A true pre-tenancy legacy_user row has no common authority/grant to
|
|
994
|
+
-- snapshot. It may use only the exact connection frozen on the turn and
|
|
995
|
+
-- still receives the exact live-attempt, destination, generation, and
|
|
996
|
+
-- per-request audit fences above. Common `user` rows never enter this
|
|
997
|
+
-- compatibility lane: already-accepted pre-cutover work therefore fails
|
|
998
|
+
-- closed instead of falling back to its owner tuple.
|
|
999
|
+
SELECT connection_value.* INTO connection_row FROM connections connection_value
|
|
1000
|
+
WHERE connection_value.id = p_connection_id
|
|
1001
|
+
AND connection_value.account_id = p_account_id;
|
|
1002
|
+
IF NOT FOUND THEN reason := 'connection_missing';
|
|
1003
|
+
ELSIF connection_row.authority_scope IS DISTINCT FROM 'legacy_user'
|
|
1004
|
+
OR p_subject_scope IS DISTINCT FROM 'subject'
|
|
1005
|
+
OR p_owner_subject_id IS NULL
|
|
1006
|
+
OR connection_row.subject_id IS DISTINCT FROM p_owner_subject_id
|
|
1007
|
+
OR connection_row.workspace_id IS DISTINCT FROM p_workspace_id
|
|
1008
|
+
OR connection_row.origin_workspace_id IS DISTINCT FROM p_workspace_id
|
|
1009
|
+
OR lower(connection_row.provider_domain) IS DISTINCT FROM lower(p_provider_domain)
|
|
1010
|
+
OR (p_connection_kind IS NOT NULL
|
|
1011
|
+
AND connection_row.kind IS DISTINCT FROM p_connection_kind)
|
|
1012
|
+
THEN reason := 'connection_identity_changed';
|
|
1013
|
+
ELSIF connection_row.status <> 'active' THEN reason := 'connection_status_inactive';
|
|
1014
|
+
ELSE
|
|
1015
|
+
resolved_connection_id := connection_row.id;
|
|
1016
|
+
connection_generation := connection_row.authority_generation;
|
|
1017
|
+
origin_workspace_id := connection_row.origin_workspace_id;
|
|
1018
|
+
resolved_connection_kind := connection_row.kind;
|
|
1019
|
+
authority_scope := 'legacy_user';
|
|
1020
|
+
owner_subject_id := connection_row.subject_id;
|
|
1021
|
+
END IF;
|
|
1022
|
+
END IF;
|
|
1023
|
+
END IF;
|
|
1024
|
+
|
|
1025
|
+
authorization_status := CASE WHEN reason IS NULL THEN 'authorized' ELSE 'denied' END;
|
|
1026
|
+
denial_reason := reason;
|
|
1027
|
+
IF NOT has_prior THEN
|
|
1028
|
+
INSERT INTO connection_use_audit_facts (
|
|
1029
|
+
physical_request_id, use_phase, request_digest, account_id, workspace_id, session_id, turn_id,
|
|
1030
|
+
attempt_id, execution_generation, server_id, connection_id,
|
|
1031
|
+
connection_generation, authority_scope, owner_subject_id, authority_id,
|
|
1032
|
+
grant_id, outcome, denial_reason
|
|
1033
|
+
) VALUES (
|
|
1034
|
+
p_physical_request_id, p_use_phase, request_digest,
|
|
1035
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id,
|
|
1036
|
+
p_attempt_id, p_execution_generation, p_server_id, resolved_connection_id,
|
|
1037
|
+
connection_generation, authority_scope, owner_subject_id, authority_id,
|
|
1038
|
+
grant_id, authorization_status, denial_reason
|
|
1039
|
+
) ON CONFLICT (physical_request_id) DO NOTHING;
|
|
1040
|
+
END IF;
|
|
1041
|
+
RETURN NEXT;
|
|
1042
|
+
END
|
|
1043
|
+
$body$
|
|
1044
|
+
$ddl$, data_schema);
|
|
1045
|
+
END
|
|
1046
|
+
$connection_authority_runtime$;
|
|
1047
|
+
|
|
1048
|
+
REVOKE ALL ON FUNCTION opengeni_private.capture_accepted_turn_connection_authorities() FROM PUBLIC;
|
|
1049
|
+
REVOKE ALL ON FUNCTION resolve_connection_use_authority_legacy_0256(
|
|
1050
|
+
uuid, uuid, uuid, jsonb
|
|
1051
|
+
) FROM PUBLIC;
|
|
1052
|
+
REVOKE ALL ON FUNCTION resolve_connection_use_authority(uuid, uuid, uuid, jsonb) FROM PUBLIC;
|
|
1053
|
+
REVOKE ALL ON FUNCTION resolve_personal_connection_authority_selection(
|
|
1054
|
+
uuid, uuid, text, uuid, jsonb
|
|
1055
|
+
) FROM PUBLIC;
|
|
1056
|
+
REVOKE ALL ON FUNCTION resolve_accepted_connection_use(
|
|
1057
|
+
uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text
|
|
1058
|
+
) FROM PUBLIC;
|
|
1059
|
+
|
|
1060
|
+
DO $grants$
|
|
1061
|
+
BEGIN
|
|
1062
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
1063
|
+
REVOKE ALL ON FUNCTION resolve_connection_use_authority_legacy_0256(
|
|
1064
|
+
uuid, uuid, uuid, jsonb
|
|
1065
|
+
) FROM opengeni_app;
|
|
1066
|
+
GRANT EXECUTE ON FUNCTION resolve_connection_use_authority(
|
|
1067
|
+
uuid, uuid, uuid, jsonb
|
|
1068
|
+
) TO opengeni_app;
|
|
1069
|
+
GRANT EXECUTE ON FUNCTION resolve_personal_connection_authority_selection(
|
|
1070
|
+
uuid, uuid, text, uuid, jsonb
|
|
1071
|
+
) TO opengeni_app;
|
|
1072
|
+
GRANT EXECUTE ON FUNCTION resolve_accepted_connection_use(
|
|
1073
|
+
uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text
|
|
1074
|
+
) TO opengeni_app;
|
|
1075
|
+
REVOKE ALL ON TABLE turn_connection_authority_snapshots FROM opengeni_app;
|
|
1076
|
+
REVOKE ALL ON TABLE connection_use_audit_facts FROM opengeni_app;
|
|
1077
|
+
END IF;
|
|
1078
|
+
END
|
|
1079
|
+
$grants$;
|
|
1080
|
+
|
|
1081
|
+
COMMENT ON TABLE "turn_connection_authority_snapshots" IS
|
|
1082
|
+
'Canonical credential-free connection authority frozen for one accepted logical turn; runtime access is only through exact-attempt SECURITY DEFINER resolution.';
|
|
1083
|
+
COMMENT ON TABLE "connection_use_audit_facts" IS
|
|
1084
|
+
'Idempotent metadata-only facts for physical connection requests; contains no token, header, provider payload, response, quota, or usage value.';
|