@opengeni/db 1.0.1 → 3.1.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/dist/attached-browser-devices.d.ts +7 -0
  2. package/dist/automations.d.ts +154 -0
  3. package/dist/canonical-human-identities.js +3 -3
  4. package/dist/capability-integrations.d.ts +13 -0
  5. package/dist/child-lifecycle-notices.d.ts +68 -0
  6. package/dist/{chunk-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
  7. package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
  8. package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
  9. package/dist/chunk-NMUBV7Z5.js.map +1 -0
  10. package/dist/chunk-OF5OBB76.js +4457 -0
  11. package/dist/chunk-OF5OBB76.js.map +1 -0
  12. package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
  13. package/dist/chunk-PR2WMLOQ.js.map +1 -0
  14. package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
  15. package/dist/chunk-SCPUJS53.js.map +1 -0
  16. package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
  17. package/dist/chunk-SIBYBA2D.js.map +1 -0
  18. package/dist/chunk-TIKF3KTI.js +383 -0
  19. package/dist/chunk-TIKF3KTI.js.map +1 -0
  20. package/dist/chunk-ZZ5LJEFX.js +516 -0
  21. package/dist/chunk-ZZ5LJEFX.js.map +1 -0
  22. package/dist/codemode-operations.d.ts +1 -6
  23. package/dist/company-brain-context-selection-schema.d.ts +705 -0
  24. package/dist/company-brain-context-selection.d.ts +94 -0
  25. package/dist/company-brain-governed-writes.d.ts +24 -0
  26. package/dist/company-brain.d.ts +55 -0
  27. package/dist/company-profile-agent-admin.d.ts +15 -0
  28. package/dist/company-profile-schema.d.ts +619 -2
  29. package/dist/company-profile.d.ts +9 -4
  30. package/dist/connection-authority.d.ts +95 -0
  31. package/dist/connection-scopes.d.ts +8 -0
  32. package/dist/connection-tenancy-backfill.d.ts +41 -0
  33. package/dist/connection-token-resolver.d.ts +30 -1
  34. package/dist/database.d.ts +19 -0
  35. package/dist/editable-artifact-durable-export.js +2 -2
  36. package/dist/editable-artifacts.d.ts +2 -2
  37. package/dist/editable-artifacts.js +3 -3
  38. package/dist/governed-learning-activation-schema.d.ts +984 -0
  39. package/dist/governed-learning-activation.d.ts +98 -0
  40. package/dist/governed-learning-evaluator-schema.d.ts +633 -0
  41. package/dist/governed-learning-evaluator.d.ts +28 -0
  42. package/dist/index.d.ts +1595 -81
  43. package/dist/index.js +21571 -8126
  44. package/dist/index.js.map +1 -1
  45. package/dist/insights.d.ts +18 -0
  46. package/dist/integration-bindings.d.ts +1 -0
  47. package/dist/integration-facets.d.ts +30 -0
  48. package/dist/interaction-placement-loss.d.ts +22 -0
  49. package/dist/managed-human-provisioning.d.ts +25 -0
  50. package/dist/memory-domain.d.ts +3 -3
  51. package/dist/migrate.d.ts +11 -1
  52. package/dist/migrate.js +1 -1
  53. package/dist/new-session-drafts.d.ts +43 -5
  54. package/dist/organization-membership-backfill.d.ts +174 -0
  55. package/dist/organization-membership-lifecycle.d.ts +162 -0
  56. package/dist/pr-review.d.ts +107 -0
  57. package/dist/preference-registry-schema.d.ts +2 -0
  58. package/dist/preference-registry.d.ts +2 -0
  59. package/dist/provision-roles.js +1 -1
  60. package/dist/runtime-posture.d.ts +22 -8
  61. package/dist/schema.d.ts +24914 -13703
  62. package/dist/schema.js +99 -1
  63. package/dist/scoped-knowledge-schema.d.ts +52 -1
  64. package/dist/session-background-commands.d.ts +125 -0
  65. package/dist/session-background-commands.js +34 -0
  66. package/dist/session-background-commands.js.map +1 -0
  67. package/dist/session-control.d.ts +156 -3
  68. package/dist/session-goal-pacing.d.ts +68 -0
  69. package/dist/session-queue-commands.d.ts +41 -4
  70. package/dist/session-realtime-ledger.d.ts +2 -0
  71. package/dist/session-tenancy.d.ts +71 -1
  72. package/dist/session-tenancy.js +25 -3
  73. package/dist/session-tool-call-settlement.d.ts +9 -4
  74. package/dist/slack-routing-personal-workspace.d.ts +23 -0
  75. package/dist/slack-routing.d.ts +153 -0
  76. package/dist/slack-task-policy-schema.d.ts +34 -0
  77. package/dist/slack-task-policy.d.ts +8 -0
  78. package/dist/slack-user-link-access.d.ts +1 -0
  79. package/dist/task-notes-schema.d.ts +594 -0
  80. package/dist/task-notes.d.ts +39 -0
  81. package/dist/turn-initiator.d.ts +2 -0
  82. package/dist/user-resource-authority.d.ts +109 -0
  83. package/dist/video-generation.js +3 -3
  84. package/dist/workspace-authority.d.ts +81 -0
  85. package/dist/workspace-instruction-policies-schema.d.ts +279 -0
  86. package/dist/workspace-instruction-policies.d.ts +62 -0
  87. package/dist/workspace-learning-policy.d.ts +1 -0
  88. package/dist/workspace-membership-permissions.d.ts +2 -0
  89. package/drizzle/0240_enrollment_connection_authority.sql +39 -0
  90. package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
  91. package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
  92. package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
  93. package/drizzle/0245_model_context_contribution_facts.sql +88 -0
  94. package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
  95. package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
  96. package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
  97. package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
  98. package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
  99. package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
  100. package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
  101. package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
  102. package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
  103. package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
  104. package/drizzle/0256_connection_authority_delegation.sql +703 -0
  105. package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
  106. package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
  107. package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
  108. package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
  109. package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
  110. package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
  111. package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
  112. package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
  113. package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
  114. package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
  115. package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
  116. package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
  117. package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
  118. package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
  119. package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
  120. package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
  121. package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
  122. package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
  123. package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
  124. package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
  125. package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
  126. package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
  127. package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
  128. package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
  129. package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
  130. package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
  131. package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
  132. package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
  133. package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
  134. package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
  135. package/drizzle/0289_session_composer_policy_authority.sql +597 -0
  136. package/drizzle/0290_organization_membership_backfill.sql +268 -0
  137. package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
  138. package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
  139. package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
  140. package/drizzle/0294_preference_activation_authority.sql +237 -0
  141. package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
  142. package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
  143. package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
  144. package/drizzle/0298_organization_tenancy_parity.sql +609 -0
  145. package/drizzle/0299_organization_membership_lock_order.sql +291 -0
  146. package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
  147. package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
  148. package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
  149. package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
  150. package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
  151. package/drizzle/0305_personal_resource_grant_management.sql +584 -0
  152. package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
  153. package/drizzle/0307_session_attention_state.sql +51 -0
  154. package/drizzle/0308_session_archives.sql +35 -0
  155. package/drizzle/0309_channel_project_pins.sql +6 -0
  156. package/drizzle/0310_channel_project_order.sql +27 -0
  157. package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
  158. package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
  159. package/drizzle/0313_private_child_session_authority.sql +365 -0
  160. package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
  161. package/drizzle/0315_personal_github_repository_selection.sql +624 -0
  162. package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
  163. package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
  164. package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
  165. package/drizzle/0319_event_triggered_automations.sql +254 -0
  166. package/drizzle/0320_pr_review_pack.sql +113 -0
  167. package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
  168. package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
  169. package/drizzle/0323_organization_private_session_enablement.sql +489 -0
  170. package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
  171. package/drizzle/0325_child_lifecycle_notices.sql +44 -0
  172. package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
  173. package/drizzle/0327_slack_first_task_hint.sql +29 -0
  174. package/drizzle/0328_session_background_commands.sql +590 -0
  175. package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
  176. package/drizzle/0330_api_key_descriptions.sql +20 -0
  177. package/drizzle/0331_managed_organization_creation.sql +163 -0
  178. package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
  179. package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
  180. package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
  181. package/drizzle/0335_slack_workspace_routing.sql +460 -0
  182. package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
  183. package/drizzle/0337_slack_routed_action_handles.sql +189 -0
  184. package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
  185. package/drizzle/0339_document_authority_reclassification.sql +1153 -0
  186. package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
  187. package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
  188. package/package.json +10 -6
  189. package/src/attached-browser-devices.ts +233 -5
  190. package/src/automations.ts +911 -0
  191. package/src/browser-sessions.ts +10 -0
  192. package/src/capability-integrations.ts +175 -1
  193. package/src/child-lifecycle-notices.ts +241 -0
  194. package/src/codemode-operations.ts +1 -35
  195. package/src/company-brain-context-selection-schema.ts +100 -0
  196. package/src/company-brain-context-selection.ts +359 -0
  197. package/src/company-brain-governed-writes.ts +1170 -0
  198. package/src/company-brain.ts +309 -0
  199. package/src/company-profile-agent-admin.ts +225 -0
  200. package/src/company-profile-schema.ts +121 -1
  201. package/src/company-profile.ts +21 -0
  202. package/src/computer-sessions.ts +10 -1
  203. package/src/connection-authority.ts +290 -0
  204. package/src/connection-scopes.ts +20 -0
  205. package/src/connection-tenancy-backfill.ts +81 -0
  206. package/src/connection-token-resolver.ts +263 -31
  207. package/src/database.ts +75 -2
  208. package/src/editable-artifacts-schema.ts +10 -5
  209. package/src/editable-artifacts.ts +21 -11
  210. package/src/governed-learning-activation-schema.ts +121 -0
  211. package/src/governed-learning-activation.ts +588 -0
  212. package/src/governed-learning-evaluator-schema.ts +72 -0
  213. package/src/governed-learning-evaluator.ts +217 -0
  214. package/src/index.ts +11651 -1548
  215. package/src/insights.ts +107 -0
  216. package/src/integration-bindings.ts +88 -6
  217. package/src/integration-facets.ts +287 -84
  218. package/src/interaction-placement-loss.ts +234 -0
  219. package/src/managed-human-provisioning.ts +116 -0
  220. package/src/memory-domain.ts +27 -32
  221. package/src/migrate.ts +77 -1
  222. package/src/new-session-drafts.ts +229 -35
  223. package/src/organization-membership-backfill.ts +735 -0
  224. package/src/organization-membership-lifecycle.ts +985 -0
  225. package/src/pr-review.ts +890 -0
  226. package/src/preference-registry.ts +31 -1
  227. package/src/provision-roles.ts +877 -6
  228. package/src/runtime-posture-cli.ts +2 -0
  229. package/src/runtime-posture.ts +1291 -76
  230. package/src/schema.ts +3303 -428
  231. package/src/scoped-knowledge-schema.ts +16 -8
  232. package/src/scoped-knowledge.ts +1 -1
  233. package/src/session-background-commands.ts +759 -0
  234. package/src/session-control.ts +640 -74
  235. package/src/session-goal-pacing.ts +126 -0
  236. package/src/session-queue-commands.ts +437 -84
  237. package/src/session-realtime-context.ts +3 -3
  238. package/src/session-realtime-ledger.ts +22 -11
  239. package/src/session-realtime-mirror.ts +1 -1
  240. package/src/session-tenancy.ts +403 -49
  241. package/src/session-tool-call-settlement.ts +38 -29
  242. package/src/slack-routing-personal-workspace.ts +67 -0
  243. package/src/slack-routing.ts +469 -0
  244. package/src/slack-task-policy-schema.ts +5 -0
  245. package/src/slack-task-policy.ts +18 -0
  246. package/src/slack-user-link-access.ts +17 -1
  247. package/src/task-notes-schema.ts +74 -0
  248. package/src/task-notes.ts +124 -2
  249. package/src/turn-initiator.ts +8 -0
  250. package/src/user-resource-authority.ts +377 -0
  251. package/src/workspace-authority.ts +160 -0
  252. package/src/workspace-instruction-policies-schema.ts +53 -1
  253. package/src/workspace-instruction-policies.ts +528 -77
  254. package/src/workspace-learning-policy.ts +21 -5
  255. package/src/workspace-membership-permissions.ts +9 -0
  256. package/dist/chunk-4SR4HKKA.js.map +0 -1
  257. package/dist/chunk-6ZYKCRFO.js.map +0 -1
  258. package/dist/chunk-NXAFLCQX.js +0 -2468
  259. package/dist/chunk-NXAFLCQX.js.map +0 -1
  260. package/dist/chunk-SK7YYHBI.js.map +0 -1
  261. package/dist/chunk-WD5ROSLL.js +0 -117
  262. package/dist/chunk-WD5ROSLL.js.map +0 -1
  263. package/dist/chunk-ZWREYEWA.js.map +0 -1
  264. /package/dist/{chunk-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
  265. /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.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.';