@opengeni/db 0.36.1 → 3.1.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-GYO7ED2Q.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-VTPRIXKG.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-HSOWSYOX.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-5KTIEDX7.js → chunk-PR2WMLOQ.js} +7714 -4443
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-Z7VYMNPZ.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.d.ts +10 -0
- package/dist/editable-artifact-durable-export.js +18 -3
- package/dist/editable-artifact-durable-export.js.map +1 -1
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1633 -87
- package/dist/index.js +22004 -8109
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/knowledge-source-sync-schema.d.ts +730 -0
- package/dist/knowledge-source-sync.d.ts +60 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +23 -9
- package/dist/schema.d.ts +24925 -13697
- package/dist/schema.js +103 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +42 -5
- package/dist/session-realtime-ledger.d.ts +3 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +153 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +1 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0240_model_context_user_messages.sql +204 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0243_google_drive_object_acl_authority.sql +554 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-artifact-durable-export.ts +27 -0
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +12001 -1699
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/knowledge-source-sync-schema.ts +83 -0
- package/src/knowledge-source-sync.ts +530 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +931 -24
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1315 -52
- package/src/schema.ts +3336 -438
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +450 -98
- package/src/session-realtime-context.ts +19 -3
- package/src/session-realtime-ledger.ts +75 -12
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +17 -1
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-5KTIEDX7.js.map +0 -1
- package/dist/chunk-FPVAD4CO.js +0 -117
- package/dist/chunk-FPVAD4CO.js.map +0 -1
- package/dist/chunk-HSOWSYOX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-VKLB2ZGI.js +0 -2389
- package/dist/chunk-VKLB2ZGI.js.map +0 -1
- package/dist/chunk-Z7VYMNPZ.js.map +0 -1
- /package/dist/{chunk-GYO7ED2Q.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-VTPRIXKG.js.map → chunk-NJVZPWXY.js.map} +0 -0
|
@@ -0,0 +1,1555 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Activate generic owner-only user-resource grant lifecycle and
|
|
3
|
+
-- correct direct/scheduled personal-resource authority so workspace location
|
|
4
|
+
-- and origin are provenance/lifecycle facts, never ownership authority.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "organization_user_resource_grants_active_identity_uq"
|
|
10
|
+
ON "organization_user_resource_grants" (
|
|
11
|
+
"account_id", "authority_id", "workspace_id", "action", "mode", "context",
|
|
12
|
+
coalesce("session_id", '00000000-0000-0000-0000-000000000000'::uuid),
|
|
13
|
+
coalesce("authority_epoch", 0)
|
|
14
|
+
) WHERE "status" = 'active';
|
|
15
|
+
|
|
16
|
+
CREATE OR REPLACE FUNCTION list_self_user_resource_authorities(
|
|
17
|
+
p_account_id uuid
|
|
18
|
+
) RETURNS TABLE (
|
|
19
|
+
authority_id uuid, resource_kind text, authority_generation bigint,
|
|
20
|
+
authority_status text, grant_id uuid, target_workspace_id uuid,
|
|
21
|
+
target_session_id uuid, action text, grant_mode text, grant_context text,
|
|
22
|
+
grant_generation bigint, grant_status text, expires_at timestamptz
|
|
23
|
+
)
|
|
24
|
+
LANGUAGE plpgsql
|
|
25
|
+
SECURITY DEFINER
|
|
26
|
+
SET search_path = pg_catalog, pg_temp
|
|
27
|
+
AS $body$
|
|
28
|
+
#variable_conflict use_column
|
|
29
|
+
DECLARE
|
|
30
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
31
|
+
owner_membership_id uuid;
|
|
32
|
+
BEGIN
|
|
33
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
34
|
+
OR caller_subject IS NULL
|
|
35
|
+
THEN
|
|
36
|
+
RAISE EXCEPTION 'user-resource authority scope mismatch' USING ERRCODE = '42501';
|
|
37
|
+
END IF;
|
|
38
|
+
SELECT membership.id INTO STRICT owner_membership_id
|
|
39
|
+
FROM organization_memberships membership
|
|
40
|
+
WHERE membership.account_id = p_account_id
|
|
41
|
+
AND membership.subject_id = caller_subject
|
|
42
|
+
AND membership.status = 'active'
|
|
43
|
+
AND membership.revoked_at IS NULL
|
|
44
|
+
FOR SHARE;
|
|
45
|
+
|
|
46
|
+
RETURN QUERY
|
|
47
|
+
SELECT authority.id, authority.resource_kind, authority.generation, authority.status,
|
|
48
|
+
grant_value.id, grant_value.workspace_id, grant_value.session_id,
|
|
49
|
+
grant_value.action, grant_value.mode, grant_value.context,
|
|
50
|
+
grant_value.generation, grant_value.status, grant_value.expires_at
|
|
51
|
+
FROM organization_user_resource_authorities authority
|
|
52
|
+
LEFT JOIN organization_user_resource_grants grant_value
|
|
53
|
+
ON grant_value.account_id = authority.account_id
|
|
54
|
+
AND grant_value.authority_id = authority.id
|
|
55
|
+
AND grant_value.owner_organization_membership_id = owner_membership_id
|
|
56
|
+
WHERE authority.account_id = p_account_id
|
|
57
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
58
|
+
ORDER BY authority.created_at, authority.id, grant_value.created_at, grant_value.id;
|
|
59
|
+
END
|
|
60
|
+
$body$;
|
|
61
|
+
|
|
62
|
+
CREATE OR REPLACE FUNCTION issue_self_user_resource_grant(
|
|
63
|
+
p_account_id uuid,
|
|
64
|
+
p_authority_id uuid,
|
|
65
|
+
p_workspace_id uuid,
|
|
66
|
+
p_action text,
|
|
67
|
+
p_mode text,
|
|
68
|
+
p_context text,
|
|
69
|
+
p_session_id uuid DEFAULT NULL,
|
|
70
|
+
p_workspace_shared_acknowledged boolean DEFAULT false
|
|
71
|
+
) RETURNS TABLE (
|
|
72
|
+
grant_id uuid, target_workspace_id uuid, target_session_id uuid,
|
|
73
|
+
action text, grant_mode text, grant_context text, grant_generation bigint,
|
|
74
|
+
grant_status text, expires_at timestamptz
|
|
75
|
+
)
|
|
76
|
+
LANGUAGE plpgsql
|
|
77
|
+
SECURITY DEFINER
|
|
78
|
+
SET search_path = pg_catalog, pg_temp
|
|
79
|
+
AS $body$
|
|
80
|
+
#variable_conflict use_column
|
|
81
|
+
DECLARE
|
|
82
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
83
|
+
owner_membership_id uuid;
|
|
84
|
+
target_epoch integer;
|
|
85
|
+
target_visibility text;
|
|
86
|
+
normalized_action text := lower(btrim(p_action));
|
|
87
|
+
BEGIN
|
|
88
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
89
|
+
OR caller_subject IS NULL
|
|
90
|
+
THEN
|
|
91
|
+
RAISE EXCEPTION 'user-resource grant scope mismatch' USING ERRCODE = '42501';
|
|
92
|
+
END IF;
|
|
93
|
+
IF p_mode NOT IN ('once', 'session', 'always')
|
|
94
|
+
OR p_context NOT IN ('user_private', 'workspace_shared')
|
|
95
|
+
OR normalized_action = '' OR length(normalized_action) > 64
|
|
96
|
+
OR normalized_action !~ '^[a-z0-9](?:[a-z0-9._:-]*[a-z0-9])?$'
|
|
97
|
+
THEN
|
|
98
|
+
RAISE EXCEPTION 'invalid user-resource grant request' USING ERRCODE = '22023';
|
|
99
|
+
END IF;
|
|
100
|
+
IF p_context = 'workspace_shared' AND NOT p_workspace_shared_acknowledged THEN
|
|
101
|
+
RAISE EXCEPTION 'workspace_shared requires durable shared-output acknowledgement'
|
|
102
|
+
USING ERRCODE = '42501';
|
|
103
|
+
END IF;
|
|
104
|
+
IF (p_mode = 'always' AND p_session_id IS NOT NULL)
|
|
105
|
+
OR (p_mode IN ('once', 'session') AND p_session_id IS NULL)
|
|
106
|
+
THEN
|
|
107
|
+
RAISE EXCEPTION 'user-resource grant session fence is invalid' USING ERRCODE = '22023';
|
|
108
|
+
END IF;
|
|
109
|
+
|
|
110
|
+
SELECT membership.id INTO STRICT owner_membership_id
|
|
111
|
+
FROM organization_memberships membership
|
|
112
|
+
WHERE membership.account_id = p_account_id
|
|
113
|
+
AND membership.subject_id = caller_subject
|
|
114
|
+
AND membership.status = 'active'
|
|
115
|
+
AND membership.revoked_at IS NULL
|
|
116
|
+
FOR SHARE;
|
|
117
|
+
|
|
118
|
+
PERFORM 1 FROM organization_user_resource_authorities authority
|
|
119
|
+
WHERE authority.id = p_authority_id
|
|
120
|
+
AND authority.account_id = p_account_id
|
|
121
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
122
|
+
AND authority.status = 'active'
|
|
123
|
+
AND authority.revoked_at IS NULL
|
|
124
|
+
FOR UPDATE;
|
|
125
|
+
IF NOT FOUND THEN
|
|
126
|
+
RAISE EXCEPTION 'active self-owned user-resource authority required' USING ERRCODE = '42501';
|
|
127
|
+
END IF;
|
|
128
|
+
|
|
129
|
+
PERFORM 1 FROM workspaces workspace_value
|
|
130
|
+
WHERE workspace_value.id = p_workspace_id AND workspace_value.account_id = p_account_id
|
|
131
|
+
FOR SHARE;
|
|
132
|
+
IF NOT FOUND THEN
|
|
133
|
+
RAISE EXCEPTION 'target workspace is outside the organization' USING ERRCODE = '42501';
|
|
134
|
+
END IF;
|
|
135
|
+
PERFORM 1 FROM organization_memberships membership
|
|
136
|
+
WHERE membership.id = owner_membership_id
|
|
137
|
+
AND (membership.personal_workspace_id = p_workspace_id OR EXISTS (
|
|
138
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
139
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
140
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
141
|
+
AND workspace_membership.subject_id = caller_subject
|
|
142
|
+
));
|
|
143
|
+
IF NOT FOUND THEN
|
|
144
|
+
RAISE EXCEPTION 'owner lacks current target-workspace access' USING ERRCODE = '42501';
|
|
145
|
+
END IF;
|
|
146
|
+
|
|
147
|
+
IF p_session_id IS NOT NULL THEN
|
|
148
|
+
SELECT session_value.authority_epoch, session_value.visibility
|
|
149
|
+
INTO STRICT target_epoch, target_visibility
|
|
150
|
+
FROM sessions session_value
|
|
151
|
+
WHERE session_value.id = p_session_id
|
|
152
|
+
AND session_value.account_id = p_account_id
|
|
153
|
+
AND session_value.workspace_id = p_workspace_id
|
|
154
|
+
AND session_value.status <> 'cancelled'
|
|
155
|
+
FOR SHARE;
|
|
156
|
+
IF target_visibility IS DISTINCT FROM p_context THEN
|
|
157
|
+
RAISE EXCEPTION 'grant context does not match current session visibility'
|
|
158
|
+
USING ERRCODE = '42501';
|
|
159
|
+
END IF;
|
|
160
|
+
END IF;
|
|
161
|
+
|
|
162
|
+
INSERT INTO organization_user_resource_grants (
|
|
163
|
+
account_id, authority_id, owner_organization_membership_id, workspace_id,
|
|
164
|
+
session_id, action, mode, context, authority_epoch, generation, status
|
|
165
|
+
) VALUES (
|
|
166
|
+
p_account_id, p_authority_id, owner_membership_id, p_workspace_id,
|
|
167
|
+
p_session_id, normalized_action, p_mode, p_context, target_epoch, 1, 'active'
|
|
168
|
+
)
|
|
169
|
+
ON CONFLICT (account_id, authority_id, workspace_id, action, mode, context,
|
|
170
|
+
(coalesce(session_id, '00000000-0000-0000-0000-000000000000'::uuid)),
|
|
171
|
+
(coalesce(authority_epoch, 0))) WHERE status = 'active'
|
|
172
|
+
DO UPDATE SET updated_at = organization_user_resource_grants.updated_at
|
|
173
|
+
RETURNING id, workspace_id, session_id, organization_user_resource_grants.action,
|
|
174
|
+
mode, context, generation, status, organization_user_resource_grants.expires_at
|
|
175
|
+
INTO grant_id, target_workspace_id, target_session_id, action, grant_mode,
|
|
176
|
+
grant_context, grant_generation, grant_status, expires_at;
|
|
177
|
+
RETURN NEXT;
|
|
178
|
+
END
|
|
179
|
+
$body$;
|
|
180
|
+
|
|
181
|
+
CREATE OR REPLACE FUNCTION revoke_self_user_resource_grant(
|
|
182
|
+
p_account_id uuid,
|
|
183
|
+
p_grant_id uuid
|
|
184
|
+
) RETURNS TABLE (
|
|
185
|
+
grant_id uuid, grant_generation bigint, grant_status text, revoked_at timestamptz
|
|
186
|
+
)
|
|
187
|
+
LANGUAGE plpgsql
|
|
188
|
+
SECURITY DEFINER
|
|
189
|
+
SET search_path = pg_catalog, pg_temp
|
|
190
|
+
AS $body$
|
|
191
|
+
#variable_conflict use_column
|
|
192
|
+
DECLARE
|
|
193
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
194
|
+
owner_membership_id uuid;
|
|
195
|
+
BEGIN
|
|
196
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
197
|
+
OR caller_subject IS NULL
|
|
198
|
+
THEN
|
|
199
|
+
RAISE EXCEPTION 'user-resource revoke scope mismatch' USING ERRCODE = '42501';
|
|
200
|
+
END IF;
|
|
201
|
+
SELECT membership.id INTO STRICT owner_membership_id
|
|
202
|
+
FROM organization_memberships membership
|
|
203
|
+
WHERE membership.account_id = p_account_id
|
|
204
|
+
AND membership.subject_id = caller_subject
|
|
205
|
+
AND membership.status = 'active'
|
|
206
|
+
AND membership.revoked_at IS NULL
|
|
207
|
+
FOR SHARE;
|
|
208
|
+
|
|
209
|
+
UPDATE organization_user_resource_grants grant_value
|
|
210
|
+
SET status = 'revoked', revoked_at = clock_timestamp(),
|
|
211
|
+
generation = grant_value.generation + 1, updated_at = clock_timestamp()
|
|
212
|
+
FROM organization_user_resource_authorities authority
|
|
213
|
+
WHERE grant_value.id = p_grant_id
|
|
214
|
+
AND grant_value.account_id = p_account_id
|
|
215
|
+
AND authority.id = grant_value.authority_id
|
|
216
|
+
AND authority.account_id = grant_value.account_id
|
|
217
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
218
|
+
AND grant_value.owner_organization_membership_id = owner_membership_id
|
|
219
|
+
AND grant_value.status <> 'revoked'
|
|
220
|
+
RETURNING grant_value.id, grant_value.generation, grant_value.status, grant_value.revoked_at
|
|
221
|
+
INTO grant_id, grant_generation, grant_status, revoked_at;
|
|
222
|
+
IF FOUND THEN RETURN NEXT; RETURN; END IF;
|
|
223
|
+
|
|
224
|
+
SELECT grant_value.id, grant_value.generation, grant_value.status, grant_value.revoked_at
|
|
225
|
+
INTO grant_id, grant_generation, grant_status, revoked_at
|
|
226
|
+
FROM organization_user_resource_grants grant_value
|
|
227
|
+
JOIN organization_user_resource_authorities authority
|
|
228
|
+
ON authority.id = grant_value.authority_id AND authority.account_id = grant_value.account_id
|
|
229
|
+
WHERE grant_value.id = p_grant_id
|
|
230
|
+
AND grant_value.account_id = p_account_id
|
|
231
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
232
|
+
AND grant_value.owner_organization_membership_id = owner_membership_id
|
|
233
|
+
AND grant_value.status = 'revoked';
|
|
234
|
+
IF NOT FOUND THEN
|
|
235
|
+
RAISE EXCEPTION 'self-owned user-resource grant not found' USING ERRCODE = '42501';
|
|
236
|
+
END IF;
|
|
237
|
+
RETURN NEXT;
|
|
238
|
+
END
|
|
239
|
+
$body$;
|
|
240
|
+
|
|
241
|
+
REVOKE ALL ON FUNCTION list_self_user_resource_authorities(uuid) FROM PUBLIC;
|
|
242
|
+
REVOKE ALL ON FUNCTION issue_self_user_resource_grant(uuid, uuid, uuid, text, text, text, uuid, boolean) FROM PUBLIC;
|
|
243
|
+
REVOKE ALL ON FUNCTION revoke_self_user_resource_grant(uuid, uuid) FROM PUBLIC;
|
|
244
|
+
DO $grants$
|
|
245
|
+
BEGIN
|
|
246
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
247
|
+
GRANT EXECUTE ON FUNCTION list_self_user_resource_authorities(uuid) TO opengeni_app;
|
|
248
|
+
GRANT EXECUTE ON FUNCTION issue_self_user_resource_grant(uuid, uuid, uuid, text, text, text, uuid, boolean) TO opengeni_app;
|
|
249
|
+
GRANT EXECUTE ON FUNCTION revoke_self_user_resource_grant(uuid, uuid) TO opengeni_app;
|
|
250
|
+
REVOKE ALL ON TABLE organization_user_resource_authorities FROM opengeni_app;
|
|
251
|
+
REVOKE ALL ON TABLE organization_user_resource_grants FROM opengeni_app;
|
|
252
|
+
END IF;
|
|
253
|
+
END
|
|
254
|
+
$grants$;
|
|
255
|
+
|
|
256
|
+
CREATE OR REPLACE FUNCTION authorize_session_attempt_personal_resource_reads(
|
|
257
|
+
p_account_id uuid,
|
|
258
|
+
p_workspace_id uuid,
|
|
259
|
+
p_attempt_id uuid
|
|
260
|
+
) RETURNS void
|
|
261
|
+
LANGUAGE plpgsql
|
|
262
|
+
SECURITY DEFINER
|
|
263
|
+
SET search_path = pg_catalog, pg_temp
|
|
264
|
+
AS $body$
|
|
265
|
+
DECLARE
|
|
266
|
+
personal_count integer;
|
|
267
|
+
BEGIN
|
|
268
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
269
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
270
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
271
|
+
)::uuid
|
|
272
|
+
THEN
|
|
273
|
+
RAISE EXCEPTION 'personal-resource read scope mismatch' USING ERRCODE = '42501';
|
|
274
|
+
END IF;
|
|
275
|
+
SELECT count(*)::integer INTO personal_count
|
|
276
|
+
FROM session_turn_attempts attempt
|
|
277
|
+
JOIN sessions session_value
|
|
278
|
+
ON session_value.id = attempt.session_id
|
|
279
|
+
AND session_value.account_id = attempt.account_id
|
|
280
|
+
AND session_value.workspace_id = attempt.workspace_id
|
|
281
|
+
LEFT JOIN workspace_variable_sets variable_set
|
|
282
|
+
ON variable_set.id = session_value.variable_set_id
|
|
283
|
+
AND variable_set.account_id = session_value.account_id
|
|
284
|
+
AND variable_set.authority_scope = 'user'
|
|
285
|
+
LEFT JOIN rigs rig
|
|
286
|
+
ON rig.id = session_value.rig_id
|
|
287
|
+
AND rig.account_id = session_value.account_id
|
|
288
|
+
AND rig.authority_scope = 'user'
|
|
289
|
+
LEFT JOIN rig_versions rig_version
|
|
290
|
+
ON rig_version.id = session_value.rig_version_id
|
|
291
|
+
AND rig_version.rig_id = session_value.rig_id
|
|
292
|
+
AND rig_version.account_id = session_value.account_id
|
|
293
|
+
LEFT JOIN LATERAL jsonb_array_elements_text(
|
|
294
|
+
coalesce(rig_version.default_variable_set_ids, '[]'::jsonb)
|
|
295
|
+
) default_id(value) ON true
|
|
296
|
+
LEFT JOIN workspace_variable_sets default_variable_set
|
|
297
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
298
|
+
AND default_variable_set.account_id = session_value.account_id
|
|
299
|
+
AND default_variable_set.authority_scope = 'user'
|
|
300
|
+
WHERE attempt.id = p_attempt_id
|
|
301
|
+
AND attempt.account_id = p_account_id
|
|
302
|
+
AND attempt.workspace_id = p_workspace_id
|
|
303
|
+
AND (variable_set.id IS NOT NULL OR rig.id IS NOT NULL OR default_variable_set.id IS NOT NULL);
|
|
304
|
+
IF personal_count = 0 THEN RETURN; END IF;
|
|
305
|
+
PERFORM * FROM resolve_session_attempt_personal_resources(
|
|
306
|
+
p_account_id, p_workspace_id, p_attempt_id
|
|
307
|
+
);
|
|
308
|
+
END
|
|
309
|
+
$body$;
|
|
310
|
+
|
|
311
|
+
REVOKE ALL ON FUNCTION authorize_session_attempt_personal_resource_reads(uuid, uuid, uuid)
|
|
312
|
+
FROM PUBLIC;
|
|
313
|
+
DO $read_grant$
|
|
314
|
+
BEGIN
|
|
315
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
316
|
+
GRANT EXECUTE ON FUNCTION
|
|
317
|
+
authorize_session_attempt_personal_resource_reads(uuid, uuid, uuid)
|
|
318
|
+
TO opengeni_app;
|
|
319
|
+
END IF;
|
|
320
|
+
END
|
|
321
|
+
$read_grant$;
|
|
322
|
+
|
|
323
|
+
DO $personal_resource_delegation_authority_correction$
|
|
324
|
+
DECLARE
|
|
325
|
+
data_schema text := current_schema();
|
|
326
|
+
BEGIN
|
|
327
|
+
EXECUTE format($ddl$
|
|
328
|
+
CREATE OR REPLACE FUNCTION %1$I.admit_session_attempt_personal_resources()
|
|
329
|
+
RETURNS trigger
|
|
330
|
+
LANGUAGE plpgsql
|
|
331
|
+
SECURITY DEFINER
|
|
332
|
+
SET search_path = %1$I, pg_catalog
|
|
333
|
+
AS $body$
|
|
334
|
+
DECLARE
|
|
335
|
+
session_row sessions%%ROWTYPE;
|
|
336
|
+
turn_row session_turns%%ROWTYPE;
|
|
337
|
+
member_row organization_memberships%%ROWTYPE;
|
|
338
|
+
resource_row record;
|
|
339
|
+
grant_row organization_user_resource_grants%%ROWTYPE;
|
|
340
|
+
resource_total integer := 0;
|
|
341
|
+
snapshot_total integer := 0;
|
|
342
|
+
initiating_subject text;
|
|
343
|
+
affected integer;
|
|
344
|
+
BEGIN
|
|
345
|
+
INSERT INTO opengeni_private.personal_resource_delegation_capabilities (
|
|
346
|
+
backend_pid, transaction_id, capability_kind
|
|
347
|
+
) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'admit')
|
|
348
|
+
ON CONFLICT DO NOTHING;
|
|
349
|
+
|
|
350
|
+
SELECT session_value.* INTO STRICT session_row
|
|
351
|
+
FROM sessions session_value
|
|
352
|
+
WHERE session_value.id = NEW.session_id
|
|
353
|
+
AND session_value.account_id = NEW.account_id
|
|
354
|
+
AND session_value.workspace_id = NEW.workspace_id
|
|
355
|
+
FOR SHARE;
|
|
356
|
+
|
|
357
|
+
SELECT turn_value.* INTO STRICT turn_row
|
|
358
|
+
FROM session_turns turn_value
|
|
359
|
+
WHERE turn_value.id = NEW.turn_id
|
|
360
|
+
AND turn_value.account_id = NEW.account_id
|
|
361
|
+
AND turn_value.workspace_id = NEW.workspace_id
|
|
362
|
+
AND turn_value.session_id = NEW.session_id
|
|
363
|
+
FOR SHARE;
|
|
364
|
+
|
|
365
|
+
SELECT count(*)::integer INTO resource_total
|
|
366
|
+
FROM (
|
|
367
|
+
SELECT DISTINCT selected.resource_kind, selected.resource_id
|
|
368
|
+
FROM (
|
|
369
|
+
SELECT 'variable_set'::text AS resource_kind, variable_set.id AS resource_id
|
|
370
|
+
FROM workspace_variable_sets variable_set
|
|
371
|
+
WHERE variable_set.id = session_row.variable_set_id
|
|
372
|
+
AND variable_set.account_id = NEW.account_id
|
|
373
|
+
AND variable_set.authority_scope = 'user'
|
|
374
|
+
UNION ALL
|
|
375
|
+
SELECT 'rig'::text, rig.id
|
|
376
|
+
FROM rigs rig
|
|
377
|
+
WHERE rig.id = session_row.rig_id
|
|
378
|
+
AND rig.account_id = NEW.account_id
|
|
379
|
+
AND rig.authority_scope = 'user'
|
|
380
|
+
UNION ALL
|
|
381
|
+
SELECT 'variable_set'::text, default_variable_set.id
|
|
382
|
+
FROM rig_versions rig_version
|
|
383
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
384
|
+
rig_version.default_variable_set_ids
|
|
385
|
+
) default_id(value)
|
|
386
|
+
JOIN workspace_variable_sets default_variable_set
|
|
387
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
388
|
+
AND default_variable_set.account_id = NEW.account_id
|
|
389
|
+
AND default_variable_set.authority_scope = 'user'
|
|
390
|
+
WHERE rig_version.id = session_row.rig_version_id
|
|
391
|
+
AND rig_version.rig_id = session_row.rig_id
|
|
392
|
+
AND rig_version.account_id = NEW.account_id
|
|
393
|
+
) selected
|
|
394
|
+
) selected_personal_resources;
|
|
395
|
+
|
|
396
|
+
IF resource_total = 0 THEN
|
|
397
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
398
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
399
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
400
|
+
AND capability_kind = 'admit';
|
|
401
|
+
RETURN NEW;
|
|
402
|
+
END IF;
|
|
403
|
+
|
|
404
|
+
IF NEW.execution_generation <= 0
|
|
405
|
+
OR NEW.state NOT IN ('claimed', 'running')
|
|
406
|
+
OR NEW.closed_at IS NOT NULL
|
|
407
|
+
OR NEW.quiesced_at IS NOT NULL
|
|
408
|
+
OR session_row.active_turn_id IS DISTINCT FROM NEW.turn_id
|
|
409
|
+
OR turn_row.active_attempt_id IS DISTINCT FROM NEW.id
|
|
410
|
+
OR turn_row.execution_generation IS DISTINCT FROM NEW.execution_generation
|
|
411
|
+
OR turn_row.status <> 'running'
|
|
412
|
+
OR EXISTS (
|
|
413
|
+
SELECT 1
|
|
414
|
+
FROM session_attempt_interruptions interruption
|
|
415
|
+
WHERE interruption.account_id = NEW.account_id
|
|
416
|
+
AND interruption.workspace_id = NEW.workspace_id
|
|
417
|
+
AND interruption.session_id = NEW.session_id
|
|
418
|
+
AND interruption.attempt_id = NEW.id
|
|
419
|
+
AND interruption.state IN ('pending', 'delivered', 'acknowledged')
|
|
420
|
+
)
|
|
421
|
+
THEN
|
|
422
|
+
RAISE EXCEPTION 'personal-resource admission requires the exact current uninterrupted attempt'
|
|
423
|
+
USING ERRCODE = '42501';
|
|
424
|
+
END IF;
|
|
425
|
+
|
|
426
|
+
IF NEW.authority_visibility IS DISTINCT FROM session_row.visibility
|
|
427
|
+
OR NEW.authority_epoch IS DISTINCT FROM session_row.authority_epoch
|
|
428
|
+
OR NEW.authority_owner_organization_membership_id
|
|
429
|
+
IS DISTINCT FROM session_row.owner_organization_membership_id
|
|
430
|
+
THEN
|
|
431
|
+
RAISE EXCEPTION 'personal-resource admission requires the exact session authority'
|
|
432
|
+
USING ERRCODE = '42501';
|
|
433
|
+
END IF;
|
|
434
|
+
|
|
435
|
+
initiating_subject := coalesce(
|
|
436
|
+
nullif(btrim(turn_row.initiating_human_subject_id), ''),
|
|
437
|
+
CASE WHEN turn_row.initiator_kind = 'subject'
|
|
438
|
+
THEN nullif(btrim(turn_row.initiator_subject_id), '') END
|
|
439
|
+
);
|
|
440
|
+
IF initiating_subject IS NULL THEN
|
|
441
|
+
RAISE EXCEPTION 'personal-resource admission requires an initiating human subject'
|
|
442
|
+
USING ERRCODE = '42501';
|
|
443
|
+
END IF;
|
|
444
|
+
|
|
445
|
+
SELECT membership.* INTO STRICT member_row
|
|
446
|
+
FROM organization_memberships membership
|
|
447
|
+
WHERE membership.account_id = NEW.account_id
|
|
448
|
+
AND membership.subject_id = initiating_subject
|
|
449
|
+
AND membership.status = 'active'
|
|
450
|
+
AND membership.revoked_at IS NULL
|
|
451
|
+
FOR SHARE;
|
|
452
|
+
|
|
453
|
+
IF member_row.personal_workspace_id IS DISTINCT FROM NEW.workspace_id THEN
|
|
454
|
+
PERFORM 1
|
|
455
|
+
FROM workspace_memberships workspace_membership
|
|
456
|
+
WHERE workspace_membership.account_id = NEW.account_id
|
|
457
|
+
AND workspace_membership.workspace_id = NEW.workspace_id
|
|
458
|
+
AND workspace_membership.subject_id = initiating_subject
|
|
459
|
+
FOR KEY SHARE;
|
|
460
|
+
IF NOT FOUND THEN
|
|
461
|
+
RAISE EXCEPTION 'initiating human lacks target-workspace membership'
|
|
462
|
+
USING ERRCODE = '42501';
|
|
463
|
+
END IF;
|
|
464
|
+
END IF;
|
|
465
|
+
|
|
466
|
+
INSERT INTO session_attempt_personal_resource_admissions (
|
|
467
|
+
attempt_id, account_id, workspace_id, session_id, turn_id,
|
|
468
|
+
execution_generation, initiating_human_subject_id,
|
|
469
|
+
owner_organization_membership_id, membership_authorization_revision,
|
|
470
|
+
session_visibility, session_authority_epoch, resource_count
|
|
471
|
+
) VALUES (
|
|
472
|
+
NEW.id, NEW.account_id, NEW.workspace_id, NEW.session_id, NEW.turn_id,
|
|
473
|
+
NEW.execution_generation, initiating_subject, member_row.id,
|
|
474
|
+
member_row.authorization_revision, session_row.visibility,
|
|
475
|
+
session_row.authority_epoch, resource_total
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
FOR resource_row IN
|
|
479
|
+
WITH selected AS (
|
|
480
|
+
SELECT
|
|
481
|
+
'variable_set'::text AS resource_kind,
|
|
482
|
+
variable_set.id AS resource_id,
|
|
483
|
+
NULL::uuid AS resource_version_id,
|
|
484
|
+
'variable_set.use'::text AS action,
|
|
485
|
+
'session_variable_set'::text AS selection_source,
|
|
486
|
+
variable_set.workspace_id AS resource_workspace_id,
|
|
487
|
+
variable_set.authority_id,
|
|
488
|
+
variable_set.owner_organization_membership_id,
|
|
489
|
+
variable_set.origin_workspace_id
|
|
490
|
+
FROM workspace_variable_sets variable_set
|
|
491
|
+
WHERE variable_set.id = session_row.variable_set_id
|
|
492
|
+
AND variable_set.account_id = NEW.account_id
|
|
493
|
+
AND variable_set.authority_scope = 'user'
|
|
494
|
+
UNION ALL
|
|
495
|
+
SELECT
|
|
496
|
+
'rig'::text, rig.id, session_row.rig_version_id, 'rig.use'::text,
|
|
497
|
+
'session_rig'::text, rig.workspace_id, rig.authority_id,
|
|
498
|
+
rig.owner_organization_membership_id, rig.origin_workspace_id
|
|
499
|
+
FROM rigs rig
|
|
500
|
+
WHERE rig.id = session_row.rig_id
|
|
501
|
+
AND rig.account_id = NEW.account_id
|
|
502
|
+
AND rig.authority_scope = 'user'
|
|
503
|
+
UNION ALL
|
|
504
|
+
SELECT
|
|
505
|
+
'variable_set'::text, default_variable_set.id, NULL::uuid,
|
|
506
|
+
'variable_set.use'::text,
|
|
507
|
+
('rig_default_variable_set:' || default_id.ordinality::text)::text,
|
|
508
|
+
default_variable_set.workspace_id, default_variable_set.authority_id,
|
|
509
|
+
default_variable_set.owner_organization_membership_id,
|
|
510
|
+
default_variable_set.origin_workspace_id
|
|
511
|
+
FROM rig_versions rig_version
|
|
512
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
513
|
+
rig_version.default_variable_set_ids
|
|
514
|
+
) WITH ORDINALITY default_id(value, ordinality)
|
|
515
|
+
JOIN workspace_variable_sets default_variable_set
|
|
516
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
517
|
+
AND default_variable_set.account_id = NEW.account_id
|
|
518
|
+
AND default_variable_set.authority_scope = 'user'
|
|
519
|
+
WHERE rig_version.id = session_row.rig_version_id
|
|
520
|
+
AND rig_version.rig_id = session_row.rig_id
|
|
521
|
+
AND rig_version.account_id = NEW.account_id
|
|
522
|
+
)
|
|
523
|
+
SELECT resource_kind, resource_id,
|
|
524
|
+
min(resource_version_id::text)::uuid AS resource_version_id,
|
|
525
|
+
action, array_agg(selection_source ORDER BY selection_source) AS selection_sources,
|
|
526
|
+
min(resource_workspace_id::text)::uuid AS resource_workspace_id,
|
|
527
|
+
min(authority_id::text)::uuid AS authority_id,
|
|
528
|
+
min(owner_organization_membership_id::text)::uuid
|
|
529
|
+
AS owner_organization_membership_id,
|
|
530
|
+
min(origin_workspace_id::text)::uuid AS origin_workspace_id
|
|
531
|
+
FROM selected
|
|
532
|
+
GROUP BY resource_kind, resource_id, action
|
|
533
|
+
ORDER BY resource_kind, resource_id
|
|
534
|
+
LOOP
|
|
535
|
+
IF resource_row.owner_organization_membership_id IS DISTINCT FROM member_row.id
|
|
536
|
+
THEN
|
|
537
|
+
RAISE EXCEPTION 'personal resource owner/origin does not match initiating membership'
|
|
538
|
+
USING ERRCODE = '42501';
|
|
539
|
+
END IF;
|
|
540
|
+
|
|
541
|
+
IF resource_row.resource_kind = 'variable_set' THEN
|
|
542
|
+
PERFORM 1 FROM workspace_variable_sets variable_set
|
|
543
|
+
WHERE variable_set.id = resource_row.resource_id
|
|
544
|
+
AND variable_set.account_id = NEW.account_id
|
|
545
|
+
AND variable_set.authority_scope = 'user'
|
|
546
|
+
AND variable_set.authority_id = resource_row.authority_id
|
|
547
|
+
AND variable_set.owner_organization_membership_id = member_row.id
|
|
548
|
+
FOR SHARE;
|
|
549
|
+
ELSE
|
|
550
|
+
PERFORM 1 FROM rigs rig
|
|
551
|
+
WHERE rig.id = resource_row.resource_id
|
|
552
|
+
AND rig.account_id = NEW.account_id
|
|
553
|
+
AND rig.authority_scope = 'user'
|
|
554
|
+
AND rig.authority_id = resource_row.authority_id
|
|
555
|
+
AND rig.owner_organization_membership_id = member_row.id
|
|
556
|
+
FOR SHARE;
|
|
557
|
+
IF FOUND THEN
|
|
558
|
+
PERFORM 1 FROM rig_versions rig_version
|
|
559
|
+
WHERE rig_version.id = resource_row.resource_version_id
|
|
560
|
+
AND rig_version.account_id = NEW.account_id
|
|
561
|
+
AND rig_version.rig_id = resource_row.resource_id
|
|
562
|
+
FOR SHARE;
|
|
563
|
+
END IF;
|
|
564
|
+
END IF;
|
|
565
|
+
IF NOT FOUND THEN
|
|
566
|
+
RAISE EXCEPTION 'personal resource identity changed during admission'
|
|
567
|
+
USING ERRCODE = '42501';
|
|
568
|
+
END IF;
|
|
569
|
+
|
|
570
|
+
PERFORM 1
|
|
571
|
+
FROM organization_user_resource_authorities authority
|
|
572
|
+
WHERE authority.id = resource_row.authority_id
|
|
573
|
+
AND authority.account_id = NEW.account_id
|
|
574
|
+
AND authority.organization_membership_id = member_row.id
|
|
575
|
+
AND authority.resource_kind = resource_row.resource_kind
|
|
576
|
+
AND authority.resource_id = resource_row.resource_id
|
|
577
|
+
AND authority.status = 'active'
|
|
578
|
+
AND authority.revoked_at IS NULL
|
|
579
|
+
FOR SHARE;
|
|
580
|
+
IF NOT FOUND THEN
|
|
581
|
+
RAISE EXCEPTION 'personal resource authority is not live'
|
|
582
|
+
USING ERRCODE = '42501';
|
|
583
|
+
END IF;
|
|
584
|
+
|
|
585
|
+
SELECT grant_value.* INTO grant_row
|
|
586
|
+
FROM organization_user_resource_grants grant_value
|
|
587
|
+
WHERE grant_value.account_id = NEW.account_id
|
|
588
|
+
AND grant_value.authority_id = resource_row.authority_id
|
|
589
|
+
AND grant_value.owner_organization_membership_id = member_row.id
|
|
590
|
+
AND grant_value.workspace_id = NEW.workspace_id
|
|
591
|
+
AND grant_value.action = resource_row.action
|
|
592
|
+
AND grant_value.context = session_row.visibility
|
|
593
|
+
AND grant_value.status = 'active'
|
|
594
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
595
|
+
AND (
|
|
596
|
+
(
|
|
597
|
+
grant_value.mode IN ('once', 'session')
|
|
598
|
+
AND grant_value.session_id = NEW.session_id
|
|
599
|
+
AND grant_value.authority_epoch = session_row.authority_epoch
|
|
600
|
+
) OR (
|
|
601
|
+
grant_value.mode = 'always'
|
|
602
|
+
AND grant_value.session_id IS NULL
|
|
603
|
+
AND grant_value.authority_epoch IS NULL
|
|
604
|
+
)
|
|
605
|
+
)
|
|
606
|
+
ORDER BY
|
|
607
|
+
CASE grant_value.mode WHEN 'once' THEN 1 WHEN 'session' THEN 2 ELSE 3 END,
|
|
608
|
+
grant_value.generation DESC,
|
|
609
|
+
grant_value.id
|
|
610
|
+
LIMIT 1
|
|
611
|
+
FOR UPDATE;
|
|
612
|
+
IF NOT FOUND THEN
|
|
613
|
+
RAISE EXCEPTION 'matching personal-resource grant required'
|
|
614
|
+
USING ERRCODE = '42501';
|
|
615
|
+
END IF;
|
|
616
|
+
|
|
617
|
+
INSERT INTO session_attempt_personal_resource_snapshots (
|
|
618
|
+
attempt_id, account_id, workspace_id, session_id, turn_id,
|
|
619
|
+
execution_generation, resource_kind, resource_id, resource_version_id,
|
|
620
|
+
selection_sources, action, origin_workspace_id,
|
|
621
|
+
owner_organization_membership_id, membership_authorization_revision,
|
|
622
|
+
authority_id, authority_generation, target_workspace_id,
|
|
623
|
+
session_visibility, session_authority_epoch, grant_id, grant_generation,
|
|
624
|
+
grant_mode, grant_context, grant_session_id, grant_authority_epoch
|
|
625
|
+
)
|
|
626
|
+
SELECT
|
|
627
|
+
NEW.id, NEW.account_id, NEW.workspace_id, NEW.session_id, NEW.turn_id,
|
|
628
|
+
NEW.execution_generation, resource_row.resource_kind, resource_row.resource_id,
|
|
629
|
+
resource_row.resource_version_id, resource_row.selection_sources,
|
|
630
|
+
resource_row.action, resource_row.origin_workspace_id, member_row.id,
|
|
631
|
+
member_row.authorization_revision, authority.id, authority.generation,
|
|
632
|
+
NEW.workspace_id, session_row.visibility, session_row.authority_epoch,
|
|
633
|
+
grant_row.id, grant_row.generation, grant_row.mode, grant_row.context,
|
|
634
|
+
grant_row.session_id, grant_row.authority_epoch
|
|
635
|
+
FROM organization_user_resource_authorities authority
|
|
636
|
+
WHERE authority.id = resource_row.authority_id
|
|
637
|
+
AND authority.account_id = NEW.account_id;
|
|
638
|
+
|
|
639
|
+
IF grant_row.mode = 'once' THEN
|
|
640
|
+
UPDATE organization_user_resource_grants
|
|
641
|
+
SET status = 'consumed', updated_at = clock_timestamp()
|
|
642
|
+
WHERE id = grant_row.id
|
|
643
|
+
AND account_id = NEW.account_id
|
|
644
|
+
AND generation = grant_row.generation
|
|
645
|
+
AND status = 'active';
|
|
646
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
647
|
+
IF affected <> 1 THEN
|
|
648
|
+
RAISE EXCEPTION 'once grant lost its first-use race'
|
|
649
|
+
USING ERRCODE = '40001';
|
|
650
|
+
END IF;
|
|
651
|
+
INSERT INTO personal_resource_once_consumption_receipts (
|
|
652
|
+
grant_id, account_id, attempt_id, workspace_id, session_id, turn_id,
|
|
653
|
+
execution_generation, authority_id, authority_generation, grant_generation
|
|
654
|
+
) SELECT
|
|
655
|
+
grant_row.id, NEW.account_id, NEW.id, NEW.workspace_id, NEW.session_id,
|
|
656
|
+
NEW.turn_id, NEW.execution_generation, authority.id, authority.generation,
|
|
657
|
+
grant_row.generation
|
|
658
|
+
FROM organization_user_resource_authorities authority
|
|
659
|
+
WHERE authority.id = resource_row.authority_id
|
|
660
|
+
AND authority.account_id = NEW.account_id;
|
|
661
|
+
END IF;
|
|
662
|
+
snapshot_total := snapshot_total + 1;
|
|
663
|
+
END LOOP;
|
|
664
|
+
|
|
665
|
+
IF snapshot_total <> resource_total THEN
|
|
666
|
+
RAISE EXCEPTION 'personal-resource snapshot collection is not exact'
|
|
667
|
+
USING ERRCODE = '23514';
|
|
668
|
+
END IF;
|
|
669
|
+
|
|
670
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
671
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
672
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
673
|
+
AND capability_kind = 'admit';
|
|
674
|
+
RETURN NEW;
|
|
675
|
+
EXCEPTION WHEN OTHERS THEN
|
|
676
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
677
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
678
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
679
|
+
AND capability_kind = 'admit';
|
|
680
|
+
RAISE;
|
|
681
|
+
END;
|
|
682
|
+
$body$;
|
|
683
|
+
$ddl$, data_schema);
|
|
684
|
+
|
|
685
|
+
EXECUTE format($ddl$
|
|
686
|
+
CREATE OR REPLACE FUNCTION %1$I.resolve_session_attempt_personal_resources(
|
|
687
|
+
p_account_id uuid,
|
|
688
|
+
p_workspace_id uuid,
|
|
689
|
+
p_attempt_id uuid
|
|
690
|
+
) RETURNS TABLE (
|
|
691
|
+
resource_kind text,
|
|
692
|
+
resource_id uuid,
|
|
693
|
+
resource_version_id uuid,
|
|
694
|
+
selection_sources text[],
|
|
695
|
+
action text,
|
|
696
|
+
authority_id uuid,
|
|
697
|
+
authority_generation bigint,
|
|
698
|
+
grant_id uuid,
|
|
699
|
+
grant_generation bigint,
|
|
700
|
+
grant_mode text
|
|
701
|
+
)
|
|
702
|
+
LANGUAGE plpgsql
|
|
703
|
+
SECURITY DEFINER
|
|
704
|
+
SET search_path = %1$I, pg_catalog
|
|
705
|
+
AS $body$
|
|
706
|
+
DECLARE
|
|
707
|
+
admission_row session_attempt_personal_resource_admissions%%ROWTYPE;
|
|
708
|
+
invalid_count integer;
|
|
709
|
+
caller_subject text := coalesce(
|
|
710
|
+
nullif(pg_catalog.current_setting('opengeni.initiating_human_subject_id', true), ''),
|
|
711
|
+
nullif(pg_catalog.current_setting('opengeni.subject_id', true), '')
|
|
712
|
+
);
|
|
713
|
+
BEGIN
|
|
714
|
+
IF p_account_id IS DISTINCT FROM nullif(
|
|
715
|
+
pg_catalog.current_setting('opengeni.account_id', true), ''
|
|
716
|
+
)::uuid
|
|
717
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
718
|
+
pg_catalog.current_setting('opengeni.workspace_id', true), ''
|
|
719
|
+
)::uuid
|
|
720
|
+
THEN
|
|
721
|
+
RAISE EXCEPTION 'personal-resource resolve scope mismatch'
|
|
722
|
+
USING ERRCODE = '42501';
|
|
723
|
+
END IF;
|
|
724
|
+
|
|
725
|
+
INSERT INTO opengeni_private.personal_resource_delegation_capabilities (
|
|
726
|
+
backend_pid, transaction_id, capability_kind
|
|
727
|
+
) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'resolve')
|
|
728
|
+
ON CONFLICT DO NOTHING;
|
|
729
|
+
|
|
730
|
+
SELECT admission.* INTO STRICT admission_row
|
|
731
|
+
FROM session_attempt_personal_resource_admissions admission
|
|
732
|
+
WHERE admission.attempt_id = p_attempt_id
|
|
733
|
+
AND admission.account_id = p_account_id
|
|
734
|
+
AND admission.workspace_id = p_workspace_id;
|
|
735
|
+
|
|
736
|
+
IF caller_subject IS DISTINCT FROM admission_row.initiating_human_subject_id THEN
|
|
737
|
+
RAISE EXCEPTION 'personal-resource resolve initiating human mismatch'
|
|
738
|
+
USING ERRCODE = '42501';
|
|
739
|
+
END IF;
|
|
740
|
+
|
|
741
|
+
PERFORM 1
|
|
742
|
+
FROM sessions session_value
|
|
743
|
+
JOIN session_turns turn_value
|
|
744
|
+
ON turn_value.account_id = session_value.account_id
|
|
745
|
+
AND turn_value.workspace_id = session_value.workspace_id
|
|
746
|
+
AND turn_value.session_id = session_value.id
|
|
747
|
+
JOIN session_turn_attempts attempt
|
|
748
|
+
ON attempt.account_id = turn_value.account_id
|
|
749
|
+
AND attempt.workspace_id = turn_value.workspace_id
|
|
750
|
+
AND attempt.session_id = turn_value.session_id
|
|
751
|
+
AND attempt.turn_id = turn_value.id
|
|
752
|
+
WHERE session_value.id = admission_row.session_id
|
|
753
|
+
AND session_value.account_id = admission_row.account_id
|
|
754
|
+
AND session_value.workspace_id = admission_row.workspace_id
|
|
755
|
+
AND session_value.active_turn_id = admission_row.turn_id
|
|
756
|
+
AND turn_value.id = admission_row.turn_id
|
|
757
|
+
AND turn_value.active_attempt_id = admission_row.attempt_id
|
|
758
|
+
AND turn_value.execution_generation = admission_row.execution_generation
|
|
759
|
+
AND turn_value.status = 'running'
|
|
760
|
+
AND attempt.id = admission_row.attempt_id
|
|
761
|
+
AND attempt.execution_generation = admission_row.execution_generation
|
|
762
|
+
AND attempt.state IN ('claimed', 'running')
|
|
763
|
+
AND attempt.closed_at IS NULL
|
|
764
|
+
AND attempt.quiesced_at IS NULL
|
|
765
|
+
FOR SHARE OF session_value, turn_value
|
|
766
|
+
FOR UPDATE OF attempt;
|
|
767
|
+
IF NOT FOUND THEN
|
|
768
|
+
RAISE EXCEPTION 'personal-resource resolve requires the exact current uninterrupted attempt'
|
|
769
|
+
USING ERRCODE = '42501';
|
|
770
|
+
END IF;
|
|
771
|
+
|
|
772
|
+
IF EXISTS (
|
|
773
|
+
SELECT 1
|
|
774
|
+
FROM session_attempt_interruptions interruption
|
|
775
|
+
WHERE interruption.account_id = admission_row.account_id
|
|
776
|
+
AND interruption.workspace_id = admission_row.workspace_id
|
|
777
|
+
AND interruption.session_id = admission_row.session_id
|
|
778
|
+
AND interruption.attempt_id = admission_row.attempt_id
|
|
779
|
+
AND interruption.state IN ('pending', 'delivered', 'acknowledged')
|
|
780
|
+
)
|
|
781
|
+
THEN
|
|
782
|
+
RAISE EXCEPTION 'personal-resource resolve requires the exact current uninterrupted attempt'
|
|
783
|
+
USING ERRCODE = '42501';
|
|
784
|
+
END IF;
|
|
785
|
+
|
|
786
|
+
SELECT count(*)::integer INTO invalid_count
|
|
787
|
+
FROM session_attempt_personal_resource_snapshots snapshot
|
|
788
|
+
WHERE snapshot.attempt_id = admission_row.attempt_id
|
|
789
|
+
AND NOT (
|
|
790
|
+
EXISTS (
|
|
791
|
+
SELECT 1
|
|
792
|
+
FROM session_turn_attempts attempt
|
|
793
|
+
JOIN sessions session_value
|
|
794
|
+
ON session_value.id = attempt.session_id
|
|
795
|
+
AND session_value.account_id = attempt.account_id
|
|
796
|
+
AND session_value.workspace_id = attempt.workspace_id
|
|
797
|
+
JOIN session_turns turn_value
|
|
798
|
+
ON turn_value.id = attempt.turn_id
|
|
799
|
+
AND turn_value.account_id = attempt.account_id
|
|
800
|
+
AND turn_value.workspace_id = attempt.workspace_id
|
|
801
|
+
AND turn_value.session_id = attempt.session_id
|
|
802
|
+
WHERE attempt.id = snapshot.attempt_id
|
|
803
|
+
AND attempt.account_id = snapshot.account_id
|
|
804
|
+
AND attempt.workspace_id = snapshot.workspace_id
|
|
805
|
+
AND attempt.session_id = snapshot.session_id
|
|
806
|
+
AND attempt.turn_id = snapshot.turn_id
|
|
807
|
+
AND attempt.execution_generation = snapshot.execution_generation
|
|
808
|
+
AND attempt.state IN ('claimed', 'running')
|
|
809
|
+
AND attempt.quiesced_at IS NULL
|
|
810
|
+
AND attempt.authority_visibility = snapshot.session_visibility
|
|
811
|
+
AND attempt.authority_epoch = snapshot.session_authority_epoch
|
|
812
|
+
AND session_value.visibility = snapshot.session_visibility
|
|
813
|
+
AND session_value.authority_epoch = snapshot.session_authority_epoch
|
|
814
|
+
AND coalesce(
|
|
815
|
+
nullif(btrim(turn_value.initiating_human_subject_id), ''),
|
|
816
|
+
CASE WHEN turn_value.initiator_kind = 'subject'
|
|
817
|
+
THEN nullif(btrim(turn_value.initiator_subject_id), '') END
|
|
818
|
+
) = admission_row.initiating_human_subject_id
|
|
819
|
+
)
|
|
820
|
+
AND EXISTS (
|
|
821
|
+
SELECT 1
|
|
822
|
+
FROM organization_memberships membership
|
|
823
|
+
WHERE membership.id = snapshot.owner_organization_membership_id
|
|
824
|
+
AND membership.account_id = snapshot.account_id
|
|
825
|
+
AND membership.subject_id = admission_row.initiating_human_subject_id
|
|
826
|
+
AND membership.status = 'active'
|
|
827
|
+
AND membership.revoked_at IS NULL
|
|
828
|
+
AND membership.authorization_revision
|
|
829
|
+
= snapshot.membership_authorization_revision
|
|
830
|
+
AND (
|
|
831
|
+
membership.personal_workspace_id = snapshot.workspace_id
|
|
832
|
+
OR EXISTS (
|
|
833
|
+
SELECT 1
|
|
834
|
+
FROM workspace_memberships workspace_membership
|
|
835
|
+
WHERE workspace_membership.account_id = membership.account_id
|
|
836
|
+
AND workspace_membership.workspace_id = snapshot.workspace_id
|
|
837
|
+
AND workspace_membership.subject_id = membership.subject_id
|
|
838
|
+
)
|
|
839
|
+
)
|
|
840
|
+
)
|
|
841
|
+
AND EXISTS (
|
|
842
|
+
SELECT 1
|
|
843
|
+
FROM organization_user_resource_authorities authority
|
|
844
|
+
WHERE authority.id = snapshot.authority_id
|
|
845
|
+
AND authority.account_id = snapshot.account_id
|
|
846
|
+
AND authority.organization_membership_id
|
|
847
|
+
= snapshot.owner_organization_membership_id
|
|
848
|
+
AND authority.resource_kind = snapshot.resource_kind
|
|
849
|
+
AND authority.resource_id = snapshot.resource_id
|
|
850
|
+
AND authority.generation = snapshot.authority_generation
|
|
851
|
+
AND authority.status = 'active'
|
|
852
|
+
AND authority.revoked_at IS NULL
|
|
853
|
+
)
|
|
854
|
+
AND (
|
|
855
|
+
(
|
|
856
|
+
snapshot.resource_kind = 'variable_set'
|
|
857
|
+
AND EXISTS (
|
|
858
|
+
SELECT 1 FROM workspace_variable_sets variable_set
|
|
859
|
+
WHERE variable_set.id = snapshot.resource_id
|
|
860
|
+
AND variable_set.account_id = snapshot.account_id
|
|
861
|
+
AND variable_set.authority_scope = 'user'
|
|
862
|
+
AND variable_set.authority_id = snapshot.authority_id
|
|
863
|
+
AND variable_set.owner_organization_membership_id
|
|
864
|
+
= snapshot.owner_organization_membership_id
|
|
865
|
+
)
|
|
866
|
+
) OR (
|
|
867
|
+
snapshot.resource_kind = 'rig'
|
|
868
|
+
AND EXISTS (
|
|
869
|
+
SELECT 1 FROM rigs rig
|
|
870
|
+
JOIN rig_versions rig_version
|
|
871
|
+
ON rig_version.id = snapshot.resource_version_id
|
|
872
|
+
AND rig_version.rig_id = rig.id
|
|
873
|
+
AND rig_version.account_id = rig.account_id
|
|
874
|
+
WHERE rig.id = snapshot.resource_id
|
|
875
|
+
AND rig.account_id = snapshot.account_id
|
|
876
|
+
AND rig.authority_scope = 'user'
|
|
877
|
+
AND rig.authority_id = snapshot.authority_id
|
|
878
|
+
AND rig.owner_organization_membership_id
|
|
879
|
+
= snapshot.owner_organization_membership_id
|
|
880
|
+
)
|
|
881
|
+
)
|
|
882
|
+
)
|
|
883
|
+
AND EXISTS (
|
|
884
|
+
SELECT 1
|
|
885
|
+
FROM organization_user_resource_grants grant_value
|
|
886
|
+
WHERE grant_value.id = snapshot.grant_id
|
|
887
|
+
AND grant_value.account_id = snapshot.account_id
|
|
888
|
+
AND grant_value.authority_id = snapshot.authority_id
|
|
889
|
+
AND grant_value.owner_organization_membership_id
|
|
890
|
+
= snapshot.owner_organization_membership_id
|
|
891
|
+
AND grant_value.workspace_id = snapshot.target_workspace_id
|
|
892
|
+
AND grant_value.action = snapshot.action
|
|
893
|
+
AND grant_value.mode = snapshot.grant_mode
|
|
894
|
+
AND grant_value.context = snapshot.grant_context
|
|
895
|
+
AND grant_value.generation = snapshot.grant_generation
|
|
896
|
+
AND grant_value.session_id IS NOT DISTINCT FROM snapshot.grant_session_id
|
|
897
|
+
AND grant_value.authority_epoch
|
|
898
|
+
IS NOT DISTINCT FROM snapshot.grant_authority_epoch
|
|
899
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
900
|
+
AND (
|
|
901
|
+
(
|
|
902
|
+
snapshot.grant_mode = 'once'
|
|
903
|
+
AND grant_value.status = 'consumed'
|
|
904
|
+
AND EXISTS (
|
|
905
|
+
SELECT 1 FROM personal_resource_once_consumption_receipts receipt
|
|
906
|
+
WHERE receipt.grant_id = snapshot.grant_id
|
|
907
|
+
AND receipt.account_id = snapshot.account_id
|
|
908
|
+
AND receipt.attempt_id = snapshot.attempt_id
|
|
909
|
+
AND receipt.authority_id = snapshot.authority_id
|
|
910
|
+
AND receipt.authority_generation = snapshot.authority_generation
|
|
911
|
+
AND receipt.grant_generation = snapshot.grant_generation
|
|
912
|
+
)
|
|
913
|
+
) OR (
|
|
914
|
+
snapshot.grant_mode IN ('session', 'always')
|
|
915
|
+
AND grant_value.status = 'active'
|
|
916
|
+
)
|
|
917
|
+
)
|
|
918
|
+
)
|
|
919
|
+
);
|
|
920
|
+
|
|
921
|
+
IF invalid_count <> 0 THEN
|
|
922
|
+
RAISE EXCEPTION 'personal-resource authority snapshot is no longer live'
|
|
923
|
+
USING ERRCODE = '42501';
|
|
924
|
+
END IF;
|
|
925
|
+
|
|
926
|
+
SELECT count(*)::integer INTO invalid_count
|
|
927
|
+
FROM session_attempt_personal_resource_snapshots snapshot
|
|
928
|
+
WHERE snapshot.attempt_id = admission_row.attempt_id;
|
|
929
|
+
IF invalid_count <> admission_row.resource_count THEN
|
|
930
|
+
RAISE EXCEPTION 'personal-resource snapshot collection is incomplete'
|
|
931
|
+
USING ERRCODE = '42501';
|
|
932
|
+
END IF;
|
|
933
|
+
|
|
934
|
+
RETURN QUERY
|
|
935
|
+
SELECT snapshot.resource_kind, snapshot.resource_id,
|
|
936
|
+
snapshot.resource_version_id, snapshot.selection_sources, snapshot.action,
|
|
937
|
+
snapshot.authority_id, snapshot.authority_generation, snapshot.grant_id,
|
|
938
|
+
snapshot.grant_generation, snapshot.grant_mode
|
|
939
|
+
FROM session_attempt_personal_resource_snapshots snapshot
|
|
940
|
+
WHERE snapshot.attempt_id = admission_row.attempt_id
|
|
941
|
+
ORDER BY snapshot.resource_kind, snapshot.resource_id;
|
|
942
|
+
|
|
943
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
944
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
945
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
946
|
+
AND capability_kind = 'resolve';
|
|
947
|
+
RETURN;
|
|
948
|
+
EXCEPTION WHEN OTHERS THEN
|
|
949
|
+
DELETE FROM opengeni_private.personal_resource_delegation_capabilities
|
|
950
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
951
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
952
|
+
AND capability_kind = 'resolve';
|
|
953
|
+
RAISE;
|
|
954
|
+
END;
|
|
955
|
+
$body$;
|
|
956
|
+
$ddl$, data_schema);
|
|
957
|
+
|
|
958
|
+
END
|
|
959
|
+
$personal_resource_delegation_authority_correction$;
|
|
960
|
+
|
|
961
|
+
CREATE OR REPLACE FUNCTION freeze_scheduled_task_personal_resources(
|
|
962
|
+
p_account_id uuid,
|
|
963
|
+
p_workspace_id uuid,
|
|
964
|
+
p_task_id uuid,
|
|
965
|
+
p_task_authority_revision bigint
|
|
966
|
+
)
|
|
967
|
+
RETURNS integer
|
|
968
|
+
LANGUAGE plpgsql
|
|
969
|
+
SECURITY DEFINER
|
|
970
|
+
SET search_path = pg_catalog, pg_temp
|
|
971
|
+
AS $freeze_scheduled_task_personal_resources$
|
|
972
|
+
DECLARE
|
|
973
|
+
task_row record;
|
|
974
|
+
member_row record;
|
|
975
|
+
session_row record;
|
|
976
|
+
resource_row record;
|
|
977
|
+
grant_row record;
|
|
978
|
+
initiating_subject text := coalesce(
|
|
979
|
+
nullif(btrim(current_setting('opengeni.initiating_human_subject_id', true)), ''),
|
|
980
|
+
nullif(btrim(current_setting('opengeni.subject_id', true)), '')
|
|
981
|
+
);
|
|
982
|
+
target_session uuid;
|
|
983
|
+
target_visibility text := 'workspace_shared';
|
|
984
|
+
target_epoch integer;
|
|
985
|
+
resource_total integer := 0;
|
|
986
|
+
BEGIN
|
|
987
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
988
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
989
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
990
|
+
)::uuid
|
|
991
|
+
THEN
|
|
992
|
+
RAISE EXCEPTION 'scheduled personal-resource task scope mismatch' USING ERRCODE = '42501';
|
|
993
|
+
END IF;
|
|
994
|
+
|
|
995
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
996
|
+
backend_pid, transaction_id, capability_kind
|
|
997
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'task_write')
|
|
998
|
+
ON CONFLICT DO NOTHING;
|
|
999
|
+
|
|
1000
|
+
SELECT task.* INTO STRICT task_row
|
|
1001
|
+
FROM scheduled_tasks task
|
|
1002
|
+
WHERE task.id = p_task_id
|
|
1003
|
+
AND task.account_id = p_account_id
|
|
1004
|
+
AND task.workspace_id = p_workspace_id
|
|
1005
|
+
AND task.authority_revision = p_task_authority_revision
|
|
1006
|
+
FOR UPDATE;
|
|
1007
|
+
|
|
1008
|
+
SELECT count(*)::integer INTO resource_total
|
|
1009
|
+
FROM (
|
|
1010
|
+
SELECT variable_set.id
|
|
1011
|
+
FROM workspace_variable_sets variable_set
|
|
1012
|
+
WHERE variable_set.id = task_row.variable_set_id
|
|
1013
|
+
AND variable_set.account_id = p_account_id
|
|
1014
|
+
AND variable_set.authority_scope = 'user'
|
|
1015
|
+
UNION
|
|
1016
|
+
SELECT rig.id
|
|
1017
|
+
FROM rigs rig
|
|
1018
|
+
WHERE rig.id = task_row.rig_id
|
|
1019
|
+
AND rig.account_id = p_account_id
|
|
1020
|
+
AND rig.authority_scope = 'user'
|
|
1021
|
+
UNION
|
|
1022
|
+
SELECT default_variable_set.id
|
|
1023
|
+
FROM rigs rig
|
|
1024
|
+
JOIN rig_versions rig_version
|
|
1025
|
+
ON rig_version.rig_id = rig.id
|
|
1026
|
+
AND rig_version.account_id = rig.account_id
|
|
1027
|
+
AND rig_version.active
|
|
1028
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
1029
|
+
rig_version.default_variable_set_ids
|
|
1030
|
+
) default_id(value)
|
|
1031
|
+
JOIN workspace_variable_sets default_variable_set
|
|
1032
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
1033
|
+
AND default_variable_set.account_id = p_account_id
|
|
1034
|
+
AND default_variable_set.authority_scope = 'user'
|
|
1035
|
+
WHERE rig.id = task_row.rig_id
|
|
1036
|
+
AND rig.account_id = p_account_id
|
|
1037
|
+
) selected;
|
|
1038
|
+
|
|
1039
|
+
IF resource_total = 0 THEN
|
|
1040
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1041
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1042
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1043
|
+
AND capability_kind = 'task_write';
|
|
1044
|
+
RETURN 0;
|
|
1045
|
+
END IF;
|
|
1046
|
+
|
|
1047
|
+
IF initiating_subject IS NULL THEN
|
|
1048
|
+
RAISE EXCEPTION 'scheduled personal resources require a causal human'
|
|
1049
|
+
USING ERRCODE = '42501';
|
|
1050
|
+
END IF;
|
|
1051
|
+
|
|
1052
|
+
SELECT membership.* INTO STRICT member_row
|
|
1053
|
+
FROM organization_memberships membership
|
|
1054
|
+
WHERE membership.account_id = p_account_id
|
|
1055
|
+
AND membership.subject_id = initiating_subject
|
|
1056
|
+
AND membership.status = 'active'
|
|
1057
|
+
AND membership.revoked_at IS NULL
|
|
1058
|
+
FOR SHARE;
|
|
1059
|
+
|
|
1060
|
+
IF member_row.personal_workspace_id IS DISTINCT FROM p_workspace_id THEN
|
|
1061
|
+
PERFORM 1 FROM workspace_memberships workspace_membership
|
|
1062
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
1063
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
1064
|
+
AND workspace_membership.subject_id = initiating_subject
|
|
1065
|
+
FOR KEY SHARE;
|
|
1066
|
+
IF NOT FOUND THEN
|
|
1067
|
+
RAISE EXCEPTION 'scheduled personal-resource owner lacks workspace access'
|
|
1068
|
+
USING ERRCODE = '42501';
|
|
1069
|
+
END IF;
|
|
1070
|
+
END IF;
|
|
1071
|
+
|
|
1072
|
+
IF task_row.run_mode = 'existing_session'
|
|
1073
|
+
OR (task_row.run_mode = 'reusable_session' AND task_row.reusable_session_id IS NOT NULL)
|
|
1074
|
+
THEN
|
|
1075
|
+
target_session := task_row.reusable_session_id;
|
|
1076
|
+
SELECT session_value.* INTO STRICT session_row
|
|
1077
|
+
FROM sessions session_value
|
|
1078
|
+
WHERE session_value.id = target_session
|
|
1079
|
+
AND session_value.account_id = p_account_id
|
|
1080
|
+
AND session_value.workspace_id = p_workspace_id
|
|
1081
|
+
AND session_value.status <> 'cancelled'
|
|
1082
|
+
FOR SHARE;
|
|
1083
|
+
target_visibility := session_row.visibility;
|
|
1084
|
+
target_epoch := session_row.authority_epoch;
|
|
1085
|
+
END IF;
|
|
1086
|
+
|
|
1087
|
+
INSERT INTO scheduled_task_personal_resource_authorities (
|
|
1088
|
+
task_id, task_authority_revision, account_id, workspace_id,
|
|
1089
|
+
initiating_human_subject_id, owner_organization_membership_id,
|
|
1090
|
+
membership_authorization_revision, target_session_id, session_visibility,
|
|
1091
|
+
session_authority_epoch, execution_digest, resource_count
|
|
1092
|
+
) VALUES (
|
|
1093
|
+
p_task_id, p_task_authority_revision, p_account_id, p_workspace_id,
|
|
1094
|
+
initiating_subject, member_row.id, member_row.authorization_revision,
|
|
1095
|
+
target_session, target_visibility, target_epoch, task_row.execution_digest,
|
|
1096
|
+
resource_total
|
|
1097
|
+
);
|
|
1098
|
+
|
|
1099
|
+
FOR resource_row IN
|
|
1100
|
+
WITH selected AS (
|
|
1101
|
+
SELECT 'variable_set'::text AS resource_kind, variable_set.id AS resource_id,
|
|
1102
|
+
NULL::uuid AS resource_version_id, 'variable_set.use'::text AS action,
|
|
1103
|
+
'session_variable_set'::text AS selection_source,
|
|
1104
|
+
variable_set.workspace_id AS resource_workspace_id, variable_set.authority_id,
|
|
1105
|
+
variable_set.owner_organization_membership_id, variable_set.origin_workspace_id
|
|
1106
|
+
FROM workspace_variable_sets variable_set
|
|
1107
|
+
WHERE variable_set.id = task_row.variable_set_id
|
|
1108
|
+
AND variable_set.account_id = p_account_id
|
|
1109
|
+
AND variable_set.authority_scope = 'user'
|
|
1110
|
+
UNION ALL
|
|
1111
|
+
SELECT 'rig'::text, rig.id, rig_version.id, 'rig.use'::text,
|
|
1112
|
+
'session_rig'::text, rig.workspace_id, rig.authority_id,
|
|
1113
|
+
rig.owner_organization_membership_id, rig.origin_workspace_id
|
|
1114
|
+
FROM rigs rig
|
|
1115
|
+
JOIN rig_versions rig_version
|
|
1116
|
+
ON rig_version.rig_id = rig.id
|
|
1117
|
+
AND rig_version.account_id = rig.account_id
|
|
1118
|
+
AND rig_version.active
|
|
1119
|
+
WHERE rig.id = task_row.rig_id
|
|
1120
|
+
AND rig.account_id = p_account_id
|
|
1121
|
+
AND rig.authority_scope = 'user'
|
|
1122
|
+
UNION ALL
|
|
1123
|
+
SELECT 'variable_set'::text, default_variable_set.id, NULL::uuid,
|
|
1124
|
+
'variable_set.use'::text,
|
|
1125
|
+
('rig_default_variable_set:' || default_id.ordinality::text)::text,
|
|
1126
|
+
default_variable_set.workspace_id, default_variable_set.authority_id,
|
|
1127
|
+
default_variable_set.owner_organization_membership_id,
|
|
1128
|
+
default_variable_set.origin_workspace_id
|
|
1129
|
+
FROM rigs rig
|
|
1130
|
+
JOIN rig_versions rig_version
|
|
1131
|
+
ON rig_version.rig_id = rig.id
|
|
1132
|
+
AND rig_version.account_id = rig.account_id
|
|
1133
|
+
AND rig_version.active
|
|
1134
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
1135
|
+
rig_version.default_variable_set_ids
|
|
1136
|
+
) WITH ORDINALITY default_id(value, ordinality)
|
|
1137
|
+
JOIN workspace_variable_sets default_variable_set
|
|
1138
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
1139
|
+
AND default_variable_set.account_id = p_account_id
|
|
1140
|
+
AND default_variable_set.authority_scope = 'user'
|
|
1141
|
+
WHERE rig.id = task_row.rig_id
|
|
1142
|
+
AND rig.account_id = p_account_id
|
|
1143
|
+
)
|
|
1144
|
+
SELECT resource_kind, resource_id, min(resource_version_id::text)::uuid resource_version_id,
|
|
1145
|
+
action, array_agg(selection_source ORDER BY selection_source) selection_sources,
|
|
1146
|
+
min(resource_workspace_id::text)::uuid resource_workspace_id,
|
|
1147
|
+
min(authority_id::text)::uuid authority_id,
|
|
1148
|
+
min(owner_organization_membership_id::text)::uuid owner_organization_membership_id,
|
|
1149
|
+
min(origin_workspace_id::text)::uuid origin_workspace_id
|
|
1150
|
+
FROM selected
|
|
1151
|
+
GROUP BY resource_kind, resource_id, action
|
|
1152
|
+
ORDER BY resource_kind, resource_id
|
|
1153
|
+
LOOP
|
|
1154
|
+
IF resource_row.owner_organization_membership_id IS DISTINCT FROM member_row.id
|
|
1155
|
+
THEN
|
|
1156
|
+
RAISE EXCEPTION 'scheduled personal resource belongs to another human or organization'
|
|
1157
|
+
USING ERRCODE = '42501';
|
|
1158
|
+
END IF;
|
|
1159
|
+
|
|
1160
|
+
SELECT grant_value.* INTO grant_row
|
|
1161
|
+
FROM organization_user_resource_grants grant_value
|
|
1162
|
+
JOIN organization_user_resource_authorities authority
|
|
1163
|
+
ON authority.id = grant_value.authority_id
|
|
1164
|
+
AND authority.account_id = grant_value.account_id
|
|
1165
|
+
WHERE authority.id = resource_row.authority_id
|
|
1166
|
+
AND authority.account_id = p_account_id
|
|
1167
|
+
AND authority.organization_membership_id = member_row.id
|
|
1168
|
+
AND authority.resource_kind = resource_row.resource_kind
|
|
1169
|
+
AND authority.resource_id = resource_row.resource_id
|
|
1170
|
+
AND authority.status = 'active'
|
|
1171
|
+
AND authority.revoked_at IS NULL
|
|
1172
|
+
AND grant_value.owner_organization_membership_id = member_row.id
|
|
1173
|
+
AND grant_value.workspace_id = p_workspace_id
|
|
1174
|
+
AND grant_value.action = resource_row.action
|
|
1175
|
+
AND grant_value.context = target_visibility
|
|
1176
|
+
AND grant_value.status = 'active'
|
|
1177
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
1178
|
+
AND (
|
|
1179
|
+
(target_session IS NULL AND grant_value.mode = 'always'
|
|
1180
|
+
AND grant_value.session_id IS NULL AND grant_value.authority_epoch IS NULL)
|
|
1181
|
+
OR (target_session IS NOT NULL AND (
|
|
1182
|
+
(grant_value.mode IN ('once', 'session')
|
|
1183
|
+
AND grant_value.session_id = target_session
|
|
1184
|
+
AND grant_value.authority_epoch = target_epoch)
|
|
1185
|
+
OR (grant_value.mode = 'always'
|
|
1186
|
+
AND grant_value.session_id IS NULL AND grant_value.authority_epoch IS NULL)
|
|
1187
|
+
))
|
|
1188
|
+
)
|
|
1189
|
+
ORDER BY CASE grant_value.mode WHEN 'once' THEN 1 WHEN 'session' THEN 2 ELSE 3 END,
|
|
1190
|
+
grant_value.generation DESC, grant_value.id
|
|
1191
|
+
LIMIT 1
|
|
1192
|
+
FOR SHARE OF authority, grant_value;
|
|
1193
|
+
IF NOT FOUND THEN
|
|
1194
|
+
RAISE EXCEPTION 'matching scheduled personal-resource grant required'
|
|
1195
|
+
USING ERRCODE = '42501';
|
|
1196
|
+
END IF;
|
|
1197
|
+
|
|
1198
|
+
INSERT INTO scheduled_task_personal_resource_snapshots (
|
|
1199
|
+
task_id, task_authority_revision, account_id, workspace_id,
|
|
1200
|
+
resource_kind, resource_id, resource_version_id, selection_sources, action,
|
|
1201
|
+
origin_workspace_id, owner_organization_membership_id,
|
|
1202
|
+
membership_authorization_revision, authority_id, authority_generation,
|
|
1203
|
+
target_workspace_id, session_visibility, session_authority_epoch,
|
|
1204
|
+
grant_id, grant_generation, grant_mode, grant_context,
|
|
1205
|
+
grant_session_id, grant_authority_epoch
|
|
1206
|
+
)
|
|
1207
|
+
SELECT p_task_id, p_task_authority_revision, p_account_id, p_workspace_id,
|
|
1208
|
+
resource_row.resource_kind, resource_row.resource_id,
|
|
1209
|
+
resource_row.resource_version_id, resource_row.selection_sources, resource_row.action,
|
|
1210
|
+
resource_row.origin_workspace_id, member_row.id, member_row.authorization_revision,
|
|
1211
|
+
authority.id, authority.generation, p_workspace_id, target_visibility, target_epoch,
|
|
1212
|
+
grant_row.id, grant_row.generation, grant_row.mode, grant_row.context,
|
|
1213
|
+
grant_row.session_id, grant_row.authority_epoch
|
|
1214
|
+
FROM organization_user_resource_authorities authority
|
|
1215
|
+
WHERE authority.id = resource_row.authority_id
|
|
1216
|
+
AND authority.account_id = p_account_id;
|
|
1217
|
+
END LOOP;
|
|
1218
|
+
|
|
1219
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1220
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1221
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1222
|
+
AND capability_kind = 'task_write';
|
|
1223
|
+
RETURN resource_total;
|
|
1224
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1225
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1226
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1227
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1228
|
+
AND capability_kind = 'task_write';
|
|
1229
|
+
RAISE;
|
|
1230
|
+
END
|
|
1231
|
+
$freeze_scheduled_task_personal_resources$;
|
|
1232
|
+
CREATE OR REPLACE FUNCTION admit_scheduled_task_run_personal_resources()
|
|
1233
|
+
RETURNS trigger
|
|
1234
|
+
LANGUAGE plpgsql
|
|
1235
|
+
SECURITY DEFINER
|
|
1236
|
+
SET search_path = pg_catalog, pg_temp
|
|
1237
|
+
AS $admit_scheduled_task_run_personal_resources$
|
|
1238
|
+
DECLARE
|
|
1239
|
+
task_row record;
|
|
1240
|
+
authority_row record;
|
|
1241
|
+
once_snapshot record;
|
|
1242
|
+
selected_personal_count integer;
|
|
1243
|
+
has_authority boolean := false;
|
|
1244
|
+
invalid_count integer;
|
|
1245
|
+
affected integer;
|
|
1246
|
+
BEGIN
|
|
1247
|
+
SELECT task.* INTO STRICT task_row
|
|
1248
|
+
FROM scheduled_tasks task
|
|
1249
|
+
WHERE task.id = NEW.task_id
|
|
1250
|
+
AND task.account_id = NEW.account_id
|
|
1251
|
+
AND task.workspace_id = NEW.workspace_id
|
|
1252
|
+
FOR UPDATE;
|
|
1253
|
+
|
|
1254
|
+
IF (NEW.task_authority_revision IS NULL) <> (NEW.task_execution_digest IS NULL) THEN
|
|
1255
|
+
RAISE EXCEPTION 'scheduled task run execution binding is incomplete'
|
|
1256
|
+
USING ERRCODE = '22023';
|
|
1257
|
+
END IF;
|
|
1258
|
+
IF NEW.task_authority_revision IS NOT NULL AND (
|
|
1259
|
+
NEW.task_authority_revision IS DISTINCT FROM task_row.authority_revision
|
|
1260
|
+
OR NEW.task_execution_digest IS DISTINCT FROM task_row.execution_digest
|
|
1261
|
+
) THEN
|
|
1262
|
+
RAISE EXCEPTION 'scheduled task changed after worker read'
|
|
1263
|
+
USING ERRCODE = '40001';
|
|
1264
|
+
END IF;
|
|
1265
|
+
|
|
1266
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
1267
|
+
backend_pid, transaction_id, capability_kind
|
|
1268
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'run_admit')
|
|
1269
|
+
ON CONFLICT DO NOTHING;
|
|
1270
|
+
|
|
1271
|
+
SELECT authority.* INTO authority_row
|
|
1272
|
+
FROM scheduled_task_personal_resource_authorities authority
|
|
1273
|
+
WHERE authority.task_id = NEW.task_id
|
|
1274
|
+
AND authority.task_authority_revision = task_row.authority_revision;
|
|
1275
|
+
has_authority := FOUND;
|
|
1276
|
+
IF NOT has_authority THEN
|
|
1277
|
+
SELECT count(*)::integer INTO selected_personal_count
|
|
1278
|
+
FROM (
|
|
1279
|
+
SELECT variable_set.id
|
|
1280
|
+
FROM workspace_variable_sets variable_set
|
|
1281
|
+
WHERE variable_set.id = task_row.variable_set_id
|
|
1282
|
+
AND variable_set.account_id = task_row.account_id
|
|
1283
|
+
AND variable_set.authority_scope = 'user'
|
|
1284
|
+
UNION
|
|
1285
|
+
SELECT rig.id
|
|
1286
|
+
FROM rigs rig
|
|
1287
|
+
WHERE rig.id = task_row.rig_id
|
|
1288
|
+
AND rig.account_id = task_row.account_id
|
|
1289
|
+
AND rig.authority_scope = 'user'
|
|
1290
|
+
UNION
|
|
1291
|
+
SELECT default_variable_set.id
|
|
1292
|
+
FROM rigs rig
|
|
1293
|
+
JOIN rig_versions rig_version
|
|
1294
|
+
ON rig_version.rig_id = rig.id
|
|
1295
|
+
AND rig_version.account_id = rig.account_id
|
|
1296
|
+
AND rig_version.active
|
|
1297
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
1298
|
+
rig_version.default_variable_set_ids
|
|
1299
|
+
) default_id(value)
|
|
1300
|
+
JOIN workspace_variable_sets default_variable_set
|
|
1301
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
1302
|
+
AND default_variable_set.account_id = task_row.account_id
|
|
1303
|
+
AND default_variable_set.authority_scope = 'user'
|
|
1304
|
+
WHERE rig.id = task_row.rig_id
|
|
1305
|
+
AND rig.account_id = task_row.account_id
|
|
1306
|
+
) selected;
|
|
1307
|
+
IF selected_personal_count = 0 THEN
|
|
1308
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1309
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1310
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1311
|
+
AND capability_kind = 'run_admit';
|
|
1312
|
+
RETURN NEW;
|
|
1313
|
+
END IF;
|
|
1314
|
+
END IF;
|
|
1315
|
+
|
|
1316
|
+
-- Ordinary run-history rows remain writable for paused tasks. The active
|
|
1317
|
+
-- fence applies only when this insert would admit personal-resource
|
|
1318
|
+
-- authority, and is checked while holding the task row lock before any
|
|
1319
|
+
-- admission/snapshot/once-consumption write.
|
|
1320
|
+
IF task_row.status <> 'active' THEN
|
|
1321
|
+
RAISE EXCEPTION 'scheduled task is not active' USING ERRCODE = '55000';
|
|
1322
|
+
END IF;
|
|
1323
|
+
|
|
1324
|
+
IF NOT has_authority THEN
|
|
1325
|
+
RAISE EXCEPTION 'scheduled personal-resource task has no authority snapshot'
|
|
1326
|
+
USING ERRCODE = '42501';
|
|
1327
|
+
END IF;
|
|
1328
|
+
|
|
1329
|
+
IF NEW.task_authority_revision IS NULL THEN
|
|
1330
|
+
RAISE EXCEPTION 'scheduled personal-resource run requires an execution binding'
|
|
1331
|
+
USING ERRCODE = '42501';
|
|
1332
|
+
END IF;
|
|
1333
|
+
IF authority_row.execution_digest IS DISTINCT FROM task_row.execution_digest
|
|
1334
|
+
OR authority_row.task_authority_revision IS DISTINCT FROM NEW.task_authority_revision
|
|
1335
|
+
OR authority_row.execution_digest IS DISTINCT FROM NEW.task_execution_digest
|
|
1336
|
+
THEN
|
|
1337
|
+
RAISE EXCEPTION 'scheduled personal-resource execution binding mismatch'
|
|
1338
|
+
USING ERRCODE = '42501';
|
|
1339
|
+
END IF;
|
|
1340
|
+
|
|
1341
|
+
SELECT count(*)::integer INTO invalid_count
|
|
1342
|
+
FROM scheduled_task_personal_resource_snapshots snapshot
|
|
1343
|
+
WHERE snapshot.task_id = authority_row.task_id
|
|
1344
|
+
AND snapshot.task_authority_revision = authority_row.task_authority_revision
|
|
1345
|
+
AND NOT (
|
|
1346
|
+
EXISTS (
|
|
1347
|
+
SELECT 1 FROM organization_memberships membership
|
|
1348
|
+
WHERE membership.id = snapshot.owner_organization_membership_id
|
|
1349
|
+
AND membership.account_id = snapshot.account_id
|
|
1350
|
+
AND membership.subject_id = authority_row.initiating_human_subject_id
|
|
1351
|
+
AND membership.status = 'active'
|
|
1352
|
+
AND membership.revoked_at IS NULL
|
|
1353
|
+
AND membership.authorization_revision = snapshot.membership_authorization_revision
|
|
1354
|
+
AND (membership.personal_workspace_id = snapshot.workspace_id OR EXISTS (
|
|
1355
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
1356
|
+
WHERE workspace_membership.account_id = membership.account_id
|
|
1357
|
+
AND workspace_membership.workspace_id = snapshot.workspace_id
|
|
1358
|
+
AND workspace_membership.subject_id = membership.subject_id
|
|
1359
|
+
))
|
|
1360
|
+
)
|
|
1361
|
+
AND EXISTS (
|
|
1362
|
+
SELECT 1 FROM organization_user_resource_authorities authority
|
|
1363
|
+
WHERE authority.id = snapshot.authority_id
|
|
1364
|
+
AND authority.account_id = snapshot.account_id
|
|
1365
|
+
AND authority.organization_membership_id = snapshot.owner_organization_membership_id
|
|
1366
|
+
AND authority.resource_kind = snapshot.resource_kind
|
|
1367
|
+
AND authority.resource_id = snapshot.resource_id
|
|
1368
|
+
AND authority.generation = snapshot.authority_generation
|
|
1369
|
+
AND authority.status = 'active'
|
|
1370
|
+
AND authority.revoked_at IS NULL
|
|
1371
|
+
)
|
|
1372
|
+
AND (
|
|
1373
|
+
(snapshot.resource_kind = 'variable_set' AND EXISTS (
|
|
1374
|
+
SELECT 1 FROM workspace_variable_sets variable_set
|
|
1375
|
+
WHERE variable_set.id = snapshot.resource_id
|
|
1376
|
+
AND variable_set.account_id = snapshot.account_id
|
|
1377
|
+
AND variable_set.authority_scope = 'user'
|
|
1378
|
+
AND variable_set.authority_id = snapshot.authority_id
|
|
1379
|
+
AND variable_set.owner_organization_membership_id =
|
|
1380
|
+
snapshot.owner_organization_membership_id
|
|
1381
|
+
)) OR (snapshot.resource_kind = 'rig' AND EXISTS (
|
|
1382
|
+
SELECT 1 FROM rigs rig
|
|
1383
|
+
JOIN rig_versions rig_version
|
|
1384
|
+
ON rig_version.id = snapshot.resource_version_id
|
|
1385
|
+
AND rig_version.rig_id = rig.id
|
|
1386
|
+
AND rig_version.account_id = rig.account_id
|
|
1387
|
+
WHERE rig.id = snapshot.resource_id
|
|
1388
|
+
AND rig.account_id = snapshot.account_id
|
|
1389
|
+
AND rig.authority_scope = 'user'
|
|
1390
|
+
AND rig.authority_id = snapshot.authority_id
|
|
1391
|
+
AND rig.owner_organization_membership_id =
|
|
1392
|
+
snapshot.owner_organization_membership_id
|
|
1393
|
+
))
|
|
1394
|
+
)
|
|
1395
|
+
AND EXISTS (
|
|
1396
|
+
SELECT 1 FROM organization_user_resource_grants grant_value
|
|
1397
|
+
WHERE grant_value.id = snapshot.grant_id
|
|
1398
|
+
AND grant_value.account_id = snapshot.account_id
|
|
1399
|
+
AND grant_value.authority_id = snapshot.authority_id
|
|
1400
|
+
AND grant_value.owner_organization_membership_id =
|
|
1401
|
+
snapshot.owner_organization_membership_id
|
|
1402
|
+
AND grant_value.workspace_id = snapshot.target_workspace_id
|
|
1403
|
+
AND grant_value.action = snapshot.action
|
|
1404
|
+
AND grant_value.mode = snapshot.grant_mode
|
|
1405
|
+
AND grant_value.context = snapshot.grant_context
|
|
1406
|
+
AND grant_value.generation = snapshot.grant_generation
|
|
1407
|
+
AND grant_value.session_id IS NOT DISTINCT FROM snapshot.grant_session_id
|
|
1408
|
+
AND grant_value.authority_epoch IS NOT DISTINCT FROM snapshot.grant_authority_epoch
|
|
1409
|
+
AND grant_value.status = 'active'
|
|
1410
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
1411
|
+
)
|
|
1412
|
+
AND (
|
|
1413
|
+
authority_row.target_session_id IS NULL
|
|
1414
|
+
OR EXISTS (
|
|
1415
|
+
SELECT 1 FROM sessions session_value
|
|
1416
|
+
WHERE session_value.id = authority_row.target_session_id
|
|
1417
|
+
AND session_value.account_id = authority_row.account_id
|
|
1418
|
+
AND session_value.workspace_id = authority_row.workspace_id
|
|
1419
|
+
AND session_value.status <> 'cancelled'
|
|
1420
|
+
AND session_value.visibility = authority_row.session_visibility
|
|
1421
|
+
AND session_value.authority_epoch = authority_row.session_authority_epoch
|
|
1422
|
+
)
|
|
1423
|
+
)
|
|
1424
|
+
);
|
|
1425
|
+
IF invalid_count <> 0 THEN
|
|
1426
|
+
RAISE EXCEPTION 'scheduled personal-resource authority snapshot is no longer live'
|
|
1427
|
+
USING ERRCODE = '42501';
|
|
1428
|
+
END IF;
|
|
1429
|
+
|
|
1430
|
+
INSERT INTO scheduled_task_run_personal_resource_admissions (
|
|
1431
|
+
run_id, task_id, task_authority_revision, account_id, workspace_id,
|
|
1432
|
+
initiating_human_subject_id, owner_organization_membership_id,
|
|
1433
|
+
membership_authorization_revision, target_session_id, session_visibility,
|
|
1434
|
+
session_authority_epoch, execution_digest, resource_count
|
|
1435
|
+
) VALUES (
|
|
1436
|
+
NEW.id, authority_row.task_id, authority_row.task_authority_revision,
|
|
1437
|
+
authority_row.account_id, authority_row.workspace_id,
|
|
1438
|
+
authority_row.initiating_human_subject_id,
|
|
1439
|
+
authority_row.owner_organization_membership_id,
|
|
1440
|
+
authority_row.membership_authorization_revision, authority_row.target_session_id,
|
|
1441
|
+
authority_row.session_visibility, authority_row.session_authority_epoch,
|
|
1442
|
+
authority_row.execution_digest, authority_row.resource_count
|
|
1443
|
+
);
|
|
1444
|
+
|
|
1445
|
+
INSERT INTO scheduled_task_run_personal_resource_snapshots (
|
|
1446
|
+
run_id, task_id, task_authority_revision, account_id, workspace_id,
|
|
1447
|
+
resource_kind, resource_id, resource_version_id, selection_sources, action,
|
|
1448
|
+
origin_workspace_id, owner_organization_membership_id,
|
|
1449
|
+
membership_authorization_revision, authority_id, authority_generation,
|
|
1450
|
+
target_workspace_id, session_visibility, session_authority_epoch,
|
|
1451
|
+
grant_id, grant_generation, grant_mode, grant_context,
|
|
1452
|
+
grant_session_id, grant_authority_epoch
|
|
1453
|
+
)
|
|
1454
|
+
SELECT NEW.id, snapshot.task_id, snapshot.task_authority_revision,
|
|
1455
|
+
snapshot.account_id, snapshot.workspace_id, snapshot.resource_kind,
|
|
1456
|
+
snapshot.resource_id, snapshot.resource_version_id, snapshot.selection_sources,
|
|
1457
|
+
snapshot.action, snapshot.origin_workspace_id,
|
|
1458
|
+
snapshot.owner_organization_membership_id,
|
|
1459
|
+
snapshot.membership_authorization_revision, snapshot.authority_id,
|
|
1460
|
+
snapshot.authority_generation, snapshot.target_workspace_id,
|
|
1461
|
+
snapshot.session_visibility, snapshot.session_authority_epoch,
|
|
1462
|
+
snapshot.grant_id, snapshot.grant_generation, snapshot.grant_mode,
|
|
1463
|
+
snapshot.grant_context, snapshot.grant_session_id, snapshot.grant_authority_epoch
|
|
1464
|
+
FROM scheduled_task_personal_resource_snapshots snapshot
|
|
1465
|
+
WHERE snapshot.task_id = authority_row.task_id
|
|
1466
|
+
AND snapshot.task_authority_revision = authority_row.task_authority_revision;
|
|
1467
|
+
|
|
1468
|
+
FOR once_snapshot IN
|
|
1469
|
+
SELECT snapshot.*
|
|
1470
|
+
FROM scheduled_task_run_personal_resource_snapshots snapshot
|
|
1471
|
+
WHERE snapshot.run_id = NEW.id
|
|
1472
|
+
AND snapshot.grant_mode = 'once'
|
|
1473
|
+
ORDER BY snapshot.grant_id
|
|
1474
|
+
LOOP
|
|
1475
|
+
UPDATE organization_user_resource_grants grant_value
|
|
1476
|
+
SET status = 'consumed', updated_at = clock_timestamp()
|
|
1477
|
+
WHERE grant_value.id = once_snapshot.grant_id
|
|
1478
|
+
AND grant_value.account_id = once_snapshot.account_id
|
|
1479
|
+
AND grant_value.authority_id = once_snapshot.authority_id
|
|
1480
|
+
AND grant_value.generation = once_snapshot.grant_generation
|
|
1481
|
+
AND grant_value.status = 'active';
|
|
1482
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
1483
|
+
IF affected <> 1 THEN
|
|
1484
|
+
RAISE EXCEPTION 'scheduled occurrence once grant lost its first-use race'
|
|
1485
|
+
USING ERRCODE = '40001';
|
|
1486
|
+
END IF;
|
|
1487
|
+
INSERT INTO scheduled_task_run_personal_resource_once_receipts (
|
|
1488
|
+
grant_id, run_id, account_id, workspace_id, authority_id,
|
|
1489
|
+
authority_generation, grant_generation
|
|
1490
|
+
) VALUES (
|
|
1491
|
+
once_snapshot.grant_id, NEW.id, once_snapshot.account_id,
|
|
1492
|
+
once_snapshot.workspace_id, once_snapshot.authority_id,
|
|
1493
|
+
once_snapshot.authority_generation, once_snapshot.grant_generation
|
|
1494
|
+
);
|
|
1495
|
+
END LOOP;
|
|
1496
|
+
|
|
1497
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1498
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1499
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1500
|
+
AND capability_kind = 'run_admit';
|
|
1501
|
+
RETURN NEW;
|
|
1502
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1503
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1504
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1505
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1506
|
+
AND capability_kind = 'run_admit';
|
|
1507
|
+
RAISE;
|
|
1508
|
+
END
|
|
1509
|
+
$admit_scheduled_task_run_personal_resources$;
|
|
1510
|
+
|
|
1511
|
+
DO $fixed_search_paths$
|
|
1512
|
+
DECLARE
|
|
1513
|
+
data_schema text := current_schema();
|
|
1514
|
+
BEGIN
|
|
1515
|
+
EXECUTE format(
|
|
1516
|
+
'ALTER FUNCTION %1$I.list_self_user_resource_authorities(uuid) '
|
|
1517
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1518
|
+
data_schema
|
|
1519
|
+
);
|
|
1520
|
+
EXECUTE format(
|
|
1521
|
+
'ALTER FUNCTION %1$I.issue_self_user_resource_grant('
|
|
1522
|
+
|| 'uuid, uuid, uuid, text, text, text, uuid, boolean) '
|
|
1523
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1524
|
+
data_schema
|
|
1525
|
+
);
|
|
1526
|
+
EXECUTE format(
|
|
1527
|
+
'ALTER FUNCTION %1$I.revoke_self_user_resource_grant(uuid, uuid) '
|
|
1528
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1529
|
+
data_schema
|
|
1530
|
+
);
|
|
1531
|
+
EXECUTE format(
|
|
1532
|
+
'ALTER FUNCTION %1$I.authorize_session_attempt_personal_resource_reads(uuid, uuid, uuid) '
|
|
1533
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1534
|
+
data_schema
|
|
1535
|
+
);
|
|
1536
|
+
EXECUTE format(
|
|
1537
|
+
'ALTER FUNCTION %1$I.freeze_scheduled_task_personal_resources(uuid, uuid, uuid, bigint) '
|
|
1538
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1539
|
+
data_schema
|
|
1540
|
+
);
|
|
1541
|
+
EXECUTE format(
|
|
1542
|
+
'ALTER FUNCTION %1$I.admit_scheduled_task_run_personal_resources() '
|
|
1543
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1544
|
+
data_schema
|
|
1545
|
+
);
|
|
1546
|
+
END
|
|
1547
|
+
$fixed_search_paths$;
|
|
1548
|
+
REVOKE ALL ON FUNCTION freeze_scheduled_task_personal_resources(uuid, uuid, uuid, bigint) FROM PUBLIC;
|
|
1549
|
+
REVOKE ALL ON FUNCTION admit_scheduled_task_run_personal_resources() FROM PUBLIC;
|
|
1550
|
+
COMMENT ON FUNCTION list_self_user_resource_authorities(uuid) IS
|
|
1551
|
+
'Owner-only opaque generic user-resource authority/grant lifecycle listing.';
|
|
1552
|
+
COMMENT ON FUNCTION issue_self_user_resource_grant(uuid, uuid, uuid, text, text, text, uuid, boolean) IS
|
|
1553
|
+
'Owner-only idempotent user-resource grant issue with server-derived owner/session fences.';
|
|
1554
|
+
COMMENT ON FUNCTION revoke_self_user_resource_grant(uuid, uuid) IS
|
|
1555
|
+
'Owner-only immediate idempotent user-resource grant revocation.';
|