@opengeni/db 1.0.1 → 3.2.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
- package/dist/chunk-2EH4WRHL.js.map +1 -0
- package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
- package/dist/chunk-6HMM2R5T.js +390 -0
- package/dist/chunk-6HMM2R5T.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
- package/dist/chunk-AZHW7R5V.js.map +1 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
- package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
- package/dist/chunk-KRLPEMLH.js.map +1 -0
- package/dist/chunk-NAB6FKFN.js +516 -0
- package/dist/chunk-NAB6FKFN.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-UZNXG44K.js +4508 -0
- package/dist/chunk-UZNXG44K.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +85 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +23 -4
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1606 -81
- package/dist/index.js +21898 -8147
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +29 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +43 -4
- package/dist/session-realtime-ledger.d.ts +2 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +268 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +28 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
- package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
- package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
- package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
- package/drizzle/0346_document_migration_audit_surface.sql +454 -0
- package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +167 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +87 -2
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +11676 -1550
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +995 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +905 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1317 -72
- package/src/schema.ts +3316 -428
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +441 -85
- package/src/session-realtime-context.ts +4 -6
- package/src/session-realtime-ledger.ts +28 -14
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +410 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +867 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +138 -11
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Align the private-session read predicate with the exact personal-workspace
|
|
3
|
+
-- authority already enforced by the 0303 transition/fork lifecycle functions.
|
|
4
|
+
-- A managed personal workspace deliberately has no workspace_memberships row,
|
|
5
|
+
-- so requiring one here made an owning human lose the session immediately
|
|
6
|
+
-- after a valid transition to user_private.
|
|
7
|
+
|
|
8
|
+
DO $session_private_actor_personal_workspace_reads$
|
|
9
|
+
DECLARE
|
|
10
|
+
data_schema text := current_schema();
|
|
11
|
+
BEGIN
|
|
12
|
+
EXECUTE format($ddl$
|
|
13
|
+
CREATE OR REPLACE FUNCTION %1$I.session_private_actor_visible(
|
|
14
|
+
p_account_id uuid,
|
|
15
|
+
p_workspace_id uuid,
|
|
16
|
+
p_owner_organization_membership_id uuid,
|
|
17
|
+
p_owner_subject_id text
|
|
18
|
+
) RETURNS boolean
|
|
19
|
+
LANGUAGE plpgsql
|
|
20
|
+
STABLE
|
|
21
|
+
SECURITY DEFINER
|
|
22
|
+
SET search_path = pg_catalog, %1$I, pg_temp
|
|
23
|
+
AS $body$
|
|
24
|
+
DECLARE
|
|
25
|
+
actor_subject_id text := nullif(
|
|
26
|
+
pg_catalog.current_setting('opengeni.subject_id', true), ''
|
|
27
|
+
);
|
|
28
|
+
initiating_human_subject_id text := nullif(
|
|
29
|
+
pg_catalog.current_setting('opengeni.initiating_human_subject_id', true), ''
|
|
30
|
+
);
|
|
31
|
+
previous_lifecycle_marker text := pg_catalog.current_setting(
|
|
32
|
+
'opengeni.organization_tenancy_lifecycle', true
|
|
33
|
+
);
|
|
34
|
+
visible boolean := false;
|
|
35
|
+
BEGIN
|
|
36
|
+
IF p_account_id IS NULL
|
|
37
|
+
OR p_workspace_id IS NULL
|
|
38
|
+
OR p_owner_organization_membership_id IS NULL
|
|
39
|
+
OR p_owner_subject_id IS NULL
|
|
40
|
+
OR (
|
|
41
|
+
actor_subject_id IS DISTINCT FROM p_owner_subject_id
|
|
42
|
+
AND initiating_human_subject_id IS DISTINCT FROM p_owner_subject_id
|
|
43
|
+
)
|
|
44
|
+
THEN
|
|
45
|
+
RETURN false;
|
|
46
|
+
END IF;
|
|
47
|
+
PERFORM pg_catalog.set_config(
|
|
48
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
49
|
+
'session_visibility_activation',
|
|
50
|
+
true
|
|
51
|
+
);
|
|
52
|
+
SELECT EXISTS (
|
|
53
|
+
SELECT 1
|
|
54
|
+
FROM organization_memberships membership
|
|
55
|
+
WHERE membership.account_id = p_account_id
|
|
56
|
+
AND membership.id = p_owner_organization_membership_id
|
|
57
|
+
AND membership.subject_id = p_owner_subject_id
|
|
58
|
+
AND membership.status = 'active'
|
|
59
|
+
AND (
|
|
60
|
+
membership.personal_workspace_id = p_workspace_id
|
|
61
|
+
OR EXISTS (
|
|
62
|
+
SELECT 1
|
|
63
|
+
FROM workspace_memberships workspace_membership
|
|
64
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
65
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
66
|
+
AND workspace_membership.subject_id = p_owner_subject_id
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
) INTO visible;
|
|
70
|
+
PERFORM pg_catalog.set_config(
|
|
71
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
72
|
+
CASE WHEN previous_lifecycle_marker IS NULL THEN '' ELSE previous_lifecycle_marker END,
|
|
73
|
+
true
|
|
74
|
+
);
|
|
75
|
+
RETURN visible;
|
|
76
|
+
EXCEPTION WHEN OTHERS THEN
|
|
77
|
+
PERFORM pg_catalog.set_config(
|
|
78
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
79
|
+
CASE WHEN previous_lifecycle_marker IS NULL THEN '' ELSE previous_lifecycle_marker END,
|
|
80
|
+
true
|
|
81
|
+
);
|
|
82
|
+
RAISE;
|
|
83
|
+
END;
|
|
84
|
+
$body$;
|
|
85
|
+
$ddl$, data_schema);
|
|
86
|
+
EXECUTE format(
|
|
87
|
+
'REVOKE ALL ON FUNCTION %I.session_private_actor_visible(uuid,uuid,uuid,text) FROM PUBLIC',
|
|
88
|
+
data_schema
|
|
89
|
+
);
|
|
90
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
91
|
+
EXECUTE format(
|
|
92
|
+
'GRANT EXECUTE ON FUNCTION %I.session_private_actor_visible(uuid,uuid,uuid,text) TO opengeni_app',
|
|
93
|
+
data_schema
|
|
94
|
+
);
|
|
95
|
+
END IF;
|
|
96
|
+
END
|
|
97
|
+
$session_private_actor_personal_workspace_reads$;
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Harden owner-managed personal-resource grants behind the activated session-
|
|
3
|
+
-- tenancy product boundary. Old lifecycle signatures are revoked from the
|
|
4
|
+
-- runtime role so rolling old callers fail closed rather than retain ambient
|
|
5
|
+
-- cross-workspace inventory or caller-selected action authority.
|
|
6
|
+
|
|
7
|
+
SET LOCAL lock_timeout = '5s';
|
|
8
|
+
SET LOCAL statement_timeout = '10min';
|
|
9
|
+
|
|
10
|
+
-- This table is FORCE-RLS. Production migrations run as its NOSUPERUSER,
|
|
11
|
+
-- NOBYPASSRLS owner without tenant GUCs, so the owner must be made visible for
|
|
12
|
+
-- this exact transactional backfill window. Ordinary roles remain policy-bound.
|
|
13
|
+
ALTER TABLE "organization_user_resource_grants" NO FORCE ROW LEVEL SECURITY;
|
|
14
|
+
ALTER TABLE "organization_user_resource_authorities" NO FORCE ROW LEVEL SECURITY;
|
|
15
|
+
|
|
16
|
+
-- Sparse upgrade fixtures may predate the action-contract trigger. When it is
|
|
17
|
+
-- present, retain its exact enabled mode across this transactional maintenance
|
|
18
|
+
-- window instead of assuming the ordinary-origin state.
|
|
19
|
+
DO $migration$
|
|
20
|
+
DECLARE
|
|
21
|
+
prior_trigger_state text;
|
|
22
|
+
BEGIN
|
|
23
|
+
SELECT trigger_value.tgenabled::text
|
|
24
|
+
INTO prior_trigger_state
|
|
25
|
+
FROM pg_catalog.pg_trigger trigger_value
|
|
26
|
+
WHERE trigger_value.tgrelid = 'organization_user_resource_grants'::regclass
|
|
27
|
+
AND trigger_value.tgname = 'organization_user_resource_grants_action_contract'
|
|
28
|
+
AND NOT trigger_value.tgisinternal;
|
|
29
|
+
|
|
30
|
+
PERFORM pg_catalog.set_config(
|
|
31
|
+
'opengeni.migration_0305_action_contract_trigger_state',
|
|
32
|
+
coalesce(prior_trigger_state, 'missing'),
|
|
33
|
+
true
|
|
34
|
+
);
|
|
35
|
+
IF prior_trigger_state IS NOT NULL AND prior_trigger_state <> 'D' THEN
|
|
36
|
+
ALTER TABLE "organization_user_resource_grants"
|
|
37
|
+
DISABLE TRIGGER organization_user_resource_grants_action_contract;
|
|
38
|
+
END IF;
|
|
39
|
+
END
|
|
40
|
+
$migration$;
|
|
41
|
+
|
|
42
|
+
UPDATE "organization_user_resource_grants"
|
|
43
|
+
SET status = 'expired', updated_at = clock_timestamp()
|
|
44
|
+
WHERE status = 'active' AND expires_at IS NOT NULL AND expires_at <= clock_timestamp();
|
|
45
|
+
|
|
46
|
+
-- Old generic issue callers could author an arbitrary action. Preserve those
|
|
47
|
+
-- rows as history, but settle every still-active mismatch so it can never be
|
|
48
|
+
-- admitted as authority under a different resource kind.
|
|
49
|
+
UPDATE "organization_user_resource_grants" grant_value
|
|
50
|
+
SET status = 'revoked', revoked_at = coalesce(grant_value.revoked_at, clock_timestamp()),
|
|
51
|
+
generation = grant_value.generation + 1, updated_at = clock_timestamp()
|
|
52
|
+
FROM "organization_user_resource_authorities" authority
|
|
53
|
+
WHERE authority.id = grant_value.authority_id
|
|
54
|
+
AND authority.account_id = grant_value.account_id
|
|
55
|
+
AND grant_value.status = 'active'
|
|
56
|
+
AND grant_value.action IS DISTINCT FROM CASE authority.resource_kind
|
|
57
|
+
WHEN 'connection' THEN 'connection.use'
|
|
58
|
+
WHEN 'document' THEN 'document.read'
|
|
59
|
+
WHEN 'variable_set' THEN 'variable_set.use'
|
|
60
|
+
WHEN 'rig' THEN 'rig.use'
|
|
61
|
+
WHEN 'connected_machine' THEN 'connected_machine.use'
|
|
62
|
+
ELSE NULL
|
|
63
|
+
END;
|
|
64
|
+
|
|
65
|
+
ALTER TABLE "organization_user_resource_grants" FORCE ROW LEVEL SECURITY;
|
|
66
|
+
ALTER TABLE "organization_user_resource_authorities" FORCE ROW LEVEL SECURITY;
|
|
67
|
+
DO $migration$
|
|
68
|
+
DECLARE
|
|
69
|
+
prior_trigger_state text := pg_catalog.current_setting(
|
|
70
|
+
'opengeni.migration_0305_action_contract_trigger_state', true
|
|
71
|
+
);
|
|
72
|
+
BEGIN
|
|
73
|
+
IF prior_trigger_state = 'O' THEN
|
|
74
|
+
ALTER TABLE "organization_user_resource_grants"
|
|
75
|
+
ENABLE TRIGGER organization_user_resource_grants_action_contract;
|
|
76
|
+
ELSIF prior_trigger_state = 'D' THEN
|
|
77
|
+
ALTER TABLE "organization_user_resource_grants"
|
|
78
|
+
DISABLE TRIGGER organization_user_resource_grants_action_contract;
|
|
79
|
+
ELSIF prior_trigger_state = 'R' THEN
|
|
80
|
+
ALTER TABLE "organization_user_resource_grants"
|
|
81
|
+
ENABLE REPLICA TRIGGER organization_user_resource_grants_action_contract;
|
|
82
|
+
ELSIF prior_trigger_state = 'A' THEN
|
|
83
|
+
ALTER TABLE "organization_user_resource_grants"
|
|
84
|
+
ENABLE ALWAYS TRIGGER organization_user_resource_grants_action_contract;
|
|
85
|
+
ELSIF prior_trigger_state IS DISTINCT FROM 'missing' THEN
|
|
86
|
+
RAISE EXCEPTION 'invalid saved action-contract trigger state'
|
|
87
|
+
USING ERRCODE = '55000';
|
|
88
|
+
END IF;
|
|
89
|
+
END
|
|
90
|
+
$migration$;
|
|
91
|
+
|
|
92
|
+
-- Runtime management remains lifecycle-only under FORCE RLS. The application
|
|
93
|
+
-- role still has zero table privileges; these policy branches are reachable
|
|
94
|
+
-- only inside the constrained SECURITY DEFINER routines below.
|
|
95
|
+
ALTER POLICY organization_tenancy_lifecycle ON "organization_memberships"
|
|
96
|
+
USING (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
97
|
+
IN (
|
|
98
|
+
'managed_human_provisioning',
|
|
99
|
+
'session_visibility_activation',
|
|
100
|
+
'organization_membership_lifecycle',
|
|
101
|
+
'personal_resource_grant_management'
|
|
102
|
+
))
|
|
103
|
+
WITH CHECK (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
104
|
+
IN (
|
|
105
|
+
'managed_human_provisioning',
|
|
106
|
+
'session_visibility_activation',
|
|
107
|
+
'organization_membership_lifecycle',
|
|
108
|
+
'personal_resource_grant_management'
|
|
109
|
+
));
|
|
110
|
+
ALTER POLICY organization_tenancy_lifecycle ON "organization_user_resource_authorities"
|
|
111
|
+
USING (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
112
|
+
IN (
|
|
113
|
+
'managed_human_provisioning',
|
|
114
|
+
'organization_membership_lifecycle',
|
|
115
|
+
'personal_resource_grant_management'
|
|
116
|
+
))
|
|
117
|
+
WITH CHECK (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
118
|
+
IN (
|
|
119
|
+
'managed_human_provisioning',
|
|
120
|
+
'organization_membership_lifecycle',
|
|
121
|
+
'personal_resource_grant_management'
|
|
122
|
+
));
|
|
123
|
+
ALTER POLICY organization_tenancy_lifecycle ON "organization_user_resource_grants"
|
|
124
|
+
USING (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
125
|
+
IN (
|
|
126
|
+
'managed_human_provisioning',
|
|
127
|
+
'session_visibility_activation',
|
|
128
|
+
'organization_membership_lifecycle',
|
|
129
|
+
'personal_resource_grant_management'
|
|
130
|
+
))
|
|
131
|
+
WITH CHECK (current_setting('opengeni.organization_tenancy_lifecycle', true)
|
|
132
|
+
IN (
|
|
133
|
+
'managed_human_provisioning',
|
|
134
|
+
'session_visibility_activation',
|
|
135
|
+
'organization_membership_lifecycle',
|
|
136
|
+
'personal_resource_grant_management'
|
|
137
|
+
));
|
|
138
|
+
|
|
139
|
+
CREATE OR REPLACE FUNCTION list_self_user_resource_authorities(
|
|
140
|
+
p_account_id uuid,
|
|
141
|
+
p_workspace_id uuid,
|
|
142
|
+
p_resource_kind text,
|
|
143
|
+
p_after_authority_id uuid DEFAULT NULL,
|
|
144
|
+
p_limit integer DEFAULT 50
|
|
145
|
+
) RETURNS TABLE (
|
|
146
|
+
authority_id uuid, organization_id uuid, resource_kind text, resource_id uuid,
|
|
147
|
+
origin_workspace_id uuid, authority_generation bigint, authority_status text,
|
|
148
|
+
grant_id uuid, target_workspace_id uuid, target_session_id uuid, action text,
|
|
149
|
+
grant_mode text, grant_context text, authority_epoch integer,
|
|
150
|
+
grant_generation bigint, grant_status text, expires_at timestamptz
|
|
151
|
+
)
|
|
152
|
+
LANGUAGE plpgsql
|
|
153
|
+
SECURITY DEFINER
|
|
154
|
+
SET search_path FROM CURRENT
|
|
155
|
+
AS $body$
|
|
156
|
+
#variable_conflict use_column
|
|
157
|
+
DECLARE
|
|
158
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
159
|
+
owner_membership_id uuid;
|
|
160
|
+
cursor_created_at timestamptz;
|
|
161
|
+
cursor_id uuid;
|
|
162
|
+
BEGIN
|
|
163
|
+
PERFORM set_config(
|
|
164
|
+
'opengeni.organization_tenancy_lifecycle', 'personal_resource_grant_management', true
|
|
165
|
+
);
|
|
166
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
167
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
168
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
169
|
+
)::uuid
|
|
170
|
+
OR caller_subject IS NULL
|
|
171
|
+
THEN
|
|
172
|
+
RAISE EXCEPTION 'user-resource authority scope mismatch' USING ERRCODE = '42501';
|
|
173
|
+
END IF;
|
|
174
|
+
IF p_resource_kind IS NULL OR p_resource_kind NOT IN (
|
|
175
|
+
'connection', 'document', 'variable_set', 'rig', 'connected_machine'
|
|
176
|
+
) OR p_limit IS NULL OR p_limit NOT BETWEEN 1 AND 101 THEN
|
|
177
|
+
RAISE EXCEPTION 'invalid user-resource authority page' USING ERRCODE = '22023';
|
|
178
|
+
END IF;
|
|
179
|
+
IF NOT EXISTS (
|
|
180
|
+
SELECT 1 FROM session_tenancy_activations activation
|
|
181
|
+
WHERE activation.account_id = p_account_id AND activation.activation_version = 1
|
|
182
|
+
) THEN
|
|
183
|
+
RAISE EXCEPTION 'session tenancy product is not activated' USING ERRCODE = '42501';
|
|
184
|
+
END IF;
|
|
185
|
+
|
|
186
|
+
SELECT membership.id INTO owner_membership_id
|
|
187
|
+
FROM organization_memberships membership
|
|
188
|
+
WHERE membership.account_id = p_account_id
|
|
189
|
+
AND membership.subject_id = caller_subject
|
|
190
|
+
AND membership.status = 'active'
|
|
191
|
+
AND membership.revoked_at IS NULL
|
|
192
|
+
FOR SHARE;
|
|
193
|
+
IF NOT FOUND THEN
|
|
194
|
+
RAISE EXCEPTION 'owner membership not found or access denied' USING ERRCODE = '42501';
|
|
195
|
+
END IF;
|
|
196
|
+
|
|
197
|
+
PERFORM 1 FROM workspaces workspace_value
|
|
198
|
+
WHERE workspace_value.id = p_workspace_id
|
|
199
|
+
AND workspace_value.account_id = p_account_id
|
|
200
|
+
AND EXISTS (
|
|
201
|
+
SELECT 1 FROM organization_memberships membership
|
|
202
|
+
WHERE membership.id = owner_membership_id
|
|
203
|
+
AND (
|
|
204
|
+
membership.personal_workspace_id = p_workspace_id
|
|
205
|
+
OR EXISTS (
|
|
206
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
207
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
208
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
209
|
+
AND workspace_membership.subject_id = caller_subject
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
FOR SHARE;
|
|
214
|
+
IF NOT FOUND THEN
|
|
215
|
+
RAISE EXCEPTION 'owner lacks current route-workspace access' USING ERRCODE = '42501';
|
|
216
|
+
END IF;
|
|
217
|
+
|
|
218
|
+
IF p_after_authority_id IS NOT NULL THEN
|
|
219
|
+
SELECT authority.created_at, authority.id
|
|
220
|
+
INTO cursor_created_at, cursor_id
|
|
221
|
+
FROM organization_user_resource_authorities authority
|
|
222
|
+
WHERE authority.id = p_after_authority_id
|
|
223
|
+
AND authority.account_id = p_account_id
|
|
224
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
225
|
+
AND authority.resource_kind = p_resource_kind;
|
|
226
|
+
IF NOT FOUND THEN
|
|
227
|
+
RAISE EXCEPTION 'invalid user-resource authority cursor' USING ERRCODE = '22023';
|
|
228
|
+
END IF;
|
|
229
|
+
END IF;
|
|
230
|
+
|
|
231
|
+
RETURN QUERY
|
|
232
|
+
WITH authority_page AS MATERIALIZED (
|
|
233
|
+
SELECT authority.*
|
|
234
|
+
FROM organization_user_resource_authorities authority
|
|
235
|
+
WHERE authority.account_id = p_account_id
|
|
236
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
237
|
+
AND authority.resource_kind = p_resource_kind
|
|
238
|
+
AND (
|
|
239
|
+
p_after_authority_id IS NULL
|
|
240
|
+
OR (authority.created_at, authority.id) > (cursor_created_at, cursor_id)
|
|
241
|
+
)
|
|
242
|
+
ORDER BY authority.created_at, authority.id
|
|
243
|
+
LIMIT p_limit
|
|
244
|
+
)
|
|
245
|
+
SELECT authority.id, authority.account_id, authority.resource_kind,
|
|
246
|
+
authority.resource_id, authority.origin_workspace_id, authority.generation,
|
|
247
|
+
authority.status, grant_value.id, grant_value.workspace_id,
|
|
248
|
+
grant_value.session_id, grant_value.action, grant_value.mode,
|
|
249
|
+
grant_value.context, grant_value.authority_epoch, grant_value.generation,
|
|
250
|
+
CASE
|
|
251
|
+
WHEN grant_value.status = 'active'
|
|
252
|
+
AND grant_value.expires_at IS NOT NULL
|
|
253
|
+
AND grant_value.expires_at <= clock_timestamp()
|
|
254
|
+
THEN 'expired'
|
|
255
|
+
ELSE grant_value.status
|
|
256
|
+
END,
|
|
257
|
+
grant_value.expires_at
|
|
258
|
+
FROM authority_page authority
|
|
259
|
+
LEFT JOIN organization_user_resource_grants grant_value
|
|
260
|
+
ON grant_value.account_id = authority.account_id
|
|
261
|
+
AND grant_value.authority_id = authority.id
|
|
262
|
+
AND grant_value.owner_organization_membership_id = owner_membership_id
|
|
263
|
+
AND grant_value.workspace_id = p_workspace_id
|
|
264
|
+
AND grant_value.action = CASE authority.resource_kind
|
|
265
|
+
WHEN 'connection' THEN 'connection.use'
|
|
266
|
+
WHEN 'document' THEN 'document.read'
|
|
267
|
+
WHEN 'variable_set' THEN 'variable_set.use'
|
|
268
|
+
WHEN 'rig' THEN 'rig.use'
|
|
269
|
+
WHEN 'connected_machine' THEN 'connected_machine.use'
|
|
270
|
+
ELSE NULL
|
|
271
|
+
END
|
|
272
|
+
ORDER BY authority.created_at, authority.id, grant_value.created_at, grant_value.id;
|
|
273
|
+
END
|
|
274
|
+
$body$;
|
|
275
|
+
|
|
276
|
+
CREATE OR REPLACE FUNCTION issue_self_user_resource_grant(
|
|
277
|
+
p_account_id uuid,
|
|
278
|
+
p_authority_id uuid,
|
|
279
|
+
p_workspace_id uuid,
|
|
280
|
+
p_resource_kind text,
|
|
281
|
+
p_mode text,
|
|
282
|
+
p_context text,
|
|
283
|
+
p_session_id uuid DEFAULT NULL,
|
|
284
|
+
p_expected_authority_epoch integer DEFAULT NULL,
|
|
285
|
+
p_workspace_shared_acknowledged boolean DEFAULT false
|
|
286
|
+
) RETURNS TABLE (
|
|
287
|
+
grant_id uuid, organization_id uuid, authority_generation bigint,
|
|
288
|
+
target_workspace_id uuid, target_session_id uuid, action text,
|
|
289
|
+
grant_mode text, grant_context text, authority_epoch integer,
|
|
290
|
+
grant_generation bigint, grant_status text, expires_at timestamptz
|
|
291
|
+
)
|
|
292
|
+
LANGUAGE plpgsql
|
|
293
|
+
SECURITY DEFINER
|
|
294
|
+
SET search_path FROM CURRENT
|
|
295
|
+
AS $body$
|
|
296
|
+
#variable_conflict use_column
|
|
297
|
+
DECLARE
|
|
298
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
299
|
+
owner_membership_id uuid;
|
|
300
|
+
target_epoch integer;
|
|
301
|
+
target_visibility text;
|
|
302
|
+
canonical_action text;
|
|
303
|
+
BEGIN
|
|
304
|
+
PERFORM set_config(
|
|
305
|
+
'opengeni.organization_tenancy_lifecycle', 'personal_resource_grant_management', true
|
|
306
|
+
);
|
|
307
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
308
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
309
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
310
|
+
)::uuid
|
|
311
|
+
OR caller_subject IS NULL
|
|
312
|
+
THEN
|
|
313
|
+
RAISE EXCEPTION 'user-resource grant scope mismatch' USING ERRCODE = '42501';
|
|
314
|
+
END IF;
|
|
315
|
+
canonical_action := CASE p_resource_kind
|
|
316
|
+
WHEN 'connection' THEN 'connection.use'
|
|
317
|
+
WHEN 'document' THEN 'document.read'
|
|
318
|
+
WHEN 'variable_set' THEN 'variable_set.use'
|
|
319
|
+
WHEN 'rig' THEN 'rig.use'
|
|
320
|
+
WHEN 'connected_machine' THEN 'connected_machine.use'
|
|
321
|
+
ELSE NULL
|
|
322
|
+
END;
|
|
323
|
+
IF p_resource_kind IS NULL OR canonical_action IS NULL
|
|
324
|
+
OR p_mode IS NULL OR p_mode NOT IN ('session', 'always')
|
|
325
|
+
OR p_context IS NULL OR p_context NOT IN ('user_private', 'workspace_shared')
|
|
326
|
+
OR (p_mode = 'always' AND (p_session_id IS NOT NULL OR p_expected_authority_epoch IS NOT NULL))
|
|
327
|
+
OR (p_mode = 'session' AND (p_session_id IS NULL OR p_expected_authority_epoch IS NULL))
|
|
328
|
+
OR coalesce(p_expected_authority_epoch, 1) <= 0
|
|
329
|
+
THEN
|
|
330
|
+
RAISE EXCEPTION 'invalid user-resource grant request' USING ERRCODE = '22023';
|
|
331
|
+
END IF;
|
|
332
|
+
IF p_context = 'workspace_shared' AND p_workspace_shared_acknowledged IS NOT TRUE THEN
|
|
333
|
+
RAISE EXCEPTION 'workspace_shared requires durable shared-output acknowledgement'
|
|
334
|
+
USING ERRCODE = '42501';
|
|
335
|
+
END IF;
|
|
336
|
+
IF NOT EXISTS (
|
|
337
|
+
SELECT 1 FROM session_tenancy_activations activation
|
|
338
|
+
WHERE activation.account_id = p_account_id AND activation.activation_version = 1
|
|
339
|
+
) THEN
|
|
340
|
+
RAISE EXCEPTION 'session tenancy product is not activated' USING ERRCODE = '42501';
|
|
341
|
+
END IF;
|
|
342
|
+
|
|
343
|
+
SELECT membership.id INTO owner_membership_id
|
|
344
|
+
FROM organization_memberships membership
|
|
345
|
+
WHERE membership.account_id = p_account_id
|
|
346
|
+
AND membership.subject_id = caller_subject
|
|
347
|
+
AND membership.status = 'active'
|
|
348
|
+
AND membership.revoked_at IS NULL
|
|
349
|
+
FOR SHARE;
|
|
350
|
+
IF NOT FOUND THEN
|
|
351
|
+
RAISE EXCEPTION 'owner membership not found or access denied' USING ERRCODE = '42501';
|
|
352
|
+
END IF;
|
|
353
|
+
|
|
354
|
+
SELECT authority.generation INTO authority_generation
|
|
355
|
+
FROM organization_user_resource_authorities authority
|
|
356
|
+
WHERE authority.id = p_authority_id
|
|
357
|
+
AND authority.account_id = p_account_id
|
|
358
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
359
|
+
AND authority.resource_kind = p_resource_kind
|
|
360
|
+
AND authority.status = 'active'
|
|
361
|
+
AND authority.revoked_at IS NULL
|
|
362
|
+
FOR UPDATE;
|
|
363
|
+
IF NOT FOUND THEN
|
|
364
|
+
RAISE EXCEPTION 'user-resource authority not found or access denied' USING ERRCODE = '42501';
|
|
365
|
+
END IF;
|
|
366
|
+
|
|
367
|
+
PERFORM 1 FROM workspaces workspace_value
|
|
368
|
+
WHERE workspace_value.id = p_workspace_id AND workspace_value.account_id = p_account_id
|
|
369
|
+
FOR SHARE;
|
|
370
|
+
IF NOT FOUND OR NOT EXISTS (
|
|
371
|
+
SELECT 1 FROM organization_memberships membership
|
|
372
|
+
WHERE membership.id = owner_membership_id
|
|
373
|
+
AND (
|
|
374
|
+
membership.personal_workspace_id = p_workspace_id
|
|
375
|
+
OR EXISTS (
|
|
376
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
377
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
378
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
379
|
+
AND workspace_membership.subject_id = caller_subject
|
|
380
|
+
)
|
|
381
|
+
)
|
|
382
|
+
) THEN
|
|
383
|
+
RAISE EXCEPTION 'owner lacks current target-workspace access' USING ERRCODE = '42501';
|
|
384
|
+
END IF;
|
|
385
|
+
|
|
386
|
+
IF p_session_id IS NOT NULL THEN
|
|
387
|
+
SELECT session_value.authority_epoch, session_value.visibility
|
|
388
|
+
INTO target_epoch, target_visibility
|
|
389
|
+
FROM sessions session_value
|
|
390
|
+
WHERE session_value.id = p_session_id
|
|
391
|
+
AND session_value.account_id = p_account_id
|
|
392
|
+
AND session_value.workspace_id = p_workspace_id
|
|
393
|
+
AND session_value.status <> 'cancelled'
|
|
394
|
+
FOR SHARE;
|
|
395
|
+
IF NOT FOUND
|
|
396
|
+
OR target_visibility IS DISTINCT FROM p_context
|
|
397
|
+
OR target_epoch IS DISTINCT FROM p_expected_authority_epoch
|
|
398
|
+
THEN
|
|
399
|
+
RAISE EXCEPTION 'target session not found or access denied' USING ERRCODE = '42501';
|
|
400
|
+
END IF;
|
|
401
|
+
END IF;
|
|
402
|
+
|
|
403
|
+
UPDATE organization_user_resource_grants grant_value
|
|
404
|
+
SET status = 'expired', updated_at = clock_timestamp()
|
|
405
|
+
WHERE grant_value.account_id = p_account_id
|
|
406
|
+
AND grant_value.authority_id = p_authority_id
|
|
407
|
+
AND grant_value.status = 'active'
|
|
408
|
+
AND grant_value.expires_at IS NOT NULL
|
|
409
|
+
AND grant_value.expires_at <= clock_timestamp();
|
|
410
|
+
|
|
411
|
+
INSERT INTO organization_user_resource_grants (
|
|
412
|
+
account_id, authority_id, owner_organization_membership_id, workspace_id,
|
|
413
|
+
session_id, action, mode, context, authority_epoch, generation, status
|
|
414
|
+
) VALUES (
|
|
415
|
+
p_account_id, p_authority_id, owner_membership_id, p_workspace_id,
|
|
416
|
+
p_session_id, canonical_action, p_mode, p_context, target_epoch, 1, 'active'
|
|
417
|
+
)
|
|
418
|
+
ON CONFLICT (account_id, authority_id, workspace_id, action, mode, context,
|
|
419
|
+
(coalesce(session_id, '00000000-0000-0000-0000-000000000000'::uuid)),
|
|
420
|
+
(coalesce(authority_epoch, 0))) WHERE status = 'active'
|
|
421
|
+
DO UPDATE SET updated_at = organization_user_resource_grants.updated_at
|
|
422
|
+
RETURNING id, workspace_id, session_id, organization_user_resource_grants.action,
|
|
423
|
+
mode, context, organization_user_resource_grants.authority_epoch, generation,
|
|
424
|
+
status, organization_user_resource_grants.expires_at
|
|
425
|
+
INTO grant_id, target_workspace_id, target_session_id, action, grant_mode,
|
|
426
|
+
grant_context, authority_epoch, grant_generation, grant_status, expires_at;
|
|
427
|
+
organization_id := p_account_id;
|
|
428
|
+
RETURN NEXT;
|
|
429
|
+
END
|
|
430
|
+
$body$;
|
|
431
|
+
|
|
432
|
+
CREATE OR REPLACE FUNCTION revoke_self_user_resource_grant(
|
|
433
|
+
p_account_id uuid,
|
|
434
|
+
p_workspace_id uuid,
|
|
435
|
+
p_grant_id uuid
|
|
436
|
+
) RETURNS TABLE (
|
|
437
|
+
grant_id uuid, grant_generation bigint, grant_status text, revoked_at timestamptz
|
|
438
|
+
)
|
|
439
|
+
LANGUAGE plpgsql
|
|
440
|
+
SECURITY DEFINER
|
|
441
|
+
SET search_path FROM CURRENT
|
|
442
|
+
AS $body$
|
|
443
|
+
#variable_conflict use_column
|
|
444
|
+
DECLARE
|
|
445
|
+
caller_subject text := nullif(current_setting('opengeni.subject_id', true), '');
|
|
446
|
+
owner_membership_id uuid;
|
|
447
|
+
BEGIN
|
|
448
|
+
PERFORM set_config(
|
|
449
|
+
'opengeni.organization_tenancy_lifecycle', 'personal_resource_grant_management', true
|
|
450
|
+
);
|
|
451
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
452
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
453
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
454
|
+
)::uuid
|
|
455
|
+
OR caller_subject IS NULL
|
|
456
|
+
THEN
|
|
457
|
+
RAISE EXCEPTION 'user-resource revoke scope mismatch' USING ERRCODE = '42501';
|
|
458
|
+
END IF;
|
|
459
|
+
IF NOT EXISTS (
|
|
460
|
+
SELECT 1 FROM session_tenancy_activations activation
|
|
461
|
+
WHERE activation.account_id = p_account_id AND activation.activation_version = 1
|
|
462
|
+
) THEN
|
|
463
|
+
RAISE EXCEPTION 'session tenancy product is not activated' USING ERRCODE = '42501';
|
|
464
|
+
END IF;
|
|
465
|
+
SELECT membership.id INTO owner_membership_id
|
|
466
|
+
FROM organization_memberships membership
|
|
467
|
+
WHERE membership.account_id = p_account_id
|
|
468
|
+
AND membership.subject_id = caller_subject
|
|
469
|
+
AND membership.status = 'active'
|
|
470
|
+
AND membership.revoked_at IS NULL
|
|
471
|
+
FOR SHARE;
|
|
472
|
+
IF NOT FOUND THEN
|
|
473
|
+
RAISE EXCEPTION 'owner membership not found or access denied' USING ERRCODE = '42501';
|
|
474
|
+
END IF;
|
|
475
|
+
|
|
476
|
+
UPDATE organization_user_resource_grants grant_value
|
|
477
|
+
SET status = 'revoked', revoked_at = clock_timestamp(),
|
|
478
|
+
generation = grant_value.generation + 1, updated_at = clock_timestamp()
|
|
479
|
+
FROM organization_user_resource_authorities authority
|
|
480
|
+
WHERE grant_value.id = p_grant_id
|
|
481
|
+
AND grant_value.account_id = p_account_id
|
|
482
|
+
AND grant_value.workspace_id = p_workspace_id
|
|
483
|
+
AND authority.id = grant_value.authority_id
|
|
484
|
+
AND authority.account_id = grant_value.account_id
|
|
485
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
486
|
+
AND grant_value.owner_organization_membership_id = owner_membership_id
|
|
487
|
+
AND grant_value.status <> 'revoked'
|
|
488
|
+
RETURNING grant_value.id, grant_value.generation, grant_value.status, grant_value.revoked_at
|
|
489
|
+
INTO grant_id, grant_generation, grant_status, revoked_at;
|
|
490
|
+
IF FOUND THEN RETURN NEXT; RETURN; END IF;
|
|
491
|
+
|
|
492
|
+
SELECT grant_value.id, grant_value.generation, grant_value.status, grant_value.revoked_at
|
|
493
|
+
INTO grant_id, grant_generation, grant_status, revoked_at
|
|
494
|
+
FROM organization_user_resource_grants grant_value
|
|
495
|
+
JOIN organization_user_resource_authorities authority
|
|
496
|
+
ON authority.id = grant_value.authority_id AND authority.account_id = grant_value.account_id
|
|
497
|
+
WHERE grant_value.id = p_grant_id
|
|
498
|
+
AND grant_value.account_id = p_account_id
|
|
499
|
+
AND grant_value.workspace_id = p_workspace_id
|
|
500
|
+
AND authority.organization_membership_id = owner_membership_id
|
|
501
|
+
AND grant_value.owner_organization_membership_id = owner_membership_id
|
|
502
|
+
AND grant_value.status = 'revoked';
|
|
503
|
+
IF NOT FOUND THEN
|
|
504
|
+
RAISE EXCEPTION 'self-owned user-resource grant not found' USING ERRCODE = '42501';
|
|
505
|
+
END IF;
|
|
506
|
+
RETURN NEXT;
|
|
507
|
+
END
|
|
508
|
+
$body$;
|
|
509
|
+
|
|
510
|
+
-- `SET search_path FROM CURRENT` captures the target schema during function
|
|
511
|
+
-- creation. Replace that broad migration path with the exact immutable runtime
|
|
512
|
+
-- path before this transaction can commit.
|
|
513
|
+
DO $harden_runtime_search_paths$
|
|
514
|
+
DECLARE
|
|
515
|
+
data_schema text := current_schema();
|
|
516
|
+
BEGIN
|
|
517
|
+
EXECUTE format(
|
|
518
|
+
'ALTER FUNCTION %I.list_self_user_resource_authorities(uuid, uuid, text, uuid, integer) SET search_path = pg_catalog, %I, pg_temp',
|
|
519
|
+
data_schema, data_schema
|
|
520
|
+
);
|
|
521
|
+
EXECUTE format(
|
|
522
|
+
'ALTER FUNCTION %I.issue_self_user_resource_grant(uuid, uuid, uuid, text, text, text, uuid, integer, boolean) SET search_path = pg_catalog, %I, pg_temp',
|
|
523
|
+
data_schema, data_schema
|
|
524
|
+
);
|
|
525
|
+
EXECUTE format(
|
|
526
|
+
'ALTER FUNCTION %I.revoke_self_user_resource_grant(uuid, uuid, uuid) SET search_path = pg_catalog, %I, pg_temp',
|
|
527
|
+
data_schema, data_schema
|
|
528
|
+
);
|
|
529
|
+
END
|
|
530
|
+
$harden_runtime_search_paths$;
|
|
531
|
+
|
|
532
|
+
REVOKE ALL ON FUNCTION list_self_user_resource_authorities(uuid, uuid, text, uuid, integer)
|
|
533
|
+
FROM PUBLIC;
|
|
534
|
+
REVOKE ALL ON FUNCTION issue_self_user_resource_grant(
|
|
535
|
+
uuid, uuid, uuid, text, text, text, uuid, integer, boolean
|
|
536
|
+
) FROM PUBLIC;
|
|
537
|
+
REVOKE ALL ON FUNCTION revoke_self_user_resource_grant(uuid, uuid, uuid) FROM PUBLIC;
|
|
538
|
+
|
|
539
|
+
DO $runtime_grants$
|
|
540
|
+
DECLARE
|
|
541
|
+
legacy_signature text;
|
|
542
|
+
legacy_function regprocedure;
|
|
543
|
+
BEGIN
|
|
544
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
545
|
+
FOREACH legacy_signature IN ARRAY ARRAY[
|
|
546
|
+
'list_self_user_resource_authorities(uuid)',
|
|
547
|
+
'issue_self_user_resource_grant(uuid,uuid,uuid,text,text,text,uuid,boolean)',
|
|
548
|
+
'revoke_self_user_resource_grant(uuid,uuid)',
|
|
549
|
+
'list_self_connection_authorities(uuid)',
|
|
550
|
+
'issue_self_connection_use_grant(uuid,uuid,uuid,text,text,uuid,boolean)',
|
|
551
|
+
'revoke_self_connection_use_grant(uuid,uuid)'
|
|
552
|
+
]
|
|
553
|
+
LOOP
|
|
554
|
+
legacy_function := pg_catalog.to_regprocedure(
|
|
555
|
+
pg_catalog.format('%I.%s', pg_catalog.current_schema(), legacy_signature)
|
|
556
|
+
);
|
|
557
|
+
IF legacy_function IS NOT NULL THEN
|
|
558
|
+
EXECUTE pg_catalog.format(
|
|
559
|
+
'REVOKE ALL ON FUNCTION %s FROM opengeni_app', legacy_function
|
|
560
|
+
);
|
|
561
|
+
END IF;
|
|
562
|
+
END LOOP;
|
|
563
|
+
|
|
564
|
+
GRANT EXECUTE ON FUNCTION
|
|
565
|
+
list_self_user_resource_authorities(uuid, uuid, text, uuid, integer)
|
|
566
|
+
TO opengeni_app;
|
|
567
|
+
GRANT EXECUTE ON FUNCTION issue_self_user_resource_grant(
|
|
568
|
+
uuid, uuid, uuid, text, text, text, uuid, integer, boolean
|
|
569
|
+
) TO opengeni_app;
|
|
570
|
+
GRANT EXECUTE ON FUNCTION revoke_self_user_resource_grant(uuid, uuid, uuid)
|
|
571
|
+
TO opengeni_app;
|
|
572
|
+
REVOKE ALL ON TABLE organization_user_resource_authorities FROM opengeni_app;
|
|
573
|
+
REVOKE ALL ON TABLE organization_user_resource_grants FROM opengeni_app;
|
|
574
|
+
END IF;
|
|
575
|
+
END
|
|
576
|
+
$runtime_grants$;
|
|
577
|
+
|
|
578
|
+
COMMENT ON FUNCTION list_self_user_resource_authorities(uuid, uuid, text, uuid, integer) IS
|
|
579
|
+
'Bounded owner-only personal-resource page filtered to one kind and route workspace.';
|
|
580
|
+
COMMENT ON FUNCTION issue_self_user_resource_grant(
|
|
581
|
+
uuid, uuid, uuid, text, text, text, uuid, integer, boolean
|
|
582
|
+
) IS 'Issues server-action-derived session/always grants after exact owner and tenancy fences.';
|
|
583
|
+
COMMENT ON FUNCTION revoke_self_user_resource_grant(uuid, uuid, uuid) IS
|
|
584
|
+
'Idempotently revokes an exact owner grant only through its route workspace.';
|