@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,773 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Migration 0280: connection-use audit facts and variable-set audit events
|
|
3
|
+
-- carry the accepted authority attribution.
|
|
4
|
+
--
|
|
5
|
+
-- `connection_use_audit_facts` (0264) recorded what was used but not by whose
|
|
6
|
+
-- causal authority: no causal initiator, no session tenancy epoch/visibility,
|
|
7
|
+
-- no owning membership. The columns below are nullable audit evidence written
|
|
8
|
+
-- by the resolver from the exact rows the same transaction already locked -
|
|
9
|
+
-- the frozen turn identity verbatim (a pre-0096 sentinel stays the sentinel)
|
|
10
|
+
-- and the live session authority triple. A denial whose lifecycle fence never
|
|
11
|
+
-- loaded the row leaves NULL attribution, which is itself the honest fact.
|
|
12
|
+
-- No foreign keys: audit rows are immutable evidence and must never block a
|
|
13
|
+
-- membership lifecycle or retention operation.
|
|
14
|
+
--
|
|
15
|
+
-- The variable-set audit events (0254/0273) gain the same attribution in
|
|
16
|
+
-- their metadata: causal human, attempt authority epoch/visibility/owner
|
|
17
|
+
-- membership, and the variable set's owner authority/membership/origin.
|
|
18
|
+
-- Denials in these functions RAISE and roll the transaction back, so denial
|
|
19
|
+
-- recording stays with the application caller in a fresh transaction
|
|
20
|
+
-- (metadata-only, content-free) - recording inside the aborted transaction is
|
|
21
|
+
-- impossible by design, and weakening RAISE to a soft return would change the
|
|
22
|
+
-- authority contract.
|
|
23
|
+
--
|
|
24
|
+
-- Rolling window: nullable columns plus CREATE OR REPLACE of three existing
|
|
25
|
+
-- functions with unchanged signatures and grants; an old image keeps writing
|
|
26
|
+
-- audit rows without attribution exactly as before.
|
|
27
|
+
|
|
28
|
+
SET LOCAL lock_timeout = '5s';
|
|
29
|
+
SET LOCAL statement_timeout = '5min';
|
|
30
|
+
|
|
31
|
+
-- 1. Attribution columns ------------------------------------------------
|
|
32
|
+
-- Conditional on the 0264 cutover having been activated: a deployment that
|
|
33
|
+
-- defers the connection-authority maintenance cutover replays this rolling
|
|
34
|
+
-- migration without the audit table, and the resolver redefinition below is
|
|
35
|
+
-- inert there for the same reason the 0264 activation gate is.
|
|
36
|
+
|
|
37
|
+
DO $connection_audit_attribution_columns$
|
|
38
|
+
BEGIN
|
|
39
|
+
IF to_regclass('connection_use_audit_facts') IS NULL THEN
|
|
40
|
+
RETURN;
|
|
41
|
+
END IF;
|
|
42
|
+
ALTER TABLE "connection_use_audit_facts"
|
|
43
|
+
ADD COLUMN IF NOT EXISTS "initiator_kind" text,
|
|
44
|
+
ADD COLUMN IF NOT EXISTS "initiator_subject_id" text,
|
|
45
|
+
ADD COLUMN IF NOT EXISTS "initiating_human_subject_id" text,
|
|
46
|
+
ADD COLUMN IF NOT EXISTS "authority_epoch" integer,
|
|
47
|
+
ADD COLUMN IF NOT EXISTS "authority_visibility" text,
|
|
48
|
+
ADD COLUMN IF NOT EXISTS "authority_owner_organization_membership_id" uuid;
|
|
49
|
+
IF NOT EXISTS (
|
|
50
|
+
SELECT 1 FROM pg_constraint
|
|
51
|
+
WHERE conname = 'connection_use_audit_attribution_check'
|
|
52
|
+
AND conrelid = 'connection_use_audit_facts'::regclass
|
|
53
|
+
) THEN
|
|
54
|
+
ALTER TABLE "connection_use_audit_facts"
|
|
55
|
+
ADD CONSTRAINT "connection_use_audit_attribution_check"
|
|
56
|
+
CHECK (
|
|
57
|
+
("authority_visibility" IS NULL
|
|
58
|
+
OR "authority_visibility" IN ('user_private', 'workspace_shared'))
|
|
59
|
+
AND ("authority_epoch" IS NULL OR "authority_epoch" > 0)
|
|
60
|
+
) NOT VALID;
|
|
61
|
+
END IF;
|
|
62
|
+
ALTER TABLE "connection_use_audit_facts"
|
|
63
|
+
VALIDATE CONSTRAINT "connection_use_audit_attribution_check";
|
|
64
|
+
END
|
|
65
|
+
$connection_audit_attribution_columns$;
|
|
66
|
+
|
|
67
|
+
-- 2. The connection-use resolver records attribution --------------------
|
|
68
|
+
|
|
69
|
+
CREATE OR REPLACE FUNCTION resolve_accepted_connection_use(
|
|
70
|
+
p_account_id uuid, p_workspace_id uuid, p_session_id uuid, p_turn_id uuid,
|
|
71
|
+
p_attempt_id uuid, p_execution_generation integer, p_physical_request_id uuid,
|
|
72
|
+
p_use_phase text, p_server_id text, p_connection_id uuid,
|
|
73
|
+
p_provider_domain text, p_connection_kind text, p_subject_scope text,
|
|
74
|
+
p_owner_subject_id text DEFAULT NULL
|
|
75
|
+
) RETURNS TABLE (
|
|
76
|
+
authorization_status text, denial_reason text, resolved_connection_id uuid,
|
|
77
|
+
connection_generation bigint, origin_workspace_id uuid,
|
|
78
|
+
resolved_connection_kind text, authority_scope text, owner_subject_id text,
|
|
79
|
+
authority_id uuid, grant_id uuid
|
|
80
|
+
)
|
|
81
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
82
|
+
SET search_path = pg_catalog, pg_temp
|
|
83
|
+
AS $body$
|
|
84
|
+
#variable_conflict use_column
|
|
85
|
+
DECLARE
|
|
86
|
+
prior record;
|
|
87
|
+
connection_row record;
|
|
88
|
+
snapshot record;
|
|
89
|
+
session_row record;
|
|
90
|
+
turn_row record;
|
|
91
|
+
audit_initiator_kind text;
|
|
92
|
+
audit_initiator_subject text;
|
|
93
|
+
audit_initiating_human text;
|
|
94
|
+
audit_authority_epoch integer;
|
|
95
|
+
audit_authority_visibility text;
|
|
96
|
+
audit_owner_membership uuid;
|
|
97
|
+
grant_row record;
|
|
98
|
+
request_digest bytea;
|
|
99
|
+
has_prior boolean := false;
|
|
100
|
+
reason text;
|
|
101
|
+
scheduled_run_id uuid;
|
|
102
|
+
BEGIN
|
|
103
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
104
|
+
OR p_workspace_id IS DISTINCT FROM nullif(current_setting('opengeni.workspace_id', true), '')::uuid
|
|
105
|
+
OR p_execution_generation <= 0
|
|
106
|
+
OR p_use_phase NOT IN ('credential_resolution', 'provider_request')
|
|
107
|
+
OR nullif(btrim(p_server_id), '') IS NULL OR octet_length(p_server_id) > 256
|
|
108
|
+
THEN RAISE EXCEPTION 'connection use scope mismatch' USING ERRCODE = '42501';
|
|
109
|
+
END IF;
|
|
110
|
+
request_digest := digest(convert_to(jsonb_build_object(
|
|
111
|
+
'accountId', p_account_id, 'workspaceId', p_workspace_id,
|
|
112
|
+
'sessionId', p_session_id, 'turnId', p_turn_id, 'attemptId', p_attempt_id,
|
|
113
|
+
'executionGeneration', p_execution_generation, 'usePhase', p_use_phase,
|
|
114
|
+
'serverId', p_server_id, 'connectionId', p_connection_id,
|
|
115
|
+
'providerDomain', lower(p_provider_domain), 'connectionKind', p_connection_kind,
|
|
116
|
+
'subjectScope', p_subject_scope, 'ownerSubjectId', p_owner_subject_id
|
|
117
|
+
)::text, 'UTF8'), 'sha256');
|
|
118
|
+
|
|
119
|
+
-- Canonical lifecycle lock prefix: control -> workspace -> session -> turn -> attempt.
|
|
120
|
+
PERFORM 1 FROM workspace_inference_controls control_row
|
|
121
|
+
WHERE control_row.account_id = p_account_id AND control_row.workspace_id = p_workspace_id
|
|
122
|
+
FOR SHARE;
|
|
123
|
+
IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
|
|
124
|
+
IF reason IS NULL THEN
|
|
125
|
+
PERFORM 1 FROM workspaces workspace_value
|
|
126
|
+
WHERE workspace_value.account_id = p_account_id AND workspace_value.id = p_workspace_id
|
|
127
|
+
FOR KEY SHARE;
|
|
128
|
+
IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
|
|
129
|
+
END IF;
|
|
130
|
+
IF reason IS NULL THEN
|
|
131
|
+
SELECT session_value.* INTO session_row FROM sessions session_value
|
|
132
|
+
WHERE session_value.id = p_session_id AND session_value.account_id = p_account_id
|
|
133
|
+
AND session_value.workspace_id = p_workspace_id
|
|
134
|
+
FOR NO KEY UPDATE;
|
|
135
|
+
IF NOT FOUND OR session_row.active_turn_id IS DISTINCT FROM p_turn_id
|
|
136
|
+
OR session_row.status = 'cancelled'
|
|
137
|
+
THEN reason := 'session_identity_changed'; END IF;
|
|
138
|
+
-- Attribution evidence (0280): captured from the row this same
|
|
139
|
+
-- transaction locked, NULL when the fence never loaded it.
|
|
140
|
+
audit_authority_epoch := session_row.authority_epoch;
|
|
141
|
+
audit_authority_visibility := session_row.visibility;
|
|
142
|
+
audit_owner_membership := session_row.owner_organization_membership_id;
|
|
143
|
+
END IF;
|
|
144
|
+
IF reason IS NULL THEN
|
|
145
|
+
SELECT turn_value.* INTO turn_row FROM session_turns turn_value
|
|
146
|
+
WHERE turn_value.id = p_turn_id AND turn_value.account_id = p_account_id
|
|
147
|
+
AND turn_value.workspace_id = p_workspace_id AND turn_value.session_id = p_session_id
|
|
148
|
+
AND turn_value.active_attempt_id = p_attempt_id
|
|
149
|
+
AND turn_value.execution_generation = p_execution_generation
|
|
150
|
+
AND turn_value.status = 'running'
|
|
151
|
+
FOR UPDATE;
|
|
152
|
+
IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
|
|
153
|
+
-- Causal-initiator evidence (0280): the frozen turn identity, recorded
|
|
154
|
+
-- verbatim (a pre-0096 sentinel stays the sentinel), NULL when the turn
|
|
155
|
+
-- fence never matched a row.
|
|
156
|
+
audit_initiator_kind := turn_row.initiator_kind;
|
|
157
|
+
audit_initiator_subject := turn_row.initiator_subject_id;
|
|
158
|
+
audit_initiating_human := turn_row.initiating_human_subject_id;
|
|
159
|
+
END IF;
|
|
160
|
+
IF reason IS NULL THEN
|
|
161
|
+
PERFORM 1 FROM session_turn_attempts attempt
|
|
162
|
+
WHERE attempt.id = p_attempt_id AND attempt.account_id = p_account_id
|
|
163
|
+
AND attempt.workspace_id = p_workspace_id AND attempt.session_id = p_session_id
|
|
164
|
+
AND attempt.turn_id = p_turn_id
|
|
165
|
+
AND attempt.execution_generation = p_execution_generation
|
|
166
|
+
AND attempt.state IN ('claimed', 'running')
|
|
167
|
+
AND attempt.closed_at IS NULL AND attempt.quiesced_at IS NULL
|
|
168
|
+
AND attempt.authority_visibility = session_row.visibility
|
|
169
|
+
AND attempt.authority_epoch = session_row.authority_epoch
|
|
170
|
+
AND attempt.authority_owner_organization_membership_id
|
|
171
|
+
IS NOT DISTINCT FROM session_row.owner_organization_membership_id
|
|
172
|
+
FOR UPDATE;
|
|
173
|
+
IF NOT FOUND OR EXISTS (
|
|
174
|
+
SELECT 1 FROM session_attempt_interruptions interruption
|
|
175
|
+
WHERE interruption.account_id = p_account_id
|
|
176
|
+
AND interruption.workspace_id = p_workspace_id
|
|
177
|
+
AND interruption.session_id = p_session_id
|
|
178
|
+
AND interruption.attempt_id = p_attempt_id
|
|
179
|
+
AND interruption.state IN ('pending', 'delivered', 'acknowledged')
|
|
180
|
+
) THEN reason := 'session_identity_changed'; END IF;
|
|
181
|
+
END IF;
|
|
182
|
+
|
|
183
|
+
PERFORM pg_advisory_xact_lock(hashtextextended(p_physical_request_id::text, 0));
|
|
184
|
+
SELECT EXISTS (SELECT 1 FROM connection_use_audit_facts audit
|
|
185
|
+
WHERE audit.physical_request_id = p_physical_request_id) INTO has_prior;
|
|
186
|
+
-- Only dereference `prior` inside the guarded branch: PL/pgSQL evaluates
|
|
187
|
+
-- both operands of `has_prior AND prior.<field>`, and an unassigned record
|
|
188
|
+
-- raises instead of yielding NULL.
|
|
189
|
+
IF has_prior THEN
|
|
190
|
+
SELECT audit.* INTO STRICT prior FROM connection_use_audit_facts audit
|
|
191
|
+
WHERE audit.physical_request_id = p_physical_request_id;
|
|
192
|
+
IF prior.request_digest IS DISTINCT FROM request_digest THEN
|
|
193
|
+
RAISE EXCEPTION 'physical connection request id was reused for different work'
|
|
194
|
+
USING ERRCODE = '23505';
|
|
195
|
+
END IF;
|
|
196
|
+
IF reason IS NULL AND prior.outcome = 'denied' THEN
|
|
197
|
+
authorization_status := prior.outcome; denial_reason := prior.denial_reason;
|
|
198
|
+
RETURN NEXT; RETURN;
|
|
199
|
+
END IF;
|
|
200
|
+
END IF;
|
|
201
|
+
|
|
202
|
+
IF reason IS NULL THEN
|
|
203
|
+
SELECT authority_snapshot.* INTO snapshot
|
|
204
|
+
FROM turn_connection_authority_snapshots authority_snapshot
|
|
205
|
+
WHERE authority_snapshot.turn_id = p_turn_id
|
|
206
|
+
AND authority_snapshot.server_id = p_server_id
|
|
207
|
+
AND authority_snapshot.account_id = p_account_id
|
|
208
|
+
AND authority_snapshot.workspace_id = p_workspace_id
|
|
209
|
+
AND authority_snapshot.session_id = p_session_id;
|
|
210
|
+
IF FOUND THEN
|
|
211
|
+
scheduled_run_id := CASE
|
|
212
|
+
WHEN snapshot.canonical_snapshot -> 'acceptedWork' ->> 'kind' = 'scheduled_task'
|
|
213
|
+
THEN nullif(snapshot.canonical_snapshot -> 'acceptedWork' ->> 'runId', '')::uuid
|
|
214
|
+
ELSE NULL END;
|
|
215
|
+
SELECT connection_value.* INTO connection_row FROM connections connection_value
|
|
216
|
+
WHERE connection_value.id = snapshot.connection_id
|
|
217
|
+
AND connection_value.account_id = p_account_id;
|
|
218
|
+
IF snapshot.snapshot_digest IS DISTINCT FROM digest(
|
|
219
|
+
convert_to(snapshot.canonical_snapshot::text, 'UTF8'), 'sha256'
|
|
220
|
+
) THEN reason := 'accepted_snapshot_digest_changed';
|
|
221
|
+
ELSIF NOT FOUND THEN reason := 'connection_missing';
|
|
222
|
+
ELSIF snapshot.authority_scope IS DISTINCT FROM 'user'
|
|
223
|
+
OR p_subject_scope IS DISTINCT FROM 'subject'
|
|
224
|
+
OR p_connection_id IS DISTINCT FROM snapshot.connection_id
|
|
225
|
+
OR lower(p_provider_domain) IS DISTINCT FROM snapshot.provider_domain
|
|
226
|
+
OR (p_connection_kind IS NOT NULL
|
|
227
|
+
AND p_connection_kind IS DISTINCT FROM snapshot.connection_kind)
|
|
228
|
+
THEN reason := 'connection_identity_changed';
|
|
229
|
+
ELSIF connection_row.status <> 'active' THEN reason := 'connection_status_inactive';
|
|
230
|
+
ELSIF connection_row.authority_scope IS DISTINCT FROM 'user'
|
|
231
|
+
OR connection_row.subject_id IS DISTINCT FROM snapshot.owner_subject_id
|
|
232
|
+
OR connection_row.owner_organization_membership_id
|
|
233
|
+
IS DISTINCT FROM snapshot.owner_organization_membership_id
|
|
234
|
+
OR connection_row.origin_workspace_id IS DISTINCT FROM snapshot.origin_workspace_id
|
|
235
|
+
THEN reason := 'connection_owner_changed';
|
|
236
|
+
ELSIF connection_row.authority_generation IS DISTINCT FROM snapshot.connection_generation
|
|
237
|
+
THEN reason := 'connection_generation_changed';
|
|
238
|
+
ELSIF session_row.visibility IS DISTINCT FROM snapshot.session_visibility
|
|
239
|
+
OR session_row.authority_epoch IS DISTINCT FROM snapshot.session_authority_epoch
|
|
240
|
+
THEN reason := 'session_authority_epoch_changed';
|
|
241
|
+
ELSIF NOT EXISTS (
|
|
242
|
+
SELECT 1 FROM organization_memberships membership
|
|
243
|
+
WHERE membership.id = snapshot.owner_organization_membership_id
|
|
244
|
+
AND membership.account_id = p_account_id
|
|
245
|
+
AND membership.subject_id = snapshot.owner_subject_id
|
|
246
|
+
AND membership.status = 'active' AND membership.revoked_at IS NULL
|
|
247
|
+
AND membership.authorization_revision = snapshot.membership_authorization_revision
|
|
248
|
+
AND (membership.personal_workspace_id = p_workspace_id OR EXISTS (
|
|
249
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
250
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
251
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
252
|
+
AND workspace_membership.subject_id = snapshot.owner_subject_id
|
|
253
|
+
))
|
|
254
|
+
) THEN reason := 'owner_membership_inactive';
|
|
255
|
+
ELSIF NOT EXISTS (
|
|
256
|
+
SELECT 1 FROM organization_user_resource_authorities authority
|
|
257
|
+
WHERE authority.id = snapshot.authority_id AND authority.account_id = p_account_id
|
|
258
|
+
AND authority.organization_membership_id = snapshot.owner_organization_membership_id
|
|
259
|
+
AND authority.resource_kind = 'connection'
|
|
260
|
+
AND authority.resource_id = snapshot.connection_id
|
|
261
|
+
AND authority.origin_workspace_id = snapshot.origin_workspace_id
|
|
262
|
+
AND authority.generation = snapshot.authority_generation
|
|
263
|
+
AND authority.status = 'active' AND authority.revoked_at IS NULL
|
|
264
|
+
) THEN reason := 'authority_status_inactive';
|
|
265
|
+
ELSE
|
|
266
|
+
SELECT grant_value.* INTO grant_row FROM organization_user_resource_grants grant_value
|
|
267
|
+
WHERE grant_value.id = snapshot.grant_id AND grant_value.account_id = p_account_id
|
|
268
|
+
FOR UPDATE;
|
|
269
|
+
IF NOT FOUND THEN reason := 'grant_missing';
|
|
270
|
+
ELSIF grant_row.authority_id IS DISTINCT FROM snapshot.authority_id
|
|
271
|
+
OR grant_row.owner_organization_membership_id
|
|
272
|
+
IS DISTINCT FROM snapshot.owner_organization_membership_id
|
|
273
|
+
OR grant_row.workspace_id IS DISTINCT FROM p_workspace_id
|
|
274
|
+
OR grant_row.action <> 'connection.use'
|
|
275
|
+
OR grant_row.mode IS DISTINCT FROM snapshot.grant_mode
|
|
276
|
+
OR grant_row.context IS DISTINCT FROM snapshot.grant_context
|
|
277
|
+
OR grant_row.session_id IS DISTINCT FROM snapshot.grant_session_id
|
|
278
|
+
OR grant_row.authority_epoch IS DISTINCT FROM snapshot.grant_authority_epoch
|
|
279
|
+
THEN reason := 'grant_identity_changed';
|
|
280
|
+
ELSIF grant_row.generation IS DISTINCT FROM snapshot.grant_generation
|
|
281
|
+
THEN reason := 'grant_generation_changed';
|
|
282
|
+
ELSIF grant_row.expires_at IS NOT NULL AND grant_row.expires_at <= clock_timestamp()
|
|
283
|
+
THEN reason := 'grant_expired';
|
|
284
|
+
ELSIF grant_row.mode = 'once' THEN
|
|
285
|
+
IF grant_row.status <> 'consumed' OR NOT EXISTS (
|
|
286
|
+
SELECT 1 FROM connection_use_once_consumption_receipts receipt
|
|
287
|
+
WHERE receipt.grant_id = grant_row.id
|
|
288
|
+
AND receipt.authority_id = snapshot.authority_id
|
|
289
|
+
AND receipt.authority_generation = snapshot.authority_generation
|
|
290
|
+
AND receipt.grant_generation = snapshot.grant_generation
|
|
291
|
+
AND (
|
|
292
|
+
(receipt.accepted_work_kind = 'turn'
|
|
293
|
+
AND receipt.accepted_work_id = p_turn_id)
|
|
294
|
+
OR (receipt.accepted_work_kind = 'scheduled_task'
|
|
295
|
+
AND scheduled_run_id IS NOT NULL
|
|
296
|
+
AND receipt.accepted_work_id = scheduled_run_id)
|
|
297
|
+
)
|
|
298
|
+
) THEN reason := 'grant_already_consumed'; END IF;
|
|
299
|
+
ELSIF grant_row.status <> 'active' THEN reason := 'grant_status_inactive';
|
|
300
|
+
END IF;
|
|
301
|
+
END IF;
|
|
302
|
+
resolved_connection_id := snapshot.connection_id;
|
|
303
|
+
connection_generation := snapshot.connection_generation;
|
|
304
|
+
origin_workspace_id := snapshot.origin_workspace_id;
|
|
305
|
+
resolved_connection_kind := snapshot.connection_kind;
|
|
306
|
+
authority_scope := snapshot.authority_scope;
|
|
307
|
+
owner_subject_id := snapshot.owner_subject_id;
|
|
308
|
+
authority_id := snapshot.authority_id;
|
|
309
|
+
grant_id := snapshot.grant_id;
|
|
310
|
+
ELSIF p_subject_scope = 'workspace' THEN
|
|
311
|
+
-- Workspace lane (0279): a workspace-owned connection is ambient shared
|
|
312
|
+
-- workspace capability, never frozen on the turn, so it is validated
|
|
313
|
+
-- against the live row inside the same canonical lifecycle fences and
|
|
314
|
+
-- recorded in the same idempotent audit facts. A frozen per-turn
|
|
315
|
+
-- personal snapshot for this server takes the snapshot lane above and
|
|
316
|
+
-- therefore denies a workspace-scope request outright (fail closed).
|
|
317
|
+
SELECT connection_value.* INTO connection_row FROM connections connection_value
|
|
318
|
+
WHERE connection_value.id = p_connection_id AND connection_value.account_id = p_account_id;
|
|
319
|
+
IF p_connection_id IS NULL OR NOT FOUND THEN reason := 'connection_missing';
|
|
320
|
+
ELSIF connection_row.authority_scope IS DISTINCT FROM 'workspace'
|
|
321
|
+
OR p_owner_subject_id IS NOT NULL
|
|
322
|
+
OR connection_row.workspace_id IS DISTINCT FROM p_workspace_id
|
|
323
|
+
OR connection_row.origin_workspace_id IS DISTINCT FROM p_workspace_id
|
|
324
|
+
OR lower(connection_row.provider_domain) IS DISTINCT FROM lower(p_provider_domain)
|
|
325
|
+
OR (p_connection_kind IS NOT NULL
|
|
326
|
+
AND connection_row.kind IS DISTINCT FROM p_connection_kind)
|
|
327
|
+
THEN reason := 'connection_identity_changed';
|
|
328
|
+
ELSIF connection_row.status <> 'active' THEN reason := 'connection_status_inactive';
|
|
329
|
+
ELSE
|
|
330
|
+
resolved_connection_id := connection_row.id;
|
|
331
|
+
connection_generation := connection_row.authority_generation;
|
|
332
|
+
origin_workspace_id := connection_row.origin_workspace_id;
|
|
333
|
+
resolved_connection_kind := connection_row.kind;
|
|
334
|
+
authority_scope := 'workspace';
|
|
335
|
+
END IF;
|
|
336
|
+
ELSE
|
|
337
|
+
SELECT connection_value.* INTO connection_row FROM connections connection_value
|
|
338
|
+
WHERE connection_value.id = p_connection_id AND connection_value.account_id = p_account_id;
|
|
339
|
+
IF NOT FOUND THEN reason := 'connection_missing';
|
|
340
|
+
ELSIF connection_row.authority_scope IS DISTINCT FROM 'legacy_user'
|
|
341
|
+
OR p_subject_scope IS DISTINCT FROM 'subject' OR p_owner_subject_id IS NULL
|
|
342
|
+
OR connection_row.subject_id IS DISTINCT FROM p_owner_subject_id
|
|
343
|
+
OR connection_row.workspace_id IS DISTINCT FROM p_workspace_id
|
|
344
|
+
OR connection_row.origin_workspace_id IS DISTINCT FROM p_workspace_id
|
|
345
|
+
OR lower(connection_row.provider_domain) IS DISTINCT FROM lower(p_provider_domain)
|
|
346
|
+
OR (p_connection_kind IS NOT NULL
|
|
347
|
+
AND connection_row.kind IS DISTINCT FROM p_connection_kind)
|
|
348
|
+
THEN reason := 'connection_identity_changed';
|
|
349
|
+
ELSIF connection_row.status <> 'active' THEN reason := 'connection_status_inactive';
|
|
350
|
+
ELSE
|
|
351
|
+
resolved_connection_id := connection_row.id;
|
|
352
|
+
connection_generation := connection_row.authority_generation;
|
|
353
|
+
origin_workspace_id := connection_row.origin_workspace_id;
|
|
354
|
+
resolved_connection_kind := connection_row.kind;
|
|
355
|
+
authority_scope := 'legacy_user'; owner_subject_id := connection_row.subject_id;
|
|
356
|
+
END IF;
|
|
357
|
+
END IF;
|
|
358
|
+
END IF;
|
|
359
|
+
|
|
360
|
+
authorization_status := CASE WHEN reason IS NULL THEN 'authorized' ELSE 'denied' END;
|
|
361
|
+
denial_reason := reason;
|
|
362
|
+
IF NOT has_prior THEN
|
|
363
|
+
INSERT INTO connection_use_audit_facts (
|
|
364
|
+
physical_request_id, use_phase, request_digest, account_id, workspace_id,
|
|
365
|
+
session_id, turn_id, attempt_id, execution_generation, server_id,
|
|
366
|
+
connection_id, connection_generation, authority_scope, owner_subject_id,
|
|
367
|
+
authority_id, grant_id, outcome, denial_reason,
|
|
368
|
+
initiator_kind, initiator_subject_id, initiating_human_subject_id,
|
|
369
|
+
authority_epoch, authority_visibility,
|
|
370
|
+
authority_owner_organization_membership_id
|
|
371
|
+
) VALUES (
|
|
372
|
+
p_physical_request_id, p_use_phase, request_digest, p_account_id,
|
|
373
|
+
p_workspace_id, p_session_id, p_turn_id, p_attempt_id,
|
|
374
|
+
p_execution_generation, p_server_id, resolved_connection_id,
|
|
375
|
+
connection_generation, authority_scope, owner_subject_id, authority_id,
|
|
376
|
+
grant_id, authorization_status, denial_reason,
|
|
377
|
+
audit_initiator_kind, audit_initiator_subject, audit_initiating_human,
|
|
378
|
+
audit_authority_epoch, audit_authority_visibility, audit_owner_membership
|
|
379
|
+
) ON CONFLICT (physical_request_id) DO NOTHING;
|
|
380
|
+
END IF;
|
|
381
|
+
RETURN NEXT;
|
|
382
|
+
END
|
|
383
|
+
$body$;
|
|
384
|
+
|
|
385
|
+
DO $connection_audit_attribution_posture$
|
|
386
|
+
DECLARE
|
|
387
|
+
data_schema text := pg_catalog.current_schema();
|
|
388
|
+
signature text := 'resolve_accepted_connection_use'
|
|
389
|
+
|| '(uuid,uuid,uuid,uuid,uuid,integer,uuid,text,text,uuid,text,text,text,text)';
|
|
390
|
+
BEGIN
|
|
391
|
+
-- Same re-pin as 0279: CREATE OR REPLACE replaced the attached search_path
|
|
392
|
+
-- with the minimal header value; `public` stays for the pgcrypto digest().
|
|
393
|
+
EXECUTE format(
|
|
394
|
+
'ALTER FUNCTION %I.%s SET search_path = pg_catalog, %I, public, pg_temp',
|
|
395
|
+
data_schema, signature, data_schema
|
|
396
|
+
);
|
|
397
|
+
EXECUTE format('REVOKE ALL ON FUNCTION %I.%s FROM PUBLIC', data_schema, signature);
|
|
398
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
399
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION %I.%s TO opengeni_app', data_schema, signature);
|
|
400
|
+
END IF;
|
|
401
|
+
END
|
|
402
|
+
$connection_audit_attribution_posture$;
|
|
403
|
+
|
|
404
|
+
-- 3. Variable-set materialization audit attribution ---------------------
|
|
405
|
+
|
|
406
|
+
CREATE OR REPLACE FUNCTION materialize_scoped_variable_set_for_attempt(
|
|
407
|
+
p_account_id uuid,
|
|
408
|
+
p_workspace_id uuid,
|
|
409
|
+
p_session_id uuid,
|
|
410
|
+
p_turn_id uuid,
|
|
411
|
+
p_attempt_id uuid,
|
|
412
|
+
p_execution_generation integer,
|
|
413
|
+
p_variable_set_id uuid
|
|
414
|
+
) RETURNS TABLE (
|
|
415
|
+
variable_set_id uuid,
|
|
416
|
+
variable_set_name text,
|
|
417
|
+
variable_set_description text,
|
|
418
|
+
authority_scope text,
|
|
419
|
+
variable_set_generation bigint,
|
|
420
|
+
variable_name text,
|
|
421
|
+
value_encrypted text
|
|
422
|
+
)
|
|
423
|
+
LANGUAGE plpgsql
|
|
424
|
+
SECURITY DEFINER
|
|
425
|
+
SET search_path FROM CURRENT
|
|
426
|
+
AS $$
|
|
427
|
+
DECLARE
|
|
428
|
+
audit_subject text;
|
|
429
|
+
causal_human text;
|
|
430
|
+
audit_actor_kind text;
|
|
431
|
+
variable_set_row workspace_variable_sets%ROWTYPE;
|
|
432
|
+
resolved_count integer;
|
|
433
|
+
attempt_authority_epoch integer;
|
|
434
|
+
attempt_authority_visibility text;
|
|
435
|
+
attempt_owner_membership uuid;
|
|
436
|
+
BEGIN
|
|
437
|
+
INSERT INTO opengeni_private.variable_set_authority_capabilities (
|
|
438
|
+
backend_pid, transaction_id, capability_kind
|
|
439
|
+
) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'materialize')
|
|
440
|
+
ON CONFLICT DO NOTHING;
|
|
441
|
+
|
|
442
|
+
SELECT actor_subject, causal_human_subject, actor_kind
|
|
443
|
+
INTO STRICT audit_subject, causal_human, audit_actor_kind
|
|
444
|
+
FROM assert_scoped_variable_set_materialization_attempt(
|
|
445
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id, p_attempt_id,
|
|
446
|
+
p_execution_generation
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
-- Attribution evidence (0280): the exact attempt the assert above already
|
|
450
|
+
-- validated owns the frozen session tenancy authority for this use.
|
|
451
|
+
SELECT attempt.authority_epoch, attempt.authority_visibility,
|
|
452
|
+
attempt.authority_owner_organization_membership_id
|
|
453
|
+
INTO attempt_authority_epoch, attempt_authority_visibility, attempt_owner_membership
|
|
454
|
+
FROM session_turn_attempts attempt
|
|
455
|
+
WHERE attempt.id = p_attempt_id
|
|
456
|
+
AND attempt.account_id = p_account_id
|
|
457
|
+
AND attempt.workspace_id = p_workspace_id;
|
|
458
|
+
|
|
459
|
+
SELECT variable_set.* INTO STRICT variable_set_row
|
|
460
|
+
FROM workspace_variable_sets variable_set
|
|
461
|
+
WHERE variable_set.id = p_variable_set_id
|
|
462
|
+
AND variable_set.account_id = p_account_id
|
|
463
|
+
AND variable_set.status = 'active'
|
|
464
|
+
FOR SHARE;
|
|
465
|
+
|
|
466
|
+
PERFORM 1 FROM sessions session_value
|
|
467
|
+
LEFT JOIN rig_versions rig_version
|
|
468
|
+
ON rig_version.id = session_value.rig_version_id
|
|
469
|
+
AND rig_version.rig_id = session_value.rig_id
|
|
470
|
+
AND rig_version.account_id = session_value.account_id
|
|
471
|
+
WHERE session_value.id = p_session_id
|
|
472
|
+
AND session_value.account_id = p_account_id
|
|
473
|
+
AND session_value.workspace_id = p_workspace_id
|
|
474
|
+
AND (
|
|
475
|
+
session_value.variable_set_id = variable_set_row.id
|
|
476
|
+
OR coalesce(rig_version.default_variable_set_ids, '[]'::jsonb)
|
|
477
|
+
? variable_set_row.id::text
|
|
478
|
+
);
|
|
479
|
+
IF NOT FOUND THEN
|
|
480
|
+
RAISE EXCEPTION 'runtime variable set was not selected by the exact session'
|
|
481
|
+
USING ERRCODE = '42501';
|
|
482
|
+
END IF;
|
|
483
|
+
|
|
484
|
+
IF variable_set_row.authority_scope = 'workspace'
|
|
485
|
+
AND variable_set_row.workspace_id IS DISTINCT FROM p_workspace_id
|
|
486
|
+
THEN
|
|
487
|
+
RAISE EXCEPTION 'workspace variable set is outside the runtime workspace'
|
|
488
|
+
USING ERRCODE = '42501';
|
|
489
|
+
ELSIF variable_set_row.authority_scope = 'user' THEN
|
|
490
|
+
IF causal_human IS NULL THEN
|
|
491
|
+
RAISE EXCEPTION 'personal variable-set materialization requires an initiating human subject'
|
|
492
|
+
USING ERRCODE = '42501';
|
|
493
|
+
END IF;
|
|
494
|
+
SELECT count(*)::integer INTO resolved_count
|
|
495
|
+
FROM resolve_session_attempt_personal_resources(
|
|
496
|
+
p_account_id, p_workspace_id, p_attempt_id
|
|
497
|
+
) resolved
|
|
498
|
+
WHERE resolved.resource_kind = 'variable_set'
|
|
499
|
+
AND resolved.resource_id = variable_set_row.id
|
|
500
|
+
AND resolved.authority_id = variable_set_row.authority_id
|
|
501
|
+
AND resolved.authority_generation = variable_set_row.generation;
|
|
502
|
+
IF resolved_count <> 1 THEN
|
|
503
|
+
RAISE EXCEPTION 'personal variable-set grant is not exact or current'
|
|
504
|
+
USING ERRCODE = '42501';
|
|
505
|
+
END IF;
|
|
506
|
+
END IF;
|
|
507
|
+
|
|
508
|
+
INSERT INTO audit_events (
|
|
509
|
+
account_id, workspace_id, subject_id, action, target_type, target_id,
|
|
510
|
+
metadata, metadata_codec_version
|
|
511
|
+
) VALUES (
|
|
512
|
+
p_account_id, p_workspace_id, audit_subject,
|
|
513
|
+
'variable_set.materialized', 'workspace_variable_set',
|
|
514
|
+
variable_set_row.id::text,
|
|
515
|
+
pg_catalog.jsonb_build_object(
|
|
516
|
+
'variableSetId', variable_set_row.id,
|
|
517
|
+
'scope', variable_set_row.authority_scope,
|
|
518
|
+
'generation', variable_set_row.generation,
|
|
519
|
+
'actorKind', audit_actor_kind,
|
|
520
|
+
'sessionId', p_session_id,
|
|
521
|
+
'turnId', p_turn_id,
|
|
522
|
+
'attemptId', p_attempt_id,
|
|
523
|
+
'executionGeneration', p_execution_generation,
|
|
524
|
+
'causalHumanSubjectId', causal_human,
|
|
525
|
+
'authorityEpoch', attempt_authority_epoch,
|
|
526
|
+
'authorityVisibility', attempt_authority_visibility,
|
|
527
|
+
'authorityOwnerOrganizationMembershipId', attempt_owner_membership,
|
|
528
|
+
'ownerAuthorityId', variable_set_row.authority_id,
|
|
529
|
+
'ownerOrganizationMembershipId', variable_set_row.owner_organization_membership_id,
|
|
530
|
+
'originWorkspaceId', variable_set_row.origin_workspace_id
|
|
531
|
+
),
|
|
532
|
+
1
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
RETURN QUERY
|
|
536
|
+
SELECT selected.id, selected.name, selected.description,
|
|
537
|
+
selected.authority_scope, selected.generation,
|
|
538
|
+
variable.name, variable.value_encrypted
|
|
539
|
+
FROM workspace_variable_sets selected
|
|
540
|
+
LEFT JOIN workspace_variable_set_variables variable
|
|
541
|
+
ON variable.account_id = selected.account_id
|
|
542
|
+
AND variable.variable_set_id = selected.id
|
|
543
|
+
WHERE selected.id = variable_set_row.id
|
|
544
|
+
ORDER BY variable.name;
|
|
545
|
+
|
|
546
|
+
DELETE FROM opengeni_private.variable_set_authority_capabilities
|
|
547
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
548
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
549
|
+
AND capability_kind = 'materialize';
|
|
550
|
+
RETURN;
|
|
551
|
+
EXCEPTION WHEN OTHERS THEN
|
|
552
|
+
DELETE FROM opengeni_private.variable_set_authority_capabilities
|
|
553
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
554
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
555
|
+
AND capability_kind = 'materialize';
|
|
556
|
+
RAISE;
|
|
557
|
+
END
|
|
558
|
+
$$;
|
|
559
|
+
|
|
560
|
+
REVOKE ALL ON FUNCTION materialize_scoped_variable_set_for_attempt(
|
|
561
|
+
uuid, uuid, uuid, uuid, uuid, integer, uuid
|
|
562
|
+
) FROM PUBLIC;
|
|
563
|
+
DO $materialize_attribution_grant$
|
|
564
|
+
BEGIN
|
|
565
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
566
|
+
GRANT EXECUTE ON FUNCTION materialize_scoped_variable_set_for_attempt(
|
|
567
|
+
uuid, uuid, uuid, uuid, uuid, integer, uuid
|
|
568
|
+
) TO opengeni_app;
|
|
569
|
+
END IF;
|
|
570
|
+
END
|
|
571
|
+
$materialize_attribution_grant$;
|
|
572
|
+
|
|
573
|
+
-- 4. Variable-set secret-read audit attribution -------------------------
|
|
574
|
+
|
|
575
|
+
DO $secret_read_attribution$
|
|
576
|
+
DECLARE
|
|
577
|
+
data_schema text := pg_catalog.current_schema();
|
|
578
|
+
BEGIN
|
|
579
|
+
EXECUTE format($ddl$
|
|
580
|
+
CREATE OR REPLACE FUNCTION %1$I.read_scoped_variable_set_secret(
|
|
581
|
+
p_account_id uuid,
|
|
582
|
+
p_workspace_id uuid,
|
|
583
|
+
p_variable_set_id uuid,
|
|
584
|
+
p_variable_set_name text,
|
|
585
|
+
p_variable_name text,
|
|
586
|
+
p_actor_kind text,
|
|
587
|
+
p_session_id uuid DEFAULT NULL,
|
|
588
|
+
p_turn_id uuid DEFAULT NULL,
|
|
589
|
+
p_attempt_id uuid DEFAULT NULL,
|
|
590
|
+
p_execution_generation integer DEFAULT NULL
|
|
591
|
+
) RETURNS TABLE (
|
|
592
|
+
variable_set_id uuid,
|
|
593
|
+
variable_name text,
|
|
594
|
+
variable_version integer,
|
|
595
|
+
value_encrypted text,
|
|
596
|
+
authority_scope text,
|
|
597
|
+
variable_set_generation bigint
|
|
598
|
+
)
|
|
599
|
+
LANGUAGE plpgsql
|
|
600
|
+
SECURITY DEFINER
|
|
601
|
+
SET search_path = %1$I, pg_catalog
|
|
602
|
+
AS $body$
|
|
603
|
+
DECLARE
|
|
604
|
+
actor_membership_id uuid;
|
|
605
|
+
variable_set_row workspace_variable_sets%%ROWTYPE;
|
|
606
|
+
resolved_count integer;
|
|
607
|
+
attempt_authority_epoch integer;
|
|
608
|
+
attempt_authority_visibility text;
|
|
609
|
+
attempt_owner_membership uuid;
|
|
610
|
+
audit_subject text := coalesce(
|
|
611
|
+
nullif(pg_catalog.current_setting('opengeni.initiating_human_subject_id', true), ''),
|
|
612
|
+
nullif(pg_catalog.current_setting('opengeni.subject_id', true), '')
|
|
613
|
+
);
|
|
614
|
+
BEGIN
|
|
615
|
+
IF (p_variable_set_id IS NULL) = (p_variable_set_name IS NULL)
|
|
616
|
+
OR p_actor_kind NOT IN ('subject', 'agent_attempt')
|
|
617
|
+
THEN
|
|
618
|
+
RAISE EXCEPTION 'invalid exact variable-set read request' USING ERRCODE = '22023';
|
|
619
|
+
END IF;
|
|
620
|
+
INSERT INTO opengeni_private.variable_set_authority_capabilities (
|
|
621
|
+
backend_pid, transaction_id, capability_kind
|
|
622
|
+
) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'materialize')
|
|
623
|
+
ON CONFLICT DO NOTHING;
|
|
624
|
+
|
|
625
|
+
IF p_actor_kind = 'subject' THEN
|
|
626
|
+
actor_membership_id := scoped_variable_set_actor_membership(
|
|
627
|
+
p_account_id, p_workspace_id
|
|
628
|
+
);
|
|
629
|
+
ELSE
|
|
630
|
+
audit_subject := assert_scoped_variable_set_attempt(
|
|
631
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id, p_attempt_id,
|
|
632
|
+
p_execution_generation
|
|
633
|
+
);
|
|
634
|
+
-- Attribution evidence (0280): the exact attempt the assert above
|
|
635
|
+
-- already validated owns the frozen session tenancy authority.
|
|
636
|
+
SELECT attempt.authority_epoch, attempt.authority_visibility,
|
|
637
|
+
attempt.authority_owner_organization_membership_id
|
|
638
|
+
INTO attempt_authority_epoch, attempt_authority_visibility,
|
|
639
|
+
attempt_owner_membership
|
|
640
|
+
FROM session_turn_attempts attempt
|
|
641
|
+
WHERE attempt.id = p_attempt_id
|
|
642
|
+
AND attempt.account_id = p_account_id
|
|
643
|
+
AND attempt.workspace_id = p_workspace_id;
|
|
644
|
+
END IF;
|
|
645
|
+
|
|
646
|
+
SELECT variable_set.* INTO STRICT variable_set_row
|
|
647
|
+
FROM workspace_variable_sets variable_set
|
|
648
|
+
WHERE variable_set.account_id = p_account_id
|
|
649
|
+
AND variable_set.status = 'active'
|
|
650
|
+
AND (
|
|
651
|
+
(p_variable_set_id IS NOT NULL AND variable_set.id = p_variable_set_id)
|
|
652
|
+
OR (p_variable_set_name IS NOT NULL AND variable_set.name = p_variable_set_name)
|
|
653
|
+
)
|
|
654
|
+
AND (
|
|
655
|
+
variable_set.authority_scope = 'organization'
|
|
656
|
+
OR (
|
|
657
|
+
variable_set.authority_scope = 'workspace'
|
|
658
|
+
AND variable_set.workspace_id = p_workspace_id
|
|
659
|
+
)
|
|
660
|
+
OR (
|
|
661
|
+
variable_set.authority_scope = 'user'
|
|
662
|
+
AND (
|
|
663
|
+
(p_actor_kind = 'subject'
|
|
664
|
+
AND variable_set.owner_organization_membership_id = actor_membership_id)
|
|
665
|
+
OR p_actor_kind = 'agent_attempt'
|
|
666
|
+
)
|
|
667
|
+
)
|
|
668
|
+
)
|
|
669
|
+
ORDER BY CASE variable_set.authority_scope
|
|
670
|
+
WHEN 'user' THEN 1 WHEN 'workspace' THEN 2 ELSE 3 END
|
|
671
|
+
LIMIT 1
|
|
672
|
+
FOR SHARE;
|
|
673
|
+
|
|
674
|
+
IF p_actor_kind = 'agent_attempt' THEN
|
|
675
|
+
PERFORM 1 FROM sessions session_value
|
|
676
|
+
LEFT JOIN rig_versions rig_version
|
|
677
|
+
ON rig_version.id = session_value.rig_version_id
|
|
678
|
+
AND rig_version.rig_id = session_value.rig_id
|
|
679
|
+
AND rig_version.account_id = session_value.account_id
|
|
680
|
+
WHERE session_value.id = p_session_id
|
|
681
|
+
AND session_value.account_id = p_account_id
|
|
682
|
+
AND session_value.workspace_id = p_workspace_id
|
|
683
|
+
AND (
|
|
684
|
+
session_value.variable_set_id = variable_set_row.id
|
|
685
|
+
OR coalesce(rig_version.default_variable_set_ids, '[]'::jsonb)
|
|
686
|
+
? variable_set_row.id::text
|
|
687
|
+
);
|
|
688
|
+
IF NOT FOUND THEN
|
|
689
|
+
RAISE EXCEPTION 'exact variable-set resource was not admitted for this attempt'
|
|
690
|
+
USING ERRCODE = '42501';
|
|
691
|
+
END IF;
|
|
692
|
+
IF variable_set_row.authority_scope = 'user' THEN
|
|
693
|
+
SELECT count(*)::integer INTO resolved_count
|
|
694
|
+
FROM resolve_session_attempt_personal_resources(
|
|
695
|
+
p_account_id, p_workspace_id, p_attempt_id
|
|
696
|
+
) resolved
|
|
697
|
+
WHERE resolved.resource_kind = 'variable_set'
|
|
698
|
+
AND resolved.resource_id = variable_set_row.id
|
|
699
|
+
AND resolved.authority_id = variable_set_row.authority_id
|
|
700
|
+
AND resolved.authority_generation = variable_set_row.generation;
|
|
701
|
+
IF resolved_count <> 1 THEN
|
|
702
|
+
RAISE EXCEPTION 'personal variable-set grant is not exact or current'
|
|
703
|
+
USING ERRCODE = '42501';
|
|
704
|
+
END IF;
|
|
705
|
+
END IF;
|
|
706
|
+
END IF;
|
|
707
|
+
|
|
708
|
+
RETURN QUERY
|
|
709
|
+
SELECT variable_set_row.id, variable.name, variable.version,
|
|
710
|
+
variable.value_encrypted, variable_set_row.authority_scope,
|
|
711
|
+
variable_set_row.generation
|
|
712
|
+
FROM workspace_variable_set_variables variable
|
|
713
|
+
WHERE variable.account_id = p_account_id
|
|
714
|
+
AND variable.variable_set_id = variable_set_row.id
|
|
715
|
+
AND variable.name = p_variable_name;
|
|
716
|
+
IF FOUND THEN
|
|
717
|
+
INSERT INTO audit_events (
|
|
718
|
+
account_id, workspace_id, subject_id, action, target_type, target_id,
|
|
719
|
+
metadata, metadata_codec_version
|
|
720
|
+
) VALUES (
|
|
721
|
+
p_account_id, p_workspace_id, audit_subject,
|
|
722
|
+
'variable_set.variable.read', 'workspace_variable_set',
|
|
723
|
+
variable_set_row.id::text,
|
|
724
|
+
pg_catalog.jsonb_build_object(
|
|
725
|
+
'variableSetId', variable_set_row.id,
|
|
726
|
+
'name', p_variable_name,
|
|
727
|
+
'actorKind', p_actor_kind,
|
|
728
|
+
'scope', variable_set_row.authority_scope,
|
|
729
|
+
'generation', variable_set_row.generation,
|
|
730
|
+
'sessionId', p_session_id,
|
|
731
|
+
'turnId', p_turn_id,
|
|
732
|
+
'attemptId', p_attempt_id,
|
|
733
|
+
'executionGeneration', p_execution_generation,
|
|
734
|
+
'actorMembershipId', actor_membership_id,
|
|
735
|
+
'authorityEpoch', attempt_authority_epoch,
|
|
736
|
+
'authorityVisibility', attempt_authority_visibility,
|
|
737
|
+
'authorityOwnerOrganizationMembershipId', attempt_owner_membership,
|
|
738
|
+
'ownerAuthorityId', variable_set_row.authority_id,
|
|
739
|
+
'ownerOrganizationMembershipId', variable_set_row.owner_organization_membership_id,
|
|
740
|
+
'originWorkspaceId', variable_set_row.origin_workspace_id
|
|
741
|
+
),
|
|
742
|
+
1
|
|
743
|
+
);
|
|
744
|
+
END IF;
|
|
745
|
+
DELETE FROM opengeni_private.variable_set_authority_capabilities
|
|
746
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
747
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
748
|
+
AND capability_kind = 'materialize';
|
|
749
|
+
RETURN;
|
|
750
|
+
EXCEPTION WHEN OTHERS THEN
|
|
751
|
+
DELETE FROM opengeni_private.variable_set_authority_capabilities
|
|
752
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
753
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
754
|
+
AND capability_kind = 'materialize';
|
|
755
|
+
RAISE;
|
|
756
|
+
END
|
|
757
|
+
$body$;
|
|
758
|
+
$ddl$, data_schema);
|
|
759
|
+
END
|
|
760
|
+
$secret_read_attribution$;
|
|
761
|
+
|
|
762
|
+
REVOKE ALL ON FUNCTION read_scoped_variable_set_secret(
|
|
763
|
+
uuid, uuid, uuid, text, text, text, uuid, uuid, uuid, integer
|
|
764
|
+
) FROM PUBLIC;
|
|
765
|
+
DO $secret_read_attribution_grant$
|
|
766
|
+
BEGIN
|
|
767
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
768
|
+
GRANT EXECUTE ON FUNCTION read_scoped_variable_set_secret(
|
|
769
|
+
uuid, uuid, uuid, text, text, text, uuid, uuid, uuid, integer
|
|
770
|
+
) TO opengeni_app;
|
|
771
|
+
END IF;
|
|
772
|
+
END
|
|
773
|
+
$secret_read_attribution_grant$;
|