@opengeni/db 1.0.1 → 3.1.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.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 +1595 -81
- package/dist/index.js +21571 -8126
- 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 +22 -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 +41 -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 +153 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +1 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/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/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-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 +11651 -1548
- 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 +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +877 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1291 -76
- package/src/schema.ts +3303 -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 +437 -84
- package/src/session-realtime-context.ts +3 -3
- package/src/session-realtime-ledger.ts +22 -11
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +17 -1
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-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-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.js.map} +0 -0
|
@@ -0,0 +1,1374 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Migration 0306: bind personal Variable Set/Rig issuance to one accepted
|
|
3
|
+
-- logical turn. Old workers select/consume `once` at attempt claim and cannot
|
|
4
|
+
-- safely recover that turn, so every API/control/turn worker must be stopped
|
|
5
|
+
-- before this cutover and no pre-0306 worker may restart afterwards.
|
|
6
|
+
|
|
7
|
+
SET LOCAL lock_timeout = '5s';
|
|
8
|
+
SET LOCAL statement_timeout = '10min';
|
|
9
|
+
|
|
10
|
+
DO $atomic_personal_resource_writer_drain_before_lock$
|
|
11
|
+
DECLARE
|
|
12
|
+
configured_roles_text text := nullif(
|
|
13
|
+
current_setting('opengeni.migration_application_roles', true), ''
|
|
14
|
+
);
|
|
15
|
+
configured_roles jsonb;
|
|
16
|
+
BEGIN
|
|
17
|
+
IF configured_roles_text IS NULL THEN
|
|
18
|
+
RAISE EXCEPTION
|
|
19
|
+
'0306 atomic personal-resource activation requires an explicit application database role list'
|
|
20
|
+
USING ERRCODE = '55000';
|
|
21
|
+
END IF;
|
|
22
|
+
BEGIN
|
|
23
|
+
configured_roles := configured_roles_text::jsonb;
|
|
24
|
+
EXCEPTION WHEN OTHERS THEN
|
|
25
|
+
RAISE EXCEPTION
|
|
26
|
+
'0306 atomic personal-resource activation received a malformed application database role list'
|
|
27
|
+
USING ERRCODE = '55000';
|
|
28
|
+
END;
|
|
29
|
+
IF jsonb_typeof(configured_roles) <> 'array'
|
|
30
|
+
OR jsonb_array_length(configured_roles) NOT BETWEEN 1 AND 16
|
|
31
|
+
OR EXISTS (
|
|
32
|
+
SELECT 1 FROM jsonb_array_elements(configured_roles) AS roles(value)
|
|
33
|
+
WHERE jsonb_typeof(value) <> 'string'
|
|
34
|
+
OR btrim(value #>> '{}') = ''
|
|
35
|
+
OR octet_length(value #>> '{}') > 63
|
|
36
|
+
)
|
|
37
|
+
OR (
|
|
38
|
+
SELECT count(*) FROM jsonb_array_elements_text(configured_roles)
|
|
39
|
+
) <> (
|
|
40
|
+
SELECT count(DISTINCT value)
|
|
41
|
+
FROM jsonb_array_elements_text(configured_roles) AS roles(value)
|
|
42
|
+
)
|
|
43
|
+
THEN
|
|
44
|
+
RAISE EXCEPTION
|
|
45
|
+
'0306 atomic personal-resource activation received an invalid application database role list'
|
|
46
|
+
USING ERRCODE = '55000';
|
|
47
|
+
END IF;
|
|
48
|
+
IF EXISTS (
|
|
49
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
50
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
51
|
+
ON roles.role_name = activity.usename
|
|
52
|
+
WHERE activity.datname = current_database()
|
|
53
|
+
AND activity.pid <> pg_backend_pid()
|
|
54
|
+
)
|
|
55
|
+
THEN
|
|
56
|
+
RAISE EXCEPTION
|
|
57
|
+
'0306 atomic personal-resource activation requires all configured OpenGeni application database sessions to be stopped'
|
|
58
|
+
USING ERRCODE = '55000';
|
|
59
|
+
END IF;
|
|
60
|
+
END
|
|
61
|
+
$atomic_personal_resource_writer_drain_before_lock$;
|
|
62
|
+
|
|
63
|
+
LOCK TABLE sessions IN ACCESS EXCLUSIVE MODE;
|
|
64
|
+
LOCK TABLE session_turns IN ACCESS EXCLUSIVE MODE;
|
|
65
|
+
LOCK TABLE session_turn_attempts IN ACCESS EXCLUSIVE MODE;
|
|
66
|
+
LOCK TABLE organization_user_resource_grants IN ACCESS EXCLUSIVE MODE;
|
|
67
|
+
|
|
68
|
+
DO $atomic_personal_resource_writer_drain_after_lock$
|
|
69
|
+
DECLARE
|
|
70
|
+
configured_roles jsonb := current_setting(
|
|
71
|
+
'opengeni.migration_application_roles', false
|
|
72
|
+
)::jsonb;
|
|
73
|
+
BEGIN
|
|
74
|
+
IF EXISTS (
|
|
75
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
76
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
77
|
+
ON roles.role_name = activity.usename
|
|
78
|
+
WHERE activity.datname = current_database()
|
|
79
|
+
AND activity.pid <> pg_backend_pid()
|
|
80
|
+
)
|
|
81
|
+
THEN
|
|
82
|
+
RAISE EXCEPTION
|
|
83
|
+
'0306 atomic personal-resource activation requires all configured OpenGeni application database sessions to be stopped'
|
|
84
|
+
USING ERRCODE = '55000';
|
|
85
|
+
END IF;
|
|
86
|
+
END
|
|
87
|
+
$atomic_personal_resource_writer_drain_after_lock$;
|
|
88
|
+
|
|
89
|
+
-- The production migrator is a NOSUPERUSER/NOBYPASSRLS table owner, so FORCE
|
|
90
|
+
-- RLS would otherwise make this global drained-work guard see an empty set.
|
|
91
|
+
-- These five tables are read-only inside this exact transaction-local window;
|
|
92
|
+
-- later mutation begins only after FORCE has been restored on every table.
|
|
93
|
+
ALTER TABLE sessions NO FORCE ROW LEVEL SECURITY;
|
|
94
|
+
ALTER TABLE session_turns NO FORCE ROW LEVEL SECURITY;
|
|
95
|
+
ALTER TABLE rigs NO FORCE ROW LEVEL SECURITY;
|
|
96
|
+
ALTER TABLE rig_versions NO FORCE ROW LEVEL SECURITY;
|
|
97
|
+
ALTER TABLE workspace_variable_sets NO FORCE ROW LEVEL SECURITY;
|
|
98
|
+
|
|
99
|
+
DO $atomic_personal_resource_work_drain$
|
|
100
|
+
BEGIN
|
|
101
|
+
IF EXISTS (
|
|
102
|
+
SELECT 1
|
|
103
|
+
FROM session_turns turn_value
|
|
104
|
+
JOIN sessions session_value
|
|
105
|
+
ON session_value.id = turn_value.session_id
|
|
106
|
+
AND session_value.account_id = turn_value.account_id
|
|
107
|
+
AND session_value.workspace_id = turn_value.workspace_id
|
|
108
|
+
WHERE turn_value.status IN (
|
|
109
|
+
'queued', 'running', 'requires_action', 'recovering', 'waiting_capacity'
|
|
110
|
+
)
|
|
111
|
+
AND (
|
|
112
|
+
EXISTS (
|
|
113
|
+
SELECT 1 FROM workspace_variable_sets variable_set
|
|
114
|
+
WHERE variable_set.id = session_value.variable_set_id
|
|
115
|
+
AND variable_set.authority_scope = 'user'
|
|
116
|
+
)
|
|
117
|
+
OR EXISTS (
|
|
118
|
+
SELECT 1 FROM rigs rig
|
|
119
|
+
WHERE rig.id = session_value.rig_id AND rig.authority_scope = 'user'
|
|
120
|
+
)
|
|
121
|
+
OR EXISTS (
|
|
122
|
+
SELECT 1
|
|
123
|
+
FROM rig_versions version_value
|
|
124
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
125
|
+
version_value.default_variable_set_ids
|
|
126
|
+
) default_id(value)
|
|
127
|
+
JOIN workspace_variable_sets variable_set
|
|
128
|
+
ON variable_set.id = default_id.value::uuid
|
|
129
|
+
AND variable_set.authority_scope = 'user'
|
|
130
|
+
WHERE version_value.id = session_value.rig_version_id
|
|
131
|
+
AND version_value.rig_id = session_value.rig_id
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
) THEN
|
|
135
|
+
RAISE EXCEPTION
|
|
136
|
+
'0306 requires all executable turns with personal Variable Set/Rig resources to be drained'
|
|
137
|
+
USING ERRCODE = '55000';
|
|
138
|
+
END IF;
|
|
139
|
+
END
|
|
140
|
+
$atomic_personal_resource_work_drain$;
|
|
141
|
+
|
|
142
|
+
ALTER TABLE sessions FORCE ROW LEVEL SECURITY;
|
|
143
|
+
ALTER TABLE session_turns FORCE ROW LEVEL SECURITY;
|
|
144
|
+
ALTER TABLE rigs FORCE ROW LEVEL SECURITY;
|
|
145
|
+
ALTER TABLE rig_versions FORCE ROW LEVEL SECURITY;
|
|
146
|
+
ALTER TABLE workspace_variable_sets FORCE ROW LEVEL SECURITY;
|
|
147
|
+
|
|
148
|
+
-- No active legacy once grant has an accepted logical-turn owner. Settle them
|
|
149
|
+
-- before activating the new protocol so no new turn can adopt ambiguous work.
|
|
150
|
+
ALTER TABLE organization_user_resource_grants NO FORCE ROW LEVEL SECURITY;
|
|
151
|
+
UPDATE organization_user_resource_grants
|
|
152
|
+
SET status = 'revoked', revoked_at = coalesce(revoked_at, clock_timestamp()),
|
|
153
|
+
generation = generation + 1, updated_at = clock_timestamp()
|
|
154
|
+
WHERE status = 'active' AND mode = 'once';
|
|
155
|
+
ALTER TABLE organization_user_resource_grants FORCE ROW LEVEL SECURITY;
|
|
156
|
+
|
|
157
|
+
ALTER TABLE sessions ADD COLUMN "initial_personal_resource_attachment_intent" jsonb;
|
|
158
|
+
ALTER TABLE session_turns
|
|
159
|
+
ADD COLUMN "personal_resource_attachment_summary" jsonb,
|
|
160
|
+
ADD COLUMN "personal_resource_protocol_version" integer NOT NULL DEFAULT 0;
|
|
161
|
+
ALTER TABLE session_turn_attempts
|
|
162
|
+
ADD COLUMN "personal_resource_protocol_version" integer NOT NULL DEFAULT 0;
|
|
163
|
+
|
|
164
|
+
ALTER TABLE session_turns ADD CONSTRAINT "session_turns_personal_resource_protocol_chk"
|
|
165
|
+
CHECK (personal_resource_protocol_version IN (0, 1));
|
|
166
|
+
ALTER TABLE session_turn_attempts
|
|
167
|
+
ADD CONSTRAINT "session_turn_attempts_personal_resource_protocol_chk"
|
|
168
|
+
CHECK (personal_resource_protocol_version IN (0, 1));
|
|
169
|
+
|
|
170
|
+
CREATE TABLE turn_personal_resource_attachment_receipts (
|
|
171
|
+
turn_id uuid PRIMARY KEY REFERENCES session_turns(id) ON DELETE CASCADE,
|
|
172
|
+
account_id uuid NOT NULL,
|
|
173
|
+
workspace_id uuid NOT NULL,
|
|
174
|
+
session_id uuid NOT NULL,
|
|
175
|
+
initiating_human_subject_id text NOT NULL,
|
|
176
|
+
owner_organization_membership_id uuid NOT NULL,
|
|
177
|
+
membership_authorization_revision bigint NOT NULL,
|
|
178
|
+
session_visibility text NOT NULL,
|
|
179
|
+
session_authority_epoch integer NOT NULL,
|
|
180
|
+
grant_mode text NOT NULL,
|
|
181
|
+
shared_output_warning_version integer NOT NULL,
|
|
182
|
+
shared_output_acknowledged boolean NOT NULL,
|
|
183
|
+
request_digest bytea NOT NULL,
|
|
184
|
+
resource_count integer NOT NULL,
|
|
185
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
186
|
+
CONSTRAINT turn_personal_resource_attachment_receipts_identity_chk CHECK (
|
|
187
|
+
octet_length(initiating_human_subject_id) BETWEEN 1 AND 512
|
|
188
|
+
AND membership_authorization_revision > 0
|
|
189
|
+
AND session_authority_epoch > 0
|
|
190
|
+
AND grant_mode IN ('once', 'session', 'always')
|
|
191
|
+
AND session_visibility IN ('user_private', 'workspace_shared')
|
|
192
|
+
AND shared_output_warning_version = 1
|
|
193
|
+
AND resource_count BETWEEN 1 AND 27
|
|
194
|
+
AND (session_visibility <> 'workspace_shared' OR shared_output_acknowledged IS TRUE)
|
|
195
|
+
),
|
|
196
|
+
CONSTRAINT turn_personal_resource_attachment_receipts_workspace_turn_fk
|
|
197
|
+
FOREIGN KEY (workspace_id, turn_id)
|
|
198
|
+
REFERENCES session_turns(workspace_id, id) ON DELETE CASCADE
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
CREATE TABLE turn_personal_resource_snapshots (
|
|
202
|
+
turn_id uuid NOT NULL REFERENCES turn_personal_resource_attachment_receipts(turn_id)
|
|
203
|
+
ON DELETE CASCADE,
|
|
204
|
+
account_id uuid NOT NULL,
|
|
205
|
+
workspace_id uuid NOT NULL,
|
|
206
|
+
session_id uuid NOT NULL,
|
|
207
|
+
resource_kind text NOT NULL,
|
|
208
|
+
resource_id uuid NOT NULL,
|
|
209
|
+
resource_version_id uuid,
|
|
210
|
+
selection_sources text[] NOT NULL,
|
|
211
|
+
action text NOT NULL,
|
|
212
|
+
origin_workspace_id uuid NOT NULL,
|
|
213
|
+
owner_organization_membership_id uuid NOT NULL,
|
|
214
|
+
membership_authorization_revision bigint NOT NULL,
|
|
215
|
+
authority_id uuid NOT NULL,
|
|
216
|
+
authority_generation bigint NOT NULL,
|
|
217
|
+
grant_id uuid NOT NULL,
|
|
218
|
+
grant_generation bigint NOT NULL,
|
|
219
|
+
grant_mode text NOT NULL,
|
|
220
|
+
grant_context text NOT NULL,
|
|
221
|
+
grant_session_id uuid,
|
|
222
|
+
grant_authority_epoch integer,
|
|
223
|
+
canonical_delegation jsonb NOT NULL,
|
|
224
|
+
snapshot_digest bytea NOT NULL,
|
|
225
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
226
|
+
PRIMARY KEY (turn_id, resource_kind, resource_id),
|
|
227
|
+
CONSTRAINT turn_personal_resource_snapshots_kind_chk CHECK (
|
|
228
|
+
(resource_kind = 'variable_set' AND action = 'variable_set.use'
|
|
229
|
+
AND resource_version_id IS NULL)
|
|
230
|
+
OR (resource_kind = 'rig' AND action = 'rig.use'
|
|
231
|
+
AND resource_version_id IS NOT NULL)
|
|
232
|
+
),
|
|
233
|
+
CONSTRAINT turn_personal_resource_snapshots_generation_chk CHECK (
|
|
234
|
+
membership_authorization_revision > 0
|
|
235
|
+
AND authority_generation > 0 AND grant_generation > 0
|
|
236
|
+
AND cardinality(selection_sources) BETWEEN 1 AND 26
|
|
237
|
+
),
|
|
238
|
+
CONSTRAINT turn_personal_resource_snapshots_grant_chk CHECK (
|
|
239
|
+
grant_mode IN ('once', 'session', 'always')
|
|
240
|
+
AND grant_context IN ('user_private', 'workspace_shared')
|
|
241
|
+
AND ((grant_mode = 'always' AND grant_session_id IS NULL
|
|
242
|
+
AND grant_authority_epoch IS NULL)
|
|
243
|
+
OR (grant_mode IN ('once', 'session') AND grant_session_id = session_id
|
|
244
|
+
AND grant_authority_epoch > 0))
|
|
245
|
+
)
|
|
246
|
+
);
|
|
247
|
+
CREATE INDEX turn_personal_resource_snapshots_authority_idx
|
|
248
|
+
ON turn_personal_resource_snapshots(authority_id);
|
|
249
|
+
CREATE INDEX turn_personal_resource_snapshots_grant_idx
|
|
250
|
+
ON turn_personal_resource_snapshots(grant_id);
|
|
251
|
+
|
|
252
|
+
CREATE TABLE turn_personal_resource_once_receipts (
|
|
253
|
+
grant_id uuid PRIMARY KEY,
|
|
254
|
+
turn_id uuid NOT NULL REFERENCES turn_personal_resource_attachment_receipts(turn_id)
|
|
255
|
+
ON DELETE CASCADE,
|
|
256
|
+
account_id uuid NOT NULL,
|
|
257
|
+
authority_id uuid NOT NULL,
|
|
258
|
+
authority_generation bigint NOT NULL,
|
|
259
|
+
grant_generation bigint NOT NULL,
|
|
260
|
+
consumed_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
261
|
+
CONSTRAINT turn_personal_resource_once_receipts_generation_chk CHECK (
|
|
262
|
+
authority_generation > 0 AND grant_generation > 0
|
|
263
|
+
)
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
ALTER TABLE turn_personal_resource_attachment_receipts ENABLE ROW LEVEL SECURITY;
|
|
267
|
+
ALTER TABLE turn_personal_resource_attachment_receipts FORCE ROW LEVEL SECURITY;
|
|
268
|
+
ALTER TABLE turn_personal_resource_snapshots ENABLE ROW LEVEL SECURITY;
|
|
269
|
+
ALTER TABLE turn_personal_resource_snapshots FORCE ROW LEVEL SECURITY;
|
|
270
|
+
ALTER TABLE turn_personal_resource_once_receipts ENABLE ROW LEVEL SECURITY;
|
|
271
|
+
ALTER TABLE turn_personal_resource_once_receipts FORCE ROW LEVEL SECURITY;
|
|
272
|
+
|
|
273
|
+
CREATE POLICY turn_personal_resource_attachment_internal
|
|
274
|
+
ON turn_personal_resource_attachment_receipts
|
|
275
|
+
USING (
|
|
276
|
+
current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
277
|
+
= 'personal_resource_grant_management'
|
|
278
|
+
OR EXISTS (
|
|
279
|
+
SELECT 1 FROM opengeni_private.personal_resource_delegation_capabilities capability
|
|
280
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
281
|
+
AND capability.transaction_id = pg_current_xact_id_if_assigned()
|
|
282
|
+
AND capability.capability_kind IN ('admit', 'resolve')
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
WITH CHECK (
|
|
286
|
+
current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
287
|
+
= 'personal_resource_grant_management'
|
|
288
|
+
OR EXISTS (
|
|
289
|
+
SELECT 1 FROM opengeni_private.personal_resource_delegation_capabilities capability
|
|
290
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
291
|
+
AND capability.transaction_id = pg_current_xact_id_if_assigned()
|
|
292
|
+
AND capability.capability_kind = 'admit'
|
|
293
|
+
)
|
|
294
|
+
);
|
|
295
|
+
CREATE POLICY turn_personal_resource_snapshot_internal
|
|
296
|
+
ON turn_personal_resource_snapshots
|
|
297
|
+
USING (
|
|
298
|
+
current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
299
|
+
= 'personal_resource_grant_management'
|
|
300
|
+
OR EXISTS (
|
|
301
|
+
SELECT 1 FROM opengeni_private.personal_resource_delegation_capabilities capability
|
|
302
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
303
|
+
AND capability.transaction_id = pg_current_xact_id_if_assigned()
|
|
304
|
+
AND capability.capability_kind IN ('admit', 'resolve')
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
WITH CHECK (
|
|
308
|
+
current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
309
|
+
= 'personal_resource_grant_management'
|
|
310
|
+
);
|
|
311
|
+
CREATE POLICY turn_personal_resource_once_internal
|
|
312
|
+
ON turn_personal_resource_once_receipts
|
|
313
|
+
USING (
|
|
314
|
+
current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
315
|
+
= 'personal_resource_grant_management'
|
|
316
|
+
OR EXISTS (
|
|
317
|
+
SELECT 1 FROM opengeni_private.personal_resource_delegation_capabilities capability
|
|
318
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
319
|
+
AND capability.transaction_id = pg_current_xact_id_if_assigned()
|
|
320
|
+
AND capability.capability_kind IN ('admit', 'resolve')
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
WITH CHECK (
|
|
324
|
+
current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
325
|
+
= 'personal_resource_grant_management'
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
CREATE FUNCTION digest_turn_personal_resource_snapshot(
|
|
329
|
+
p_snapshot turn_personal_resource_snapshots
|
|
330
|
+
) RETURNS bytea
|
|
331
|
+
LANGUAGE sql
|
|
332
|
+
IMMUTABLE
|
|
333
|
+
PARALLEL SAFE
|
|
334
|
+
SET search_path FROM CURRENT
|
|
335
|
+
AS $body$
|
|
336
|
+
SELECT digest(convert_to(
|
|
337
|
+
(to_jsonb(p_snapshot) - 'snapshot_digest' - 'created_at')::text,
|
|
338
|
+
'UTF8'
|
|
339
|
+
), 'sha256')
|
|
340
|
+
$body$;
|
|
341
|
+
|
|
342
|
+
CREATE FUNCTION stamp_turn_personal_resource_snapshot_digest()
|
|
343
|
+
RETURNS trigger
|
|
344
|
+
LANGUAGE plpgsql
|
|
345
|
+
SECURITY DEFINER
|
|
346
|
+
SET search_path FROM CURRENT
|
|
347
|
+
AS $body$
|
|
348
|
+
BEGIN
|
|
349
|
+
NEW.snapshot_digest := digest_turn_personal_resource_snapshot(NEW);
|
|
350
|
+
RETURN NEW;
|
|
351
|
+
END
|
|
352
|
+
$body$;
|
|
353
|
+
|
|
354
|
+
CREATE TRIGGER turn_personal_resource_snapshots_digest
|
|
355
|
+
BEFORE INSERT ON turn_personal_resource_snapshots
|
|
356
|
+
FOR EACH ROW EXECUTE FUNCTION stamp_turn_personal_resource_snapshot_digest();
|
|
357
|
+
|
|
358
|
+
CREATE FUNCTION reject_turn_personal_resource_snapshot_update()
|
|
359
|
+
RETURNS trigger
|
|
360
|
+
LANGUAGE plpgsql
|
|
361
|
+
SECURITY DEFINER
|
|
362
|
+
SET search_path FROM CURRENT
|
|
363
|
+
AS $body$
|
|
364
|
+
BEGIN
|
|
365
|
+
RAISE EXCEPTION 'accepted turn personal-resource evidence is immutable'
|
|
366
|
+
USING ERRCODE = '42501';
|
|
367
|
+
END
|
|
368
|
+
$body$;
|
|
369
|
+
|
|
370
|
+
CREATE TRIGGER turn_personal_resource_attachment_receipts_immutable
|
|
371
|
+
BEFORE UPDATE ON turn_personal_resource_attachment_receipts
|
|
372
|
+
FOR EACH ROW EXECUTE FUNCTION reject_turn_personal_resource_snapshot_update();
|
|
373
|
+
CREATE TRIGGER turn_personal_resource_snapshots_immutable
|
|
374
|
+
BEFORE UPDATE ON turn_personal_resource_snapshots
|
|
375
|
+
FOR EACH ROW EXECUTE FUNCTION reject_turn_personal_resource_snapshot_update();
|
|
376
|
+
CREATE TRIGGER turn_personal_resource_once_receipts_immutable
|
|
377
|
+
BEFORE UPDATE ON turn_personal_resource_once_receipts
|
|
378
|
+
FOR EACH ROW EXECUTE FUNCTION reject_turn_personal_resource_snapshot_update();
|
|
379
|
+
|
|
380
|
+
CREATE FUNCTION fence_turn_personal_resource_protocol_update()
|
|
381
|
+
RETURNS trigger
|
|
382
|
+
LANGUAGE plpgsql
|
|
383
|
+
SECURITY DEFINER
|
|
384
|
+
SET search_path FROM CURRENT
|
|
385
|
+
AS $body$
|
|
386
|
+
BEGIN
|
|
387
|
+
IF OLD.personal_resource_protocol_version = 1 AND (
|
|
388
|
+
NEW.personal_resource_protocol_version IS DISTINCT FROM OLD.personal_resource_protocol_version
|
|
389
|
+
OR NEW.personal_resource_attachment_summary
|
|
390
|
+
IS DISTINCT FROM OLD.personal_resource_attachment_summary
|
|
391
|
+
) THEN
|
|
392
|
+
RAISE EXCEPTION 'accepted turn personal-resource binding is immutable'
|
|
393
|
+
USING ERRCODE = '42501';
|
|
394
|
+
END IF;
|
|
395
|
+
RETURN NEW;
|
|
396
|
+
END
|
|
397
|
+
$body$;
|
|
398
|
+
|
|
399
|
+
CREATE TRIGGER session_turns_personal_resource_binding_immutable
|
|
400
|
+
BEFORE UPDATE ON session_turns
|
|
401
|
+
FOR EACH ROW EXECUTE FUNCTION fence_turn_personal_resource_protocol_update();
|
|
402
|
+
|
|
403
|
+
CREATE FUNCTION fence_attempt_personal_resource_protocol_update()
|
|
404
|
+
RETURNS trigger
|
|
405
|
+
LANGUAGE plpgsql
|
|
406
|
+
SECURITY DEFINER
|
|
407
|
+
SET search_path FROM CURRENT
|
|
408
|
+
AS $body$
|
|
409
|
+
BEGIN
|
|
410
|
+
IF NEW.personal_resource_protocol_version
|
|
411
|
+
IS DISTINCT FROM OLD.personal_resource_protocol_version
|
|
412
|
+
THEN
|
|
413
|
+
RAISE EXCEPTION 'attempt personal-resource protocol version is immutable'
|
|
414
|
+
USING ERRCODE = '42501';
|
|
415
|
+
END IF;
|
|
416
|
+
RETURN NEW;
|
|
417
|
+
END
|
|
418
|
+
$body$;
|
|
419
|
+
|
|
420
|
+
CREATE TRIGGER session_turn_attempts_personal_resource_protocol_immutable
|
|
421
|
+
BEFORE UPDATE ON session_turn_attempts
|
|
422
|
+
FOR EACH ROW EXECUTE FUNCTION fence_attempt_personal_resource_protocol_update();
|
|
423
|
+
|
|
424
|
+
CREATE FUNCTION fence_session_initial_personal_resource_intent_update()
|
|
425
|
+
RETURNS trigger
|
|
426
|
+
LANGUAGE plpgsql
|
|
427
|
+
SECURITY DEFINER
|
|
428
|
+
SET search_path FROM CURRENT
|
|
429
|
+
AS $body$
|
|
430
|
+
BEGIN
|
|
431
|
+
IF NEW.initial_personal_resource_attachment_intent
|
|
432
|
+
IS DISTINCT FROM OLD.initial_personal_resource_attachment_intent
|
|
433
|
+
THEN
|
|
434
|
+
RAISE EXCEPTION 'initial personal-resource attachment intent is immutable'
|
|
435
|
+
USING ERRCODE = '42501';
|
|
436
|
+
END IF;
|
|
437
|
+
RETURN NEW;
|
|
438
|
+
END
|
|
439
|
+
$body$;
|
|
440
|
+
|
|
441
|
+
CREATE TRIGGER sessions_initial_personal_resource_intent_immutable
|
|
442
|
+
BEFORE UPDATE ON sessions
|
|
443
|
+
FOR EACH ROW EXECUTE FUNCTION fence_session_initial_personal_resource_intent_update();
|
|
444
|
+
|
|
445
|
+
REVOKE ALL ON turn_personal_resource_attachment_receipts FROM PUBLIC;
|
|
446
|
+
REVOKE ALL ON turn_personal_resource_snapshots FROM PUBLIC;
|
|
447
|
+
REVOKE ALL ON turn_personal_resource_once_receipts FROM PUBLIC;
|
|
448
|
+
REVOKE ALL ON FUNCTION digest_turn_personal_resource_snapshot(
|
|
449
|
+
turn_personal_resource_snapshots
|
|
450
|
+
) FROM PUBLIC;
|
|
451
|
+
REVOKE ALL ON FUNCTION stamp_turn_personal_resource_snapshot_digest() FROM PUBLIC;
|
|
452
|
+
REVOKE ALL ON FUNCTION reject_turn_personal_resource_snapshot_update() FROM PUBLIC;
|
|
453
|
+
REVOKE ALL ON FUNCTION fence_turn_personal_resource_protocol_update() FROM PUBLIC;
|
|
454
|
+
REVOKE ALL ON FUNCTION fence_attempt_personal_resource_protocol_update() FROM PUBLIC;
|
|
455
|
+
REVOKE ALL ON FUNCTION fence_session_initial_personal_resource_intent_update() FROM PUBLIC;
|
|
456
|
+
|
|
457
|
+
CREATE OR REPLACE FUNCTION accept_turn_personal_resource_attachment(
|
|
458
|
+
p_account_id uuid,
|
|
459
|
+
p_workspace_id uuid,
|
|
460
|
+
p_session_id uuid,
|
|
461
|
+
p_turn_id uuid,
|
|
462
|
+
p_mode text,
|
|
463
|
+
p_expected_authority_epoch integer,
|
|
464
|
+
p_workspace_shared_acknowledged boolean,
|
|
465
|
+
p_shared_output_warning_version integer
|
|
466
|
+
) RETURNS TABLE (
|
|
467
|
+
grant_mode text,
|
|
468
|
+
grant_context text,
|
|
469
|
+
resource_count integer,
|
|
470
|
+
resource_kinds text[],
|
|
471
|
+
shared_output_warning_version integer,
|
|
472
|
+
replay boolean
|
|
473
|
+
)
|
|
474
|
+
LANGUAGE plpgsql
|
|
475
|
+
SECURITY DEFINER
|
|
476
|
+
SET search_path FROM CURRENT
|
|
477
|
+
AS $body$
|
|
478
|
+
#variable_conflict use_column
|
|
479
|
+
DECLARE
|
|
480
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
481
|
+
session_row sessions%ROWTYPE;
|
|
482
|
+
turn_row session_turns%ROWTYPE;
|
|
483
|
+
member_row organization_memberships%ROWTYPE;
|
|
484
|
+
resource_row record;
|
|
485
|
+
authority_row organization_user_resource_authorities%ROWTYPE;
|
|
486
|
+
grant_row organization_user_resource_grants%ROWTYPE;
|
|
487
|
+
existing_receipt turn_personal_resource_attachment_receipts%ROWTYPE;
|
|
488
|
+
canonical jsonb;
|
|
489
|
+
request_hash bytea;
|
|
490
|
+
selected_resources jsonb := '[]'::jsonb;
|
|
491
|
+
selected_count integer := 0;
|
|
492
|
+
unauthorized_count integer := 0;
|
|
493
|
+
kinds text[] := ARRAY[]::text[];
|
|
494
|
+
BEGIN
|
|
495
|
+
PERFORM set_config(
|
|
496
|
+
'opengeni.organization_tenancy_lifecycle', 'personal_resource_grant_management', true
|
|
497
|
+
);
|
|
498
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
499
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
500
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
501
|
+
)::uuid
|
|
502
|
+
OR caller_subject IS NULL
|
|
503
|
+
OR p_mode IS NULL OR p_mode NOT IN ('once', 'session', 'always')
|
|
504
|
+
OR p_expected_authority_epoch IS NULL OR p_expected_authority_epoch <= 0
|
|
505
|
+
OR p_shared_output_warning_version IS DISTINCT FROM 1
|
|
506
|
+
THEN
|
|
507
|
+
RAISE EXCEPTION 'invalid atomic personal-resource attachment request'
|
|
508
|
+
USING ERRCODE = '22023';
|
|
509
|
+
END IF;
|
|
510
|
+
IF NOT EXISTS (
|
|
511
|
+
SELECT 1 FROM session_tenancy_activations activation
|
|
512
|
+
WHERE activation.account_id = p_account_id AND activation.activation_version = 1
|
|
513
|
+
) THEN
|
|
514
|
+
RAISE EXCEPTION 'session tenancy product is not activated' USING ERRCODE = '42501';
|
|
515
|
+
END IF;
|
|
516
|
+
|
|
517
|
+
PERFORM 1 FROM workspace_inference_controls control_row
|
|
518
|
+
WHERE control_row.account_id = p_account_id AND control_row.workspace_id = p_workspace_id
|
|
519
|
+
FOR SHARE;
|
|
520
|
+
PERFORM 1 FROM workspaces workspace_value
|
|
521
|
+
WHERE workspace_value.account_id = p_account_id AND workspace_value.id = p_workspace_id
|
|
522
|
+
FOR KEY SHARE;
|
|
523
|
+
SELECT session_value.* INTO STRICT session_row
|
|
524
|
+
FROM sessions session_value
|
|
525
|
+
WHERE session_value.id = p_session_id
|
|
526
|
+
AND session_value.account_id = p_account_id
|
|
527
|
+
AND session_value.workspace_id = p_workspace_id
|
|
528
|
+
FOR NO KEY UPDATE;
|
|
529
|
+
SELECT turn_value.* INTO STRICT turn_row
|
|
530
|
+
FROM session_turns turn_value
|
|
531
|
+
WHERE turn_value.id = p_turn_id
|
|
532
|
+
AND turn_value.account_id = p_account_id
|
|
533
|
+
AND turn_value.workspace_id = p_workspace_id
|
|
534
|
+
AND turn_value.session_id = p_session_id
|
|
535
|
+
FOR UPDATE;
|
|
536
|
+
|
|
537
|
+
IF coalesce(nullif(btrim(turn_row.initiating_human_subject_id), ''),
|
|
538
|
+
CASE WHEN turn_row.initiator_kind = 'subject'
|
|
539
|
+
THEN nullif(btrim(turn_row.initiator_subject_id), '') END)
|
|
540
|
+
IS DISTINCT FROM caller_subject
|
|
541
|
+
OR session_row.authority_epoch IS DISTINCT FROM p_expected_authority_epoch
|
|
542
|
+
THEN
|
|
543
|
+
RAISE EXCEPTION 'atomic personal-resource attachment is not exact accepted work'
|
|
544
|
+
USING ERRCODE = '42501';
|
|
545
|
+
END IF;
|
|
546
|
+
IF session_row.visibility = 'workspace_shared'
|
|
547
|
+
AND p_workspace_shared_acknowledged IS NOT TRUE
|
|
548
|
+
THEN
|
|
549
|
+
RAISE EXCEPTION 'workspace-shared personal-resource attachment requires warning acknowledgement'
|
|
550
|
+
USING ERRCODE = '42501';
|
|
551
|
+
END IF;
|
|
552
|
+
|
|
553
|
+
request_hash := digest(convert_to(jsonb_build_object(
|
|
554
|
+
'accountId', p_account_id, 'workspaceId', p_workspace_id,
|
|
555
|
+
'sessionId', p_session_id, 'turnId', p_turn_id, 'mode', p_mode,
|
|
556
|
+
'authorityEpoch', p_expected_authority_epoch,
|
|
557
|
+
'sessionVisibility', session_row.visibility,
|
|
558
|
+
'workspaceSharedAcknowledged', p_workspace_shared_acknowledged IS TRUE,
|
|
559
|
+
'sharedOutputWarningVersion', p_shared_output_warning_version
|
|
560
|
+
)::text, 'UTF8'), 'sha256');
|
|
561
|
+
|
|
562
|
+
SELECT receipt.* INTO existing_receipt
|
|
563
|
+
FROM turn_personal_resource_attachment_receipts receipt
|
|
564
|
+
WHERE receipt.turn_id = p_turn_id;
|
|
565
|
+
IF FOUND THEN
|
|
566
|
+
IF existing_receipt.request_digest IS DISTINCT FROM request_hash THEN
|
|
567
|
+
RAISE EXCEPTION 'atomic personal-resource attachment replay changed its request'
|
|
568
|
+
USING ERRCODE = '23505';
|
|
569
|
+
END IF;
|
|
570
|
+
grant_mode := existing_receipt.grant_mode;
|
|
571
|
+
grant_context := existing_receipt.session_visibility;
|
|
572
|
+
resource_count := existing_receipt.resource_count;
|
|
573
|
+
SELECT array_agg(DISTINCT snapshot.resource_kind ORDER BY snapshot.resource_kind)
|
|
574
|
+
INTO resource_kinds
|
|
575
|
+
FROM turn_personal_resource_snapshots snapshot
|
|
576
|
+
WHERE snapshot.turn_id = p_turn_id;
|
|
577
|
+
shared_output_warning_version := existing_receipt.shared_output_warning_version;
|
|
578
|
+
replay := true;
|
|
579
|
+
RETURN NEXT;
|
|
580
|
+
RETURN;
|
|
581
|
+
END IF;
|
|
582
|
+
|
|
583
|
+
IF session_row.status = 'cancelled'
|
|
584
|
+
OR turn_row.status <> 'queued'
|
|
585
|
+
OR turn_row.active_attempt_id IS NOT NULL
|
|
586
|
+
THEN
|
|
587
|
+
RAISE EXCEPTION 'personal-resource issuance requires new queued accepted work'
|
|
588
|
+
USING ERRCODE = '42501';
|
|
589
|
+
END IF;
|
|
590
|
+
|
|
591
|
+
SELECT membership.* INTO STRICT member_row
|
|
592
|
+
FROM organization_memberships membership
|
|
593
|
+
WHERE membership.account_id = p_account_id
|
|
594
|
+
AND membership.subject_id = caller_subject
|
|
595
|
+
AND membership.status = 'active' AND membership.revoked_at IS NULL
|
|
596
|
+
FOR SHARE;
|
|
597
|
+
IF member_row.personal_workspace_id IS DISTINCT FROM p_workspace_id
|
|
598
|
+
AND NOT EXISTS (
|
|
599
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
600
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
601
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
602
|
+
AND workspace_membership.subject_id = caller_subject
|
|
603
|
+
)
|
|
604
|
+
THEN
|
|
605
|
+
RAISE EXCEPTION 'owner lacks target-workspace access' USING ERRCODE = '42501';
|
|
606
|
+
END IF;
|
|
607
|
+
|
|
608
|
+
-- Freeze every row that can change the server-derived closure before
|
|
609
|
+
-- evaluating it. The closure is materialized once below and reused for grant
|
|
610
|
+
-- issuance, so a concurrent resource edit cannot create a mixed snapshot.
|
|
611
|
+
PERFORM 1 FROM rigs rig
|
|
612
|
+
WHERE rig.id = session_row.rig_id AND rig.account_id = p_account_id
|
|
613
|
+
FOR SHARE;
|
|
614
|
+
PERFORM 1 FROM rig_versions version_value
|
|
615
|
+
WHERE version_value.id = session_row.rig_version_id
|
|
616
|
+
AND version_value.rig_id = session_row.rig_id
|
|
617
|
+
AND version_value.account_id = p_account_id
|
|
618
|
+
FOR SHARE;
|
|
619
|
+
PERFORM 1 FROM workspace_variable_sets variable_set
|
|
620
|
+
WHERE variable_set.id = session_row.variable_set_id
|
|
621
|
+
AND variable_set.account_id = p_account_id
|
|
622
|
+
FOR SHARE;
|
|
623
|
+
PERFORM variable_set.id
|
|
624
|
+
FROM rig_versions version_value
|
|
625
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(version_value.default_variable_set_ids)
|
|
626
|
+
default_id(value)
|
|
627
|
+
JOIN workspace_variable_sets variable_set
|
|
628
|
+
ON variable_set.id = default_id.value::uuid
|
|
629
|
+
AND variable_set.account_id = p_account_id
|
|
630
|
+
WHERE version_value.id = session_row.rig_version_id
|
|
631
|
+
AND version_value.rig_id = session_row.rig_id
|
|
632
|
+
AND version_value.account_id = p_account_id
|
|
633
|
+
ORDER BY variable_set.id
|
|
634
|
+
FOR SHARE OF variable_set;
|
|
635
|
+
|
|
636
|
+
WITH selected AS (
|
|
637
|
+
SELECT 'variable_set'::text AS resource_kind, variable_set.id AS resource_id,
|
|
638
|
+
NULL::uuid AS resource_version_id, 'variable_set.use'::text AS action,
|
|
639
|
+
'session_variable_set'::text AS selection_source,
|
|
640
|
+
variable_set.origin_workspace_id, variable_set.authority_id,
|
|
641
|
+
variable_set.owner_organization_membership_id
|
|
642
|
+
FROM workspace_variable_sets variable_set
|
|
643
|
+
WHERE variable_set.id = session_row.variable_set_id
|
|
644
|
+
AND variable_set.account_id = p_account_id
|
|
645
|
+
AND variable_set.authority_scope = 'user'
|
|
646
|
+
UNION ALL
|
|
647
|
+
SELECT 'rig', rig.id, session_row.rig_version_id, 'rig.use', 'session_rig',
|
|
648
|
+
rig.origin_workspace_id, rig.authority_id, rig.owner_organization_membership_id
|
|
649
|
+
FROM rigs rig
|
|
650
|
+
WHERE rig.id = session_row.rig_id
|
|
651
|
+
AND rig.account_id = p_account_id AND rig.authority_scope = 'user'
|
|
652
|
+
UNION ALL
|
|
653
|
+
SELECT 'variable_set', variable_set.id, NULL::uuid, 'variable_set.use',
|
|
654
|
+
'rig_default_variable_set:' || default_id.ordinality::text,
|
|
655
|
+
variable_set.origin_workspace_id, variable_set.authority_id,
|
|
656
|
+
variable_set.owner_organization_membership_id
|
|
657
|
+
FROM rig_versions version_value
|
|
658
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(version_value.default_variable_set_ids)
|
|
659
|
+
WITH ORDINALITY default_id(value, ordinality)
|
|
660
|
+
JOIN workspace_variable_sets variable_set
|
|
661
|
+
ON variable_set.id = default_id.value::uuid
|
|
662
|
+
AND variable_set.account_id = p_account_id
|
|
663
|
+
AND variable_set.authority_scope = 'user'
|
|
664
|
+
WHERE version_value.id = session_row.rig_version_id
|
|
665
|
+
AND version_value.rig_id = session_row.rig_id
|
|
666
|
+
AND version_value.account_id = p_account_id
|
|
667
|
+
), grouped AS (
|
|
668
|
+
SELECT resource_kind, resource_id,
|
|
669
|
+
min(resource_version_id::text)::uuid AS resource_version_id,
|
|
670
|
+
action, array_agg(selection_source ORDER BY selection_source) AS selection_sources,
|
|
671
|
+
min(origin_workspace_id::text)::uuid AS origin_workspace_id,
|
|
672
|
+
min(authority_id::text)::uuid AS authority_id,
|
|
673
|
+
min(owner_organization_membership_id::text)::uuid
|
|
674
|
+
AS owner_organization_membership_id
|
|
675
|
+
FROM selected
|
|
676
|
+
GROUP BY resource_kind, resource_id, action
|
|
677
|
+
)
|
|
678
|
+
SELECT count(*)::integer,
|
|
679
|
+
array_agg(DISTINCT resource_kind ORDER BY resource_kind),
|
|
680
|
+
count(*) FILTER (
|
|
681
|
+
WHERE owner_organization_membership_id IS DISTINCT FROM member_row.id
|
|
682
|
+
)::integer,
|
|
683
|
+
coalesce(jsonb_agg(jsonb_build_object(
|
|
684
|
+
'resource_kind', resource_kind,
|
|
685
|
+
'resource_id', resource_id,
|
|
686
|
+
'resource_version_id', resource_version_id,
|
|
687
|
+
'action', action,
|
|
688
|
+
'selection_sources', selection_sources,
|
|
689
|
+
'origin_workspace_id', origin_workspace_id,
|
|
690
|
+
'authority_id', authority_id,
|
|
691
|
+
'owner_organization_membership_id', owner_organization_membership_id
|
|
692
|
+
) ORDER BY resource_kind, resource_id), '[]'::jsonb)
|
|
693
|
+
INTO selected_count, kinds, unauthorized_count, selected_resources
|
|
694
|
+
FROM grouped;
|
|
695
|
+
IF selected_count = 0 THEN
|
|
696
|
+
RAISE EXCEPTION 'no personal Variable Set or Rig is selected by this session'
|
|
697
|
+
USING ERRCODE = '22023';
|
|
698
|
+
END IF;
|
|
699
|
+
IF selected_count > 27 THEN
|
|
700
|
+
RAISE EXCEPTION 'personal Variable Set/Rig closure exceeds the accepted-work bound'
|
|
701
|
+
USING ERRCODE = '22023';
|
|
702
|
+
END IF;
|
|
703
|
+
IF unauthorized_count <> 0 THEN
|
|
704
|
+
RAISE EXCEPTION 'a nonowner cannot attach another user''s personal resource'
|
|
705
|
+
USING ERRCODE = '42501';
|
|
706
|
+
END IF;
|
|
707
|
+
|
|
708
|
+
INSERT INTO turn_personal_resource_attachment_receipts (
|
|
709
|
+
turn_id, account_id, workspace_id, session_id,
|
|
710
|
+
initiating_human_subject_id, owner_organization_membership_id,
|
|
711
|
+
membership_authorization_revision, session_visibility,
|
|
712
|
+
session_authority_epoch, grant_mode, shared_output_warning_version,
|
|
713
|
+
shared_output_acknowledged, request_digest, resource_count
|
|
714
|
+
) VALUES (
|
|
715
|
+
p_turn_id, p_account_id, p_workspace_id, p_session_id, caller_subject,
|
|
716
|
+
member_row.id, member_row.authorization_revision, session_row.visibility,
|
|
717
|
+
session_row.authority_epoch, p_mode, p_shared_output_warning_version,
|
|
718
|
+
p_workspace_shared_acknowledged IS TRUE, request_hash, selected_count
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
FOR resource_row IN
|
|
722
|
+
SELECT * FROM jsonb_to_recordset(selected_resources) AS selected(
|
|
723
|
+
resource_kind text,
|
|
724
|
+
resource_id uuid,
|
|
725
|
+
resource_version_id uuid,
|
|
726
|
+
action text,
|
|
727
|
+
selection_sources text[],
|
|
728
|
+
origin_workspace_id uuid,
|
|
729
|
+
authority_id uuid,
|
|
730
|
+
owner_organization_membership_id uuid
|
|
731
|
+
)
|
|
732
|
+
ORDER BY resource_kind, resource_id
|
|
733
|
+
LOOP
|
|
734
|
+
SELECT authority.* INTO STRICT authority_row
|
|
735
|
+
FROM organization_user_resource_authorities authority
|
|
736
|
+
WHERE authority.id = resource_row.authority_id
|
|
737
|
+
AND authority.account_id = p_account_id
|
|
738
|
+
AND authority.organization_membership_id = member_row.id
|
|
739
|
+
AND authority.resource_kind = resource_row.resource_kind
|
|
740
|
+
AND authority.resource_id = resource_row.resource_id
|
|
741
|
+
AND authority.status = 'active' AND authority.revoked_at IS NULL
|
|
742
|
+
FOR UPDATE;
|
|
743
|
+
|
|
744
|
+
UPDATE organization_user_resource_grants grant_value
|
|
745
|
+
SET status = 'expired', updated_at = clock_timestamp()
|
|
746
|
+
WHERE grant_value.account_id = p_account_id
|
|
747
|
+
AND grant_value.authority_id = authority_row.id
|
|
748
|
+
AND grant_value.status = 'active'
|
|
749
|
+
AND grant_value.expires_at IS NOT NULL
|
|
750
|
+
AND grant_value.expires_at <= clock_timestamp();
|
|
751
|
+
|
|
752
|
+
INSERT INTO organization_user_resource_grants (
|
|
753
|
+
account_id, authority_id, owner_organization_membership_id, workspace_id,
|
|
754
|
+
session_id, action, mode, context, authority_epoch, generation, status
|
|
755
|
+
) VALUES (
|
|
756
|
+
p_account_id, authority_row.id, member_row.id, p_workspace_id,
|
|
757
|
+
CASE WHEN p_mode = 'always' THEN NULL ELSE p_session_id END,
|
|
758
|
+
resource_row.action, p_mode, session_row.visibility,
|
|
759
|
+
CASE WHEN p_mode = 'always' THEN NULL ELSE session_row.authority_epoch END,
|
|
760
|
+
1, 'active'
|
|
761
|
+
)
|
|
762
|
+
ON CONFLICT (account_id, authority_id, workspace_id, action, mode, context,
|
|
763
|
+
(coalesce(session_id, '00000000-0000-0000-0000-000000000000'::uuid)),
|
|
764
|
+
(coalesce(authority_epoch, 0))) WHERE status = 'active'
|
|
765
|
+
DO UPDATE SET updated_at = organization_user_resource_grants.updated_at
|
|
766
|
+
RETURNING * INTO grant_row;
|
|
767
|
+
|
|
768
|
+
canonical := jsonb_build_object(
|
|
769
|
+
'authorityId', authority_row.id, 'grantId', grant_row.id,
|
|
770
|
+
'organizationId', p_account_id, 'workspaceId', p_workspace_id,
|
|
771
|
+
'sessionId', grant_row.session_id, 'action', resource_row.action,
|
|
772
|
+
'mode', grant_row.mode, 'context', grant_row.context,
|
|
773
|
+
'authorityEpoch', grant_row.authority_epoch,
|
|
774
|
+
'authorityGeneration', authority_row.generation,
|
|
775
|
+
'grantGeneration', grant_row.generation,
|
|
776
|
+
'resourceVersionId', resource_row.resource_version_id
|
|
777
|
+
);
|
|
778
|
+
canonical := canonical - CASE WHEN resource_row.resource_version_id IS NULL
|
|
779
|
+
THEN 'resourceVersionId' ELSE '__not_present__' END;
|
|
780
|
+
|
|
781
|
+
INSERT INTO turn_personal_resource_snapshots (
|
|
782
|
+
turn_id, account_id, workspace_id, session_id, resource_kind, resource_id,
|
|
783
|
+
resource_version_id, selection_sources, action, origin_workspace_id,
|
|
784
|
+
owner_organization_membership_id, membership_authorization_revision,
|
|
785
|
+
authority_id, authority_generation, grant_id, grant_generation, grant_mode,
|
|
786
|
+
grant_context, grant_session_id, grant_authority_epoch,
|
|
787
|
+
canonical_delegation, snapshot_digest
|
|
788
|
+
) VALUES (
|
|
789
|
+
p_turn_id, p_account_id, p_workspace_id, p_session_id,
|
|
790
|
+
resource_row.resource_kind, resource_row.resource_id,
|
|
791
|
+
resource_row.resource_version_id, resource_row.selection_sources,
|
|
792
|
+
resource_row.action, resource_row.origin_workspace_id, member_row.id,
|
|
793
|
+
member_row.authorization_revision, authority_row.id, authority_row.generation,
|
|
794
|
+
grant_row.id, grant_row.generation, grant_row.mode, grant_row.context,
|
|
795
|
+
grant_row.session_id, grant_row.authority_epoch, canonical,
|
|
796
|
+
decode('', 'hex')
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
IF p_mode = 'once' THEN
|
|
800
|
+
UPDATE organization_user_resource_grants
|
|
801
|
+
SET status = 'consumed', updated_at = clock_timestamp()
|
|
802
|
+
WHERE id = grant_row.id AND account_id = p_account_id
|
|
803
|
+
AND generation = grant_row.generation AND status = 'active';
|
|
804
|
+
IF NOT FOUND THEN
|
|
805
|
+
RAISE EXCEPTION 'once grant lost its accepted-turn race' USING ERRCODE = '40001';
|
|
806
|
+
END IF;
|
|
807
|
+
INSERT INTO turn_personal_resource_once_receipts (
|
|
808
|
+
grant_id, turn_id, account_id, authority_id,
|
|
809
|
+
authority_generation, grant_generation
|
|
810
|
+
) VALUES (
|
|
811
|
+
grant_row.id, p_turn_id, p_account_id, authority_row.id,
|
|
812
|
+
authority_row.generation, grant_row.generation
|
|
813
|
+
);
|
|
814
|
+
END IF;
|
|
815
|
+
END LOOP;
|
|
816
|
+
|
|
817
|
+
UPDATE session_turns
|
|
818
|
+
SET personal_resource_attachment_summary = jsonb_build_object(
|
|
819
|
+
'mode', p_mode, 'context', session_row.visibility,
|
|
820
|
+
'resourceCount', selected_count, 'resourceKinds', to_jsonb(kinds),
|
|
821
|
+
'sharedOutputWarningVersion', p_shared_output_warning_version
|
|
822
|
+
),
|
|
823
|
+
personal_resource_protocol_version = 1,
|
|
824
|
+
updated_at = clock_timestamp()
|
|
825
|
+
WHERE id = p_turn_id AND account_id = p_account_id AND workspace_id = p_workspace_id;
|
|
826
|
+
|
|
827
|
+
INSERT INTO audit_events (
|
|
828
|
+
account_id, workspace_id, subject_id, action, target_type, target_id,
|
|
829
|
+
metadata, metadata_codec_version
|
|
830
|
+
) VALUES (
|
|
831
|
+
p_account_id, p_workspace_id, caller_subject,
|
|
832
|
+
'session.personal_resources.attach', 'session_turn', p_turn_id::text,
|
|
833
|
+
jsonb_build_object(
|
|
834
|
+
'sessionId', p_session_id, 'turnId', p_turn_id, 'mode', p_mode,
|
|
835
|
+
'context', session_row.visibility, 'authorityEpoch', session_row.authority_epoch,
|
|
836
|
+
'sharedOutputWarningVersion', p_shared_output_warning_version,
|
|
837
|
+
'sharedOutputAcknowledged', p_workspace_shared_acknowledged IS TRUE,
|
|
838
|
+
'resourceCount', selected_count, 'resourceKinds', to_jsonb(kinds)
|
|
839
|
+
), 1
|
|
840
|
+
);
|
|
841
|
+
|
|
842
|
+
grant_mode := p_mode;
|
|
843
|
+
grant_context := session_row.visibility;
|
|
844
|
+
resource_count := selected_count;
|
|
845
|
+
resource_kinds := kinds;
|
|
846
|
+
shared_output_warning_version := p_shared_output_warning_version;
|
|
847
|
+
replay := false;
|
|
848
|
+
RETURN NEXT;
|
|
849
|
+
END
|
|
850
|
+
$body$;
|
|
851
|
+
|
|
852
|
+
REVOKE ALL ON FUNCTION accept_turn_personal_resource_attachment(
|
|
853
|
+
uuid, uuid, uuid, uuid, text, integer, boolean, integer
|
|
854
|
+
) FROM PUBLIC;
|
|
855
|
+
|
|
856
|
+
-- Keep legacy attempt selection only for version-0 turns. In particular it no
|
|
857
|
+
-- longer has any path that can consume an unbound active once grant.
|
|
858
|
+
DO $legacy_personal_resource_trigger_fence$
|
|
859
|
+
DECLARE data_schema text := current_schema();
|
|
860
|
+
BEGIN
|
|
861
|
+
EXECUTE format(
|
|
862
|
+
'DROP TRIGGER IF EXISTS session_attempt_personal_resource_admission ON %I.session_turn_attempts',
|
|
863
|
+
data_schema
|
|
864
|
+
);
|
|
865
|
+
EXECUTE format(
|
|
866
|
+
'CREATE TRIGGER session_attempt_personal_resource_admission_legacy '
|
|
867
|
+
|| 'AFTER INSERT ON %I.session_turn_attempts FOR EACH ROW '
|
|
868
|
+
|| 'WHEN (NEW.personal_resource_protocol_version = 0) '
|
|
869
|
+
|| 'EXECUTE FUNCTION %I.admit_session_attempt_personal_resources()',
|
|
870
|
+
data_schema, data_schema
|
|
871
|
+
);
|
|
872
|
+
END
|
|
873
|
+
$legacy_personal_resource_trigger_fence$;
|
|
874
|
+
|
|
875
|
+
-- The v1 trigger and resolver are appended below after the legacy function is
|
|
876
|
+
-- renamed; keeping them in this migration makes the cutover indivisible.
|
|
877
|
+
|
|
878
|
+
CREATE OR REPLACE FUNCTION admit_session_attempt_personal_resources_v1()
|
|
879
|
+
RETURNS trigger
|
|
880
|
+
LANGUAGE plpgsql
|
|
881
|
+
SECURITY DEFINER
|
|
882
|
+
SET search_path FROM CURRENT
|
|
883
|
+
AS $body$
|
|
884
|
+
DECLARE
|
|
885
|
+
session_row sessions%ROWTYPE;
|
|
886
|
+
turn_row session_turns%ROWTYPE;
|
|
887
|
+
receipt_row turn_personal_resource_attachment_receipts%ROWTYPE;
|
|
888
|
+
copied_count integer;
|
|
889
|
+
invalid_count integer;
|
|
890
|
+
BEGIN
|
|
891
|
+
INSERT INTO opengeni_private.personal_resource_delegation_capabilities (
|
|
892
|
+
backend_pid, transaction_id, capability_kind
|
|
893
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'admit')
|
|
894
|
+
ON CONFLICT DO NOTHING;
|
|
895
|
+
|
|
896
|
+
SELECT session_value.* INTO STRICT session_row
|
|
897
|
+
FROM sessions session_value
|
|
898
|
+
WHERE session_value.id = NEW.session_id
|
|
899
|
+
AND session_value.account_id = NEW.account_id
|
|
900
|
+
AND session_value.workspace_id = NEW.workspace_id
|
|
901
|
+
FOR SHARE;
|
|
902
|
+
SELECT turn_value.* INTO STRICT turn_row
|
|
903
|
+
FROM session_turns turn_value
|
|
904
|
+
WHERE turn_value.id = NEW.turn_id
|
|
905
|
+
AND turn_value.account_id = NEW.account_id
|
|
906
|
+
AND turn_value.workspace_id = NEW.workspace_id
|
|
907
|
+
AND turn_value.session_id = NEW.session_id
|
|
908
|
+
FOR SHARE;
|
|
909
|
+
SELECT receipt.* INTO STRICT receipt_row
|
|
910
|
+
FROM turn_personal_resource_attachment_receipts receipt
|
|
911
|
+
WHERE receipt.turn_id = NEW.turn_id
|
|
912
|
+
AND receipt.account_id = NEW.account_id
|
|
913
|
+
AND receipt.workspace_id = NEW.workspace_id
|
|
914
|
+
AND receipt.session_id = NEW.session_id;
|
|
915
|
+
|
|
916
|
+
IF NEW.execution_generation <= 0
|
|
917
|
+
OR NEW.state NOT IN ('claimed', 'running')
|
|
918
|
+
OR NEW.closed_at IS NOT NULL OR NEW.quiesced_at IS NOT NULL
|
|
919
|
+
OR session_row.active_turn_id IS DISTINCT FROM NEW.turn_id
|
|
920
|
+
OR turn_row.active_attempt_id IS DISTINCT FROM NEW.id
|
|
921
|
+
OR turn_row.execution_generation IS DISTINCT FROM NEW.execution_generation
|
|
922
|
+
OR turn_row.status <> 'running'
|
|
923
|
+
OR turn_row.personal_resource_protocol_version <> 1
|
|
924
|
+
OR NEW.personal_resource_protocol_version <> 1
|
|
925
|
+
OR NEW.authority_visibility IS DISTINCT FROM session_row.visibility
|
|
926
|
+
OR NEW.authority_epoch IS DISTINCT FROM session_row.authority_epoch
|
|
927
|
+
OR NEW.authority_owner_organization_membership_id
|
|
928
|
+
IS DISTINCT FROM session_row.owner_organization_membership_id
|
|
929
|
+
OR receipt_row.session_visibility IS DISTINCT FROM session_row.visibility
|
|
930
|
+
OR receipt_row.session_authority_epoch IS DISTINCT FROM session_row.authority_epoch
|
|
931
|
+
OR receipt_row.initiating_human_subject_id IS DISTINCT FROM coalesce(
|
|
932
|
+
nullif(btrim(turn_row.initiating_human_subject_id), ''),
|
|
933
|
+
CASE WHEN turn_row.initiator_kind = 'subject'
|
|
934
|
+
THEN nullif(btrim(turn_row.initiator_subject_id), '') END
|
|
935
|
+
)
|
|
936
|
+
THEN
|
|
937
|
+
RAISE EXCEPTION 'turn-bound personal-resource admission requires the exact current attempt'
|
|
938
|
+
USING ERRCODE = '42501';
|
|
939
|
+
END IF;
|
|
940
|
+
|
|
941
|
+
SELECT count(*)::integer INTO invalid_count
|
|
942
|
+
FROM turn_personal_resource_snapshots snapshot
|
|
943
|
+
WHERE snapshot.turn_id = NEW.turn_id
|
|
944
|
+
AND NOT (
|
|
945
|
+
snapshot.account_id = NEW.account_id
|
|
946
|
+
AND snapshot.workspace_id = NEW.workspace_id
|
|
947
|
+
AND snapshot.session_id = NEW.session_id
|
|
948
|
+
AND snapshot.owner_organization_membership_id
|
|
949
|
+
= receipt_row.owner_organization_membership_id
|
|
950
|
+
AND snapshot.membership_authorization_revision
|
|
951
|
+
= receipt_row.membership_authorization_revision
|
|
952
|
+
AND snapshot.grant_context = receipt_row.session_visibility
|
|
953
|
+
AND snapshot.grant_mode = receipt_row.grant_mode
|
|
954
|
+
AND EXISTS (
|
|
955
|
+
SELECT 1 FROM organization_memberships membership
|
|
956
|
+
WHERE membership.id = snapshot.owner_organization_membership_id
|
|
957
|
+
AND membership.account_id = snapshot.account_id
|
|
958
|
+
AND membership.subject_id = receipt_row.initiating_human_subject_id
|
|
959
|
+
AND membership.status = 'active' AND membership.revoked_at IS NULL
|
|
960
|
+
AND membership.authorization_revision = snapshot.membership_authorization_revision
|
|
961
|
+
AND (membership.personal_workspace_id = snapshot.workspace_id OR EXISTS (
|
|
962
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
963
|
+
WHERE workspace_membership.account_id = membership.account_id
|
|
964
|
+
AND workspace_membership.workspace_id = snapshot.workspace_id
|
|
965
|
+
AND workspace_membership.subject_id = membership.subject_id
|
|
966
|
+
))
|
|
967
|
+
)
|
|
968
|
+
AND EXISTS (
|
|
969
|
+
SELECT 1 FROM organization_user_resource_authorities authority
|
|
970
|
+
WHERE authority.id = snapshot.authority_id
|
|
971
|
+
AND authority.account_id = snapshot.account_id
|
|
972
|
+
AND authority.organization_membership_id = snapshot.owner_organization_membership_id
|
|
973
|
+
AND authority.resource_kind = snapshot.resource_kind
|
|
974
|
+
AND authority.resource_id = snapshot.resource_id
|
|
975
|
+
AND authority.generation = snapshot.authority_generation
|
|
976
|
+
AND authority.status = 'active' AND authority.revoked_at IS NULL
|
|
977
|
+
)
|
|
978
|
+
AND EXISTS (
|
|
979
|
+
SELECT 1 FROM organization_user_resource_grants grant_value
|
|
980
|
+
WHERE grant_value.id = snapshot.grant_id
|
|
981
|
+
AND grant_value.account_id = snapshot.account_id
|
|
982
|
+
AND grant_value.authority_id = snapshot.authority_id
|
|
983
|
+
AND grant_value.owner_organization_membership_id
|
|
984
|
+
= snapshot.owner_organization_membership_id
|
|
985
|
+
AND grant_value.workspace_id = snapshot.workspace_id
|
|
986
|
+
AND grant_value.action = snapshot.action
|
|
987
|
+
AND grant_value.mode = snapshot.grant_mode
|
|
988
|
+
AND grant_value.context = snapshot.grant_context
|
|
989
|
+
AND grant_value.generation = snapshot.grant_generation
|
|
990
|
+
AND grant_value.session_id IS NOT DISTINCT FROM snapshot.grant_session_id
|
|
991
|
+
AND grant_value.authority_epoch IS NOT DISTINCT FROM snapshot.grant_authority_epoch
|
|
992
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
993
|
+
AND ((snapshot.grant_mode = 'once' AND grant_value.status = 'consumed'
|
|
994
|
+
AND EXISTS (
|
|
995
|
+
SELECT 1 FROM turn_personal_resource_once_receipts once_receipt
|
|
996
|
+
WHERE once_receipt.grant_id = snapshot.grant_id
|
|
997
|
+
AND once_receipt.turn_id = snapshot.turn_id
|
|
998
|
+
AND once_receipt.account_id = snapshot.account_id
|
|
999
|
+
AND once_receipt.authority_id = snapshot.authority_id
|
|
1000
|
+
AND once_receipt.authority_generation = snapshot.authority_generation
|
|
1001
|
+
AND once_receipt.grant_generation = snapshot.grant_generation
|
|
1002
|
+
)) OR (snapshot.grant_mode IN ('session', 'always')
|
|
1003
|
+
AND grant_value.status = 'active'))
|
|
1004
|
+
)
|
|
1005
|
+
AND snapshot.snapshot_digest = digest_turn_personal_resource_snapshot(snapshot)
|
|
1006
|
+
);
|
|
1007
|
+
IF invalid_count <> 0 THEN
|
|
1008
|
+
RAISE EXCEPTION 'turn-bound personal-resource snapshot is no longer live'
|
|
1009
|
+
USING ERRCODE = '42501';
|
|
1010
|
+
END IF;
|
|
1011
|
+
|
|
1012
|
+
INSERT INTO session_attempt_personal_resource_admissions (
|
|
1013
|
+
attempt_id, account_id, workspace_id, session_id, turn_id,
|
|
1014
|
+
execution_generation, initiating_human_subject_id,
|
|
1015
|
+
owner_organization_membership_id, membership_authorization_revision,
|
|
1016
|
+
session_visibility, session_authority_epoch, resource_count
|
|
1017
|
+
) VALUES (
|
|
1018
|
+
NEW.id, NEW.account_id, NEW.workspace_id, NEW.session_id, NEW.turn_id,
|
|
1019
|
+
NEW.execution_generation, receipt_row.initiating_human_subject_id,
|
|
1020
|
+
receipt_row.owner_organization_membership_id,
|
|
1021
|
+
receipt_row.membership_authorization_revision,
|
|
1022
|
+
receipt_row.session_visibility, receipt_row.session_authority_epoch,
|
|
1023
|
+
receipt_row.resource_count
|
|
1024
|
+
);
|
|
1025
|
+
|
|
1026
|
+
INSERT INTO session_attempt_personal_resource_snapshots (
|
|
1027
|
+
attempt_id, account_id, workspace_id, session_id, turn_id,
|
|
1028
|
+
execution_generation, resource_kind, resource_id, resource_version_id,
|
|
1029
|
+
selection_sources, action, origin_workspace_id,
|
|
1030
|
+
owner_organization_membership_id, membership_authorization_revision,
|
|
1031
|
+
authority_id, authority_generation, target_workspace_id,
|
|
1032
|
+
session_visibility, session_authority_epoch, grant_id, grant_generation,
|
|
1033
|
+
grant_mode, grant_context, grant_session_id, grant_authority_epoch
|
|
1034
|
+
)
|
|
1035
|
+
SELECT NEW.id, snapshot.account_id, snapshot.workspace_id, snapshot.session_id,
|
|
1036
|
+
snapshot.turn_id, NEW.execution_generation, snapshot.resource_kind,
|
|
1037
|
+
snapshot.resource_id, snapshot.resource_version_id, snapshot.selection_sources,
|
|
1038
|
+
snapshot.action, snapshot.origin_workspace_id,
|
|
1039
|
+
snapshot.owner_organization_membership_id,
|
|
1040
|
+
snapshot.membership_authorization_revision, snapshot.authority_id,
|
|
1041
|
+
snapshot.authority_generation, snapshot.workspace_id,
|
|
1042
|
+
receipt_row.session_visibility, receipt_row.session_authority_epoch,
|
|
1043
|
+
snapshot.grant_id, snapshot.grant_generation, snapshot.grant_mode,
|
|
1044
|
+
snapshot.grant_context, snapshot.grant_session_id, snapshot.grant_authority_epoch
|
|
1045
|
+
FROM turn_personal_resource_snapshots snapshot
|
|
1046
|
+
WHERE snapshot.turn_id = NEW.turn_id
|
|
1047
|
+
ORDER BY snapshot.resource_kind, snapshot.resource_id;
|
|
1048
|
+
GET DIAGNOSTICS copied_count = ROW_COUNT;
|
|
1049
|
+
IF copied_count <> receipt_row.resource_count THEN
|
|
1050
|
+
RAISE EXCEPTION 'turn-bound personal-resource snapshot collection is incomplete'
|
|
1051
|
+
USING ERRCODE = '42501';
|
|
1052
|
+
END IF;
|
|
1053
|
+
|
|
1054
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
1055
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1056
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1057
|
+
AND capability_kind = 'admit';
|
|
1058
|
+
RETURN NEW;
|
|
1059
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1060
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
1061
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1062
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1063
|
+
AND capability_kind = 'admit';
|
|
1064
|
+
RAISE;
|
|
1065
|
+
END
|
|
1066
|
+
$body$;
|
|
1067
|
+
|
|
1068
|
+
DO $v1_personal_resource_trigger_install$
|
|
1069
|
+
DECLARE data_schema text := current_schema();
|
|
1070
|
+
BEGIN
|
|
1071
|
+
EXECUTE format(
|
|
1072
|
+
'CREATE TRIGGER session_attempt_personal_resource_admission_v1 '
|
|
1073
|
+
|| 'AFTER INSERT ON %I.session_turn_attempts FOR EACH ROW '
|
|
1074
|
+
|| 'WHEN (NEW.personal_resource_protocol_version = 1) '
|
|
1075
|
+
|| 'EXECUTE FUNCTION %I.admit_session_attempt_personal_resources_v1()',
|
|
1076
|
+
data_schema, data_schema
|
|
1077
|
+
);
|
|
1078
|
+
END
|
|
1079
|
+
$v1_personal_resource_trigger_install$;
|
|
1080
|
+
|
|
1081
|
+
ALTER FUNCTION resolve_session_attempt_personal_resources(uuid, uuid, uuid)
|
|
1082
|
+
RENAME TO resolve_session_attempt_personal_resources_legacy_0305;
|
|
1083
|
+
|
|
1084
|
+
CREATE FUNCTION resolve_session_attempt_personal_resources(
|
|
1085
|
+
p_account_id uuid,
|
|
1086
|
+
p_workspace_id uuid,
|
|
1087
|
+
p_attempt_id uuid
|
|
1088
|
+
) RETURNS TABLE (
|
|
1089
|
+
resource_kind text,
|
|
1090
|
+
resource_id uuid,
|
|
1091
|
+
resource_version_id uuid,
|
|
1092
|
+
selection_sources text[],
|
|
1093
|
+
action text,
|
|
1094
|
+
authority_id uuid,
|
|
1095
|
+
authority_generation bigint,
|
|
1096
|
+
grant_id uuid,
|
|
1097
|
+
grant_generation bigint,
|
|
1098
|
+
grant_mode text
|
|
1099
|
+
)
|
|
1100
|
+
LANGUAGE plpgsql
|
|
1101
|
+
SECURITY DEFINER
|
|
1102
|
+
SET search_path FROM CURRENT
|
|
1103
|
+
AS $body$
|
|
1104
|
+
#variable_conflict use_column
|
|
1105
|
+
DECLARE
|
|
1106
|
+
attempt_protocol integer;
|
|
1107
|
+
admission_row session_attempt_personal_resource_admissions%ROWTYPE;
|
|
1108
|
+
invalid_count integer;
|
|
1109
|
+
caller_subject text := coalesce(
|
|
1110
|
+
nullif(current_setting('opengeni.initiating_human_subject_id', true), ''),
|
|
1111
|
+
nullif(current_setting('opengeni.subject_id', true), '')
|
|
1112
|
+
);
|
|
1113
|
+
BEGIN
|
|
1114
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
1115
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
1116
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
1117
|
+
)::uuid
|
|
1118
|
+
THEN
|
|
1119
|
+
RAISE EXCEPTION 'personal-resource resolve scope mismatch' USING ERRCODE = '42501';
|
|
1120
|
+
END IF;
|
|
1121
|
+
SELECT attempt.personal_resource_protocol_version INTO STRICT attempt_protocol
|
|
1122
|
+
FROM session_turn_attempts attempt
|
|
1123
|
+
WHERE attempt.id = p_attempt_id
|
|
1124
|
+
AND attempt.account_id = p_account_id
|
|
1125
|
+
AND attempt.workspace_id = p_workspace_id;
|
|
1126
|
+
IF attempt_protocol = 0 THEN
|
|
1127
|
+
RETURN QUERY SELECT * FROM resolve_session_attempt_personal_resources_legacy_0305(
|
|
1128
|
+
p_account_id, p_workspace_id, p_attempt_id
|
|
1129
|
+
);
|
|
1130
|
+
RETURN;
|
|
1131
|
+
ELSIF attempt_protocol <> 1 THEN
|
|
1132
|
+
RAISE EXCEPTION 'unsupported personal-resource protocol version'
|
|
1133
|
+
USING ERRCODE = '42501';
|
|
1134
|
+
END IF;
|
|
1135
|
+
|
|
1136
|
+
INSERT INTO opengeni_private.personal_resource_delegation_capabilities (
|
|
1137
|
+
backend_pid, transaction_id, capability_kind
|
|
1138
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'resolve')
|
|
1139
|
+
ON CONFLICT DO NOTHING;
|
|
1140
|
+
SELECT admission.* INTO STRICT admission_row
|
|
1141
|
+
FROM session_attempt_personal_resource_admissions admission
|
|
1142
|
+
WHERE admission.attempt_id = p_attempt_id
|
|
1143
|
+
AND admission.account_id = p_account_id
|
|
1144
|
+
AND admission.workspace_id = p_workspace_id;
|
|
1145
|
+
IF caller_subject IS DISTINCT FROM admission_row.initiating_human_subject_id THEN
|
|
1146
|
+
RAISE EXCEPTION 'personal-resource resolve initiating human mismatch'
|
|
1147
|
+
USING ERRCODE = '42501';
|
|
1148
|
+
END IF;
|
|
1149
|
+
|
|
1150
|
+
PERFORM 1
|
|
1151
|
+
FROM session_turn_attempts attempt
|
|
1152
|
+
JOIN sessions session_value
|
|
1153
|
+
ON session_value.id = attempt.session_id
|
|
1154
|
+
AND session_value.account_id = attempt.account_id
|
|
1155
|
+
AND session_value.workspace_id = attempt.workspace_id
|
|
1156
|
+
JOIN session_turns turn_value
|
|
1157
|
+
ON turn_value.id = attempt.turn_id
|
|
1158
|
+
AND turn_value.account_id = attempt.account_id
|
|
1159
|
+
AND turn_value.workspace_id = attempt.workspace_id
|
|
1160
|
+
AND turn_value.session_id = attempt.session_id
|
|
1161
|
+
WHERE attempt.id = admission_row.attempt_id
|
|
1162
|
+
AND session_value.active_turn_id = admission_row.turn_id
|
|
1163
|
+
AND turn_value.active_attempt_id = admission_row.attempt_id
|
|
1164
|
+
AND turn_value.execution_generation = admission_row.execution_generation
|
|
1165
|
+
AND turn_value.personal_resource_protocol_version = 1
|
|
1166
|
+
AND turn_value.status = 'running'
|
|
1167
|
+
AND attempt.execution_generation = admission_row.execution_generation
|
|
1168
|
+
AND attempt.personal_resource_protocol_version = 1
|
|
1169
|
+
AND attempt.state IN ('claimed', 'running')
|
|
1170
|
+
AND attempt.closed_at IS NULL AND attempt.quiesced_at IS NULL
|
|
1171
|
+
AND attempt.authority_visibility = admission_row.session_visibility
|
|
1172
|
+
AND attempt.authority_epoch = admission_row.session_authority_epoch
|
|
1173
|
+
AND session_value.visibility = admission_row.session_visibility
|
|
1174
|
+
AND session_value.authority_epoch = admission_row.session_authority_epoch
|
|
1175
|
+
AND NOT EXISTS (
|
|
1176
|
+
SELECT 1 FROM session_attempt_interruptions interruption
|
|
1177
|
+
WHERE interruption.account_id = attempt.account_id
|
|
1178
|
+
AND interruption.workspace_id = attempt.workspace_id
|
|
1179
|
+
AND interruption.session_id = attempt.session_id
|
|
1180
|
+
AND interruption.attempt_id = attempt.id
|
|
1181
|
+
AND interruption.state IN ('pending', 'delivered', 'acknowledged')
|
|
1182
|
+
)
|
|
1183
|
+
FOR SHARE OF session_value, turn_value
|
|
1184
|
+
FOR UPDATE OF attempt;
|
|
1185
|
+
IF NOT FOUND THEN
|
|
1186
|
+
RAISE EXCEPTION 'personal-resource resolve requires the exact current uninterrupted attempt'
|
|
1187
|
+
USING ERRCODE = '42501';
|
|
1188
|
+
END IF;
|
|
1189
|
+
|
|
1190
|
+
SELECT count(*)::integer INTO invalid_count
|
|
1191
|
+
FROM session_attempt_personal_resource_snapshots snapshot
|
|
1192
|
+
JOIN turn_personal_resource_snapshots accepted
|
|
1193
|
+
ON accepted.turn_id = snapshot.turn_id
|
|
1194
|
+
AND accepted.resource_kind = snapshot.resource_kind
|
|
1195
|
+
AND accepted.resource_id = snapshot.resource_id
|
|
1196
|
+
WHERE snapshot.attempt_id = admission_row.attempt_id
|
|
1197
|
+
AND NOT (
|
|
1198
|
+
snapshot.account_id = accepted.account_id
|
|
1199
|
+
AND snapshot.workspace_id = accepted.workspace_id
|
|
1200
|
+
AND snapshot.session_id = accepted.session_id
|
|
1201
|
+
AND snapshot.resource_version_id IS NOT DISTINCT FROM accepted.resource_version_id
|
|
1202
|
+
AND snapshot.selection_sources = accepted.selection_sources
|
|
1203
|
+
AND snapshot.action = accepted.action
|
|
1204
|
+
AND snapshot.origin_workspace_id = accepted.origin_workspace_id
|
|
1205
|
+
AND snapshot.owner_organization_membership_id
|
|
1206
|
+
= accepted.owner_organization_membership_id
|
|
1207
|
+
AND snapshot.membership_authorization_revision
|
|
1208
|
+
= accepted.membership_authorization_revision
|
|
1209
|
+
AND snapshot.authority_id = accepted.authority_id
|
|
1210
|
+
AND snapshot.authority_generation = accepted.authority_generation
|
|
1211
|
+
AND snapshot.grant_id = accepted.grant_id
|
|
1212
|
+
AND snapshot.grant_generation = accepted.grant_generation
|
|
1213
|
+
AND snapshot.grant_mode = accepted.grant_mode
|
|
1214
|
+
AND snapshot.grant_context = accepted.grant_context
|
|
1215
|
+
AND snapshot.grant_session_id IS NOT DISTINCT FROM accepted.grant_session_id
|
|
1216
|
+
AND snapshot.grant_authority_epoch IS NOT DISTINCT FROM accepted.grant_authority_epoch
|
|
1217
|
+
AND accepted.snapshot_digest = digest_turn_personal_resource_snapshot(accepted)
|
|
1218
|
+
AND EXISTS (
|
|
1219
|
+
SELECT 1 FROM organization_memberships membership
|
|
1220
|
+
WHERE membership.id = accepted.owner_organization_membership_id
|
|
1221
|
+
AND membership.account_id = accepted.account_id
|
|
1222
|
+
AND membership.subject_id = admission_row.initiating_human_subject_id
|
|
1223
|
+
AND membership.status = 'active' AND membership.revoked_at IS NULL
|
|
1224
|
+
AND membership.authorization_revision = accepted.membership_authorization_revision
|
|
1225
|
+
AND (membership.personal_workspace_id = accepted.workspace_id OR EXISTS (
|
|
1226
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
1227
|
+
WHERE workspace_membership.account_id = membership.account_id
|
|
1228
|
+
AND workspace_membership.workspace_id = accepted.workspace_id
|
|
1229
|
+
AND workspace_membership.subject_id = membership.subject_id
|
|
1230
|
+
))
|
|
1231
|
+
)
|
|
1232
|
+
AND EXISTS (
|
|
1233
|
+
SELECT 1 FROM organization_user_resource_authorities authority
|
|
1234
|
+
WHERE authority.id = accepted.authority_id
|
|
1235
|
+
AND authority.account_id = accepted.account_id
|
|
1236
|
+
AND authority.organization_membership_id = accepted.owner_organization_membership_id
|
|
1237
|
+
AND authority.resource_kind = accepted.resource_kind
|
|
1238
|
+
AND authority.resource_id = accepted.resource_id
|
|
1239
|
+
AND authority.generation = accepted.authority_generation
|
|
1240
|
+
AND authority.status = 'active' AND authority.revoked_at IS NULL
|
|
1241
|
+
)
|
|
1242
|
+
AND EXISTS (
|
|
1243
|
+
SELECT 1 FROM organization_user_resource_grants grant_value
|
|
1244
|
+
WHERE grant_value.id = accepted.grant_id
|
|
1245
|
+
AND grant_value.account_id = accepted.account_id
|
|
1246
|
+
AND grant_value.authority_id = accepted.authority_id
|
|
1247
|
+
AND grant_value.owner_organization_membership_id
|
|
1248
|
+
= accepted.owner_organization_membership_id
|
|
1249
|
+
AND grant_value.workspace_id = accepted.workspace_id
|
|
1250
|
+
AND grant_value.action = accepted.action
|
|
1251
|
+
AND grant_value.mode = accepted.grant_mode
|
|
1252
|
+
AND grant_value.context = accepted.grant_context
|
|
1253
|
+
AND grant_value.generation = accepted.grant_generation
|
|
1254
|
+
AND grant_value.session_id IS NOT DISTINCT FROM accepted.grant_session_id
|
|
1255
|
+
AND grant_value.authority_epoch IS NOT DISTINCT FROM accepted.grant_authority_epoch
|
|
1256
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
1257
|
+
AND ((accepted.grant_mode = 'once' AND grant_value.status = 'consumed'
|
|
1258
|
+
AND EXISTS (
|
|
1259
|
+
SELECT 1 FROM turn_personal_resource_once_receipts once_receipt
|
|
1260
|
+
WHERE once_receipt.grant_id = accepted.grant_id
|
|
1261
|
+
AND once_receipt.turn_id = accepted.turn_id
|
|
1262
|
+
AND once_receipt.account_id = accepted.account_id
|
|
1263
|
+
AND once_receipt.authority_id = accepted.authority_id
|
|
1264
|
+
AND once_receipt.authority_generation = accepted.authority_generation
|
|
1265
|
+
AND once_receipt.grant_generation = accepted.grant_generation
|
|
1266
|
+
)) OR (accepted.grant_mode IN ('session', 'always')
|
|
1267
|
+
AND grant_value.status = 'active'))
|
|
1268
|
+
)
|
|
1269
|
+
);
|
|
1270
|
+
IF invalid_count <> 0 THEN
|
|
1271
|
+
RAISE EXCEPTION 'personal-resource authority snapshot is no longer live'
|
|
1272
|
+
USING ERRCODE = '42501';
|
|
1273
|
+
END IF;
|
|
1274
|
+
SELECT count(*)::integer INTO invalid_count
|
|
1275
|
+
FROM session_attempt_personal_resource_snapshots snapshot
|
|
1276
|
+
WHERE snapshot.attempt_id = admission_row.attempt_id;
|
|
1277
|
+
IF invalid_count <> admission_row.resource_count THEN
|
|
1278
|
+
RAISE EXCEPTION 'personal-resource snapshot collection is incomplete'
|
|
1279
|
+
USING ERRCODE = '42501';
|
|
1280
|
+
END IF;
|
|
1281
|
+
|
|
1282
|
+
RETURN QUERY
|
|
1283
|
+
SELECT snapshot.resource_kind, snapshot.resource_id, snapshot.resource_version_id,
|
|
1284
|
+
snapshot.selection_sources, snapshot.action, snapshot.authority_id,
|
|
1285
|
+
snapshot.authority_generation, snapshot.grant_id,
|
|
1286
|
+
snapshot.grant_generation, snapshot.grant_mode
|
|
1287
|
+
FROM session_attempt_personal_resource_snapshots snapshot
|
|
1288
|
+
WHERE snapshot.attempt_id = admission_row.attempt_id
|
|
1289
|
+
ORDER BY snapshot.resource_kind, snapshot.resource_id;
|
|
1290
|
+
|
|
1291
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
1292
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1293
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1294
|
+
AND capability_kind = 'resolve';
|
|
1295
|
+
RETURN;
|
|
1296
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1297
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
1298
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1299
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1300
|
+
AND capability_kind = 'resolve';
|
|
1301
|
+
RAISE;
|
|
1302
|
+
END
|
|
1303
|
+
$body$;
|
|
1304
|
+
|
|
1305
|
+
REVOKE ALL ON FUNCTION admit_session_attempt_personal_resources_v1() FROM PUBLIC;
|
|
1306
|
+
REVOKE ALL ON FUNCTION resolve_session_attempt_personal_resources(uuid, uuid, uuid) FROM PUBLIC;
|
|
1307
|
+
REVOKE ALL ON FUNCTION resolve_session_attempt_personal_resources_legacy_0305(
|
|
1308
|
+
uuid, uuid, uuid
|
|
1309
|
+
) FROM PUBLIC;
|
|
1310
|
+
|
|
1311
|
+
DO $atomic_personal_resource_function_hardening$
|
|
1312
|
+
DECLARE data_schema text := current_schema();
|
|
1313
|
+
BEGIN
|
|
1314
|
+
EXECUTE format(
|
|
1315
|
+
'ALTER FUNCTION %I.digest_turn_personal_resource_snapshot('
|
|
1316
|
+
|| '%I.turn_personal_resource_snapshots) '
|
|
1317
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1318
|
+
data_schema, data_schema, data_schema
|
|
1319
|
+
);
|
|
1320
|
+
EXECUTE format(
|
|
1321
|
+
'ALTER FUNCTION %I.stamp_turn_personal_resource_snapshot_digest() '
|
|
1322
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1323
|
+
data_schema, data_schema
|
|
1324
|
+
);
|
|
1325
|
+
EXECUTE format(
|
|
1326
|
+
'ALTER FUNCTION %I.reject_turn_personal_resource_snapshot_update() '
|
|
1327
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1328
|
+
data_schema, data_schema
|
|
1329
|
+
);
|
|
1330
|
+
EXECUTE format(
|
|
1331
|
+
'ALTER FUNCTION %I.fence_turn_personal_resource_protocol_update() '
|
|
1332
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1333
|
+
data_schema, data_schema
|
|
1334
|
+
);
|
|
1335
|
+
EXECUTE format(
|
|
1336
|
+
'ALTER FUNCTION %I.fence_attempt_personal_resource_protocol_update() '
|
|
1337
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1338
|
+
data_schema, data_schema
|
|
1339
|
+
);
|
|
1340
|
+
EXECUTE format(
|
|
1341
|
+
'ALTER FUNCTION %I.fence_session_initial_personal_resource_intent_update() '
|
|
1342
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1343
|
+
data_schema, data_schema
|
|
1344
|
+
);
|
|
1345
|
+
EXECUTE format(
|
|
1346
|
+
'ALTER FUNCTION %I.accept_turn_personal_resource_attachment('
|
|
1347
|
+
|| 'uuid,uuid,uuid,uuid,text,integer,boolean,integer) '
|
|
1348
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1349
|
+
data_schema, data_schema
|
|
1350
|
+
);
|
|
1351
|
+
EXECUTE format(
|
|
1352
|
+
'ALTER FUNCTION %I.admit_session_attempt_personal_resources_v1() '
|
|
1353
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1354
|
+
data_schema, data_schema
|
|
1355
|
+
);
|
|
1356
|
+
EXECUTE format(
|
|
1357
|
+
'ALTER FUNCTION %I.resolve_session_attempt_personal_resources(uuid,uuid,uuid) '
|
|
1358
|
+
|| 'SET search_path TO pg_catalog, %I, pg_temp',
|
|
1359
|
+
data_schema, data_schema
|
|
1360
|
+
);
|
|
1361
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
1362
|
+
EXECUTE format(
|
|
1363
|
+
'GRANT EXECUTE ON FUNCTION %I.accept_turn_personal_resource_attachment('
|
|
1364
|
+
|| 'uuid,uuid,uuid,uuid,text,integer,boolean,integer) TO opengeni_app',
|
|
1365
|
+
data_schema
|
|
1366
|
+
);
|
|
1367
|
+
EXECUTE format(
|
|
1368
|
+
'GRANT EXECUTE ON FUNCTION %I.resolve_session_attempt_personal_resources('
|
|
1369
|
+
|| 'uuid,uuid,uuid) TO opengeni_app',
|
|
1370
|
+
data_schema
|
|
1371
|
+
);
|
|
1372
|
+
END IF;
|
|
1373
|
+
END
|
|
1374
|
+
$atomic_personal_resource_function_hardening$;
|