@opengeni/db 0.28.9 → 0.36.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 +42 -0
- package/dist/attempt-tool-catalogs.d.ts +16 -0
- package/dist/browser-auth.d.ts +299 -0
- package/dist/browser-downloads.d.ts +51 -0
- package/dist/browser-identities.d.ts +137 -0
- package/dist/browser-sessions.d.ts +354 -0
- package/dist/browser-state-artifacts.d.ts +91 -0
- package/dist/canonical-human-identities.d.ts +48 -0
- package/dist/canonical-human-identities.js +28 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +194 -0
- package/dist/chunk-5KTIEDX7.js +13200 -0
- package/dist/chunk-5KTIEDX7.js.map +1 -0
- package/dist/chunk-CQZ4QSP5.js +995 -0
- package/dist/chunk-CQZ4QSP5.js.map +1 -0
- package/dist/chunk-F32YT7MP.js +117 -0
- package/dist/chunk-F32YT7MP.js.map +1 -0
- package/dist/chunk-JC5QPLUM.js +666 -0
- package/dist/chunk-JC5QPLUM.js.map +1 -0
- package/dist/chunk-P3RE4D2B.js +3535 -0
- package/dist/chunk-P3RE4D2B.js.map +1 -0
- package/dist/chunk-TAUTWOS5.js +259 -0
- package/dist/chunk-TAUTWOS5.js.map +1 -0
- package/dist/chunk-VKLB2ZGI.js +2389 -0
- package/dist/chunk-VKLB2ZGI.js.map +1 -0
- package/dist/codemode-operations.d.ts +117 -0
- package/dist/codex-token-resolver.d.ts +2 -2
- package/dist/company-profile-schema.d.ts +798 -0
- package/dist/company-profile.d.ts +268 -0
- package/dist/computer-sessions.d.ts +109 -0
- package/dist/connection-token-resolver.d.ts +9 -4
- package/dist/database.d.ts +57 -3
- package/dist/editable-artifact-durable-export.d.ts +153 -0
- package/dist/editable-artifact-durable-export.js +788 -0
- package/dist/editable-artifact-durable-export.js.map +1 -0
- package/dist/editable-artifact-materialization.d.ts +123 -0
- package/dist/editable-artifacts-schema.d.ts +4152 -0
- package/dist/editable-artifacts.d.ts +610 -0
- package/dist/editable-artifacts.js +26 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/generated-images.d.ts +139 -0
- package/dist/index.d.ts +1122 -126
- package/dist/index.js +42329 -18407
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +73 -10
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-facets.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +14 -0
- package/dist/interaction-schema.d.ts +6036 -0
- package/dist/knowledge-source-sync-schema.d.ts +1679 -0
- package/dist/knowledge-source-sync.d.ts +274 -0
- package/dist/memory-governance.d.ts +2 -2
- package/dist/memory-slack-delivery.d.ts +101 -0
- package/dist/new-session-drafts.d.ts +2 -2
- package/dist/pack-components.d.ts +76 -0
- package/dist/pack-installations.d.ts +86 -0
- package/dist/persistence-errors.d.ts +3 -3
- package/dist/plugin-packages.d.ts +126 -0
- package/dist/preference-registry.d.ts +1 -1
- package/dist/provision-roles.d.ts +9 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +19 -6
- package/dist/schema.d.ts +22654 -9359
- package/dist/schema.js +209 -3
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +33 -7
- package/dist/session-queue-commands.d.ts +19 -11
- package/dist/session-realtime-context.d.ts +2 -2
- package/dist/session-realtime-ledger.d.ts +4 -4
- package/dist/session-realtime-mirror.d.ts +2 -2
- package/dist/session-realtime-state.d.ts +1 -1
- package/dist/session-realtime-terminal.d.ts +1 -1
- package/dist/session-realtime.d.ts +7 -7
- package/dist/session-tenancy.d.ts +44 -0
- package/dist/session-tenancy.js +16 -0
- package/dist/session-tenancy.js.map +1 -0
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/slack-task-policy-schema.d.ts +847 -0
- package/dist/slack-task-policy.d.ts +65 -0
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/task-notes-schema.d.ts +924 -0
- package/dist/task-notes.d.ts +85 -0
- package/dist/transcription-recordings.d.ts +2 -2
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +225 -0
- package/dist/video-generation.js +68 -0
- package/dist/video-generation.js.map +1 -0
- package/dist/workspace-artifacts.d.ts +1 -1
- package/dist/workspace-instruction-policies.d.ts +1 -1
- package/dist/workspace-learning-policy-schema.d.ts +932 -0
- package/dist/workspace-learning-policy.d.ts +161 -0
- package/dist/xai-subscription.d.ts +326 -0
- package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
- package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
- package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
- package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
- package/drizzle/0187_generated_image_artifacts.sql +173 -0
- package/drizzle/0188_image_generation_retention_failure.sql +25 -0
- package/drizzle/0189_retained_session_image_formats.sql +13 -0
- package/drizzle/0190_timeline_annotations.sql +31 -0
- package/drizzle/0191_editable_artifact_engine.sql +3853 -0
- package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
- package/drizzle/0193_editable_artifact_authorization.sql +190 -0
- package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
- package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
- package/drizzle/0196_rig_provider_images.sql +152 -0
- package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
- package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
- package/drizzle/0199_workspace_learning_policy.sql +980 -0
- package/drizzle/0201_company_profile_authority.sql +849 -0
- package/drizzle/0202_document_index_checkpoints.sql +85 -0
- package/drizzle/0203_durable_video_generation.sql +353 -0
- package/drizzle/0204_video_generation_funding.sql +44 -0
- package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
- package/drizzle/0206_browser_sessions.sql +646 -0
- package/drizzle/0207_browser_identities.sql +379 -0
- package/drizzle/0208_attached_browser_devices.sql +165 -0
- package/drizzle/0209_computer_sessions.sql +575 -0
- package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
- package/drizzle/0211_editable_artifact_session_links.sql +58 -0
- package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
- package/drizzle/0212_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_browser_interaction_authority.sql +67 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_browser_download_saves.sql +34 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_browser_controller_host.sql +385 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/drizzle/0217_external_browser_auth_operations.sql +22 -0
- package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
- package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
- package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
- package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
- package/drizzle/0220_session_channels.sql +54 -0
- package/drizzle/0221_sessions_channel_index.sql +5 -0
- package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
- package/drizzle/0222_sessions_channel_fk.sql +89 -0
- package/drizzle/0223_pending_tool_event_output.sql +55 -0
- package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
- package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
- package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
- package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
- package/drizzle/0227_slack_native_actions.sql +145 -0
- package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
- package/drizzle/0228_slack_task_policy.sql +400 -0
- package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
- package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
- package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
- package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
- package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
- package/drizzle/0234_xai_subscription_authority.sql +1139 -0
- package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
- package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
- package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
- package/drizzle/0237_interaction_transition_reaper.sql +263 -0
- package/drizzle/0238_goal_persistence_policy.sql +471 -0
- package/drizzle/0238_supergrok_realtime_model.sql +29 -0
- package/drizzle/0239_supergrok_video_funding.sql +50 -0
- package/drizzle/0239_task_tree_notes.sql +990 -0
- package/package.json +26 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +4373 -0
- package/src/browser-downloads.ts +517 -0
- package/src/browser-identities.ts +1385 -0
- package/src/browser-sessions.ts +2294 -0
- package/src/browser-state-artifacts.ts +449 -0
- package/src/canonical-human-identities.ts +317 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1571 -0
- package/src/codemode-operations.ts +674 -0
- package/src/company-profile-schema.ts +143 -0
- package/src/company-profile.ts +968 -0
- package/src/computer-sessions.ts +1130 -0
- package/src/connection-token-resolver.ts +270 -25
- package/src/database.ts +468 -24
- package/src/editable-artifact-durable-export.ts +1201 -0
- package/src/editable-artifact-materialization.ts +893 -0
- package/src/editable-artifacts-schema.ts +879 -0
- package/src/editable-artifacts.ts +5404 -0
- package/src/generated-images.ts +740 -0
- package/src/index.ts +9963 -1465
- package/src/insights.ts +251 -23
- package/src/integration-bindings.ts +455 -0
- package/src/integration-facets.ts +862 -0
- package/src/interaction-revisions.ts +70 -0
- package/src/interaction-schema.ts +1606 -0
- package/src/knowledge-source-sync-schema.ts +192 -0
- package/src/knowledge-source-sync.ts +1922 -0
- package/src/memory-slack-delivery.ts +801 -0
- package/src/pack-components.ts +954 -0
- package/src/pack-installations.ts +886 -0
- package/src/persistence-errors.ts +4 -7
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +534 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +721 -5
- package/src/schema.ts +6270 -3159
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +130 -42
- package/src/session-queue-commands.ts +265 -15
- package/src/session-realtime-context.ts +2 -2
- package/src/session-realtime-ledger.ts +3 -3
- package/src/session-realtime.ts +7 -7
- package/src/session-tenancy.ts +188 -0
- package/src/session-tool-call-settlement.ts +18 -3
- package/src/slack-task-policy-schema.ts +130 -0
- package/src/slack-task-policy.ts +341 -0
- package/src/slack-user-link-access.ts +702 -0
- package/src/task-notes-schema.ts +135 -0
- package/src/task-notes.ts +178 -0
- package/src/video-generation.ts +1530 -0
- package/src/workspace-artifacts.ts +1 -1
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/src/xai-subscription.ts +1887 -0
- package/dist/chunk-P6CUHXQZ.js +0 -7248
- package/dist/chunk-P6CUHXQZ.js.map +0 -1
- package/dist/chunk-VHBFHMPC.js +0 -1219
- package/dist/chunk-VHBFHMPC.js.map +0 -1
package/dist/chunk-VHBFHMPC.js
DELETED
|
@@ -1,1219 +0,0 @@
|
|
|
1
|
-
// src/provision-roles.ts
|
|
2
|
-
import postgres from "postgres";
|
|
3
|
-
|
|
4
|
-
// src/runtime-posture.ts
|
|
5
|
-
import { sql } from "drizzle-orm";
|
|
6
|
-
|
|
7
|
-
// src/role-relationships.ts
|
|
8
|
-
function roleRelationshipsCatalogQuery(targetRole) {
|
|
9
|
-
return `
|
|
10
|
-
with recursive target_role(oid, rolname) as (
|
|
11
|
-
select oid, rolname
|
|
12
|
-
from pg_roles
|
|
13
|
-
where rolname = ${targetRole}
|
|
14
|
-
), inherited_roles(oid, rolname) as (
|
|
15
|
-
select parent.oid, parent.rolname
|
|
16
|
-
from pg_auth_members membership
|
|
17
|
-
join target_role target on target.oid = membership.member
|
|
18
|
-
join pg_roles parent on parent.oid = membership.roleid
|
|
19
|
-
union
|
|
20
|
-
select parent.oid, parent.rolname
|
|
21
|
-
from inherited_roles inherited
|
|
22
|
-
join pg_auth_members membership on membership.member = inherited.oid
|
|
23
|
-
join pg_roles parent on parent.oid = membership.roleid
|
|
24
|
-
), relationships as (
|
|
25
|
-
select
|
|
26
|
-
('inherits:' || inherited.rolname)::text as relationship,
|
|
27
|
-
'inherits'::text as direction,
|
|
28
|
-
null::boolean as admin_option,
|
|
29
|
-
null::boolean as inherit_option,
|
|
30
|
-
null::boolean as set_option,
|
|
31
|
-
null::boolean as member_is_superuser,
|
|
32
|
-
null::boolean as member_can_create_role,
|
|
33
|
-
null::text as grantor_role,
|
|
34
|
-
null::boolean as grantor_is_superuser
|
|
35
|
-
from inherited_roles inherited
|
|
36
|
-
union all
|
|
37
|
-
select
|
|
38
|
-
('member:' || member.rolname)::text as relationship,
|
|
39
|
-
'member'::text as direction,
|
|
40
|
-
membership.admin_option,
|
|
41
|
-
case
|
|
42
|
-
when current_setting('server_version_num')::integer >= 160000
|
|
43
|
-
then (to_jsonb(membership) ->> 'inherit_option')::boolean
|
|
44
|
-
else null::boolean
|
|
45
|
-
end as inherit_option,
|
|
46
|
-
case
|
|
47
|
-
when current_setting('server_version_num')::integer >= 160000
|
|
48
|
-
then (to_jsonb(membership) ->> 'set_option')::boolean
|
|
49
|
-
else null::boolean
|
|
50
|
-
end as set_option,
|
|
51
|
-
member.rolsuper as member_is_superuser,
|
|
52
|
-
member.rolcreaterole as member_can_create_role,
|
|
53
|
-
grantor.rolname::text as grantor_role,
|
|
54
|
-
grantor.rolsuper as grantor_is_superuser
|
|
55
|
-
from pg_auth_members membership
|
|
56
|
-
join target_role target on target.oid = membership.roleid
|
|
57
|
-
join pg_roles member on member.oid = membership.member
|
|
58
|
-
left join pg_roles grantor on grantor.oid = membership.grantor
|
|
59
|
-
)
|
|
60
|
-
select
|
|
61
|
-
relationship,
|
|
62
|
-
direction,
|
|
63
|
-
current_setting('server_version_num')::integer as server_version_num,
|
|
64
|
-
admin_option,
|
|
65
|
-
inherit_option,
|
|
66
|
-
set_option,
|
|
67
|
-
member_is_superuser,
|
|
68
|
-
member_can_create_role,
|
|
69
|
-
grantor_role,
|
|
70
|
-
grantor_is_superuser
|
|
71
|
-
from relationships
|
|
72
|
-
order by relationship, direction, grantor_role nulls first
|
|
73
|
-
`;
|
|
74
|
-
}
|
|
75
|
-
function isManagementOnlyRoleRelationship(row) {
|
|
76
|
-
return row.direction === "member" && row.server_version_num >= 16e4 && row.admin_option === true && row.inherit_option === false && row.set_option === false && row.member_is_superuser === false && row.member_can_create_role === true && row.grantor_is_superuser === true;
|
|
77
|
-
}
|
|
78
|
-
function sortedUnique(values) {
|
|
79
|
-
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
80
|
-
}
|
|
81
|
-
function classifyRoleRelationships(rows) {
|
|
82
|
-
const managementOnlyRows = rows.filter(isManagementOnlyRoleRelationship);
|
|
83
|
-
const unsafeRows = rows.filter((row) => !isManagementOnlyRoleRelationship(row));
|
|
84
|
-
return {
|
|
85
|
-
relationships: sortedUnique(rows.map((row) => row.relationship)),
|
|
86
|
-
managementOnlyRelationships: sortedUnique(managementOnlyRows.map((row) => row.relationship)),
|
|
87
|
-
unsafeRelationships: sortedUnique(unsafeRows.map((row) => row.relationship))
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// src/runtime-posture.ts
|
|
92
|
-
var FORCE_RLS_TABLES = [
|
|
93
|
-
"agent_run_states",
|
|
94
|
-
"api_keys",
|
|
95
|
-
"audit_events",
|
|
96
|
-
"billing_customers",
|
|
97
|
-
"capability_catalog_items",
|
|
98
|
-
"capability_installations",
|
|
99
|
-
"codex_apps_settings",
|
|
100
|
-
"codex_capacity_waiters",
|
|
101
|
-
"codex_credential_leases",
|
|
102
|
-
"codex_reset_redemption_attempts",
|
|
103
|
-
"codex_rotation_settings",
|
|
104
|
-
"codex_subscription_credentials",
|
|
105
|
-
"composer_drafts",
|
|
106
|
-
"connection_disconnect_operations",
|
|
107
|
-
"connections",
|
|
108
|
-
"connector_action_policies",
|
|
109
|
-
"connector_action_requests",
|
|
110
|
-
"credit_ledger_entries",
|
|
111
|
-
"device_enrollment_requests",
|
|
112
|
-
"document_bases",
|
|
113
|
-
"document_chunks",
|
|
114
|
-
"documents",
|
|
115
|
-
"enrollments",
|
|
116
|
-
"file_uploads",
|
|
117
|
-
"files",
|
|
118
|
-
"github_installation_repositories",
|
|
119
|
-
"github_installations",
|
|
120
|
-
"host_export_config",
|
|
121
|
-
"host_export_consumers",
|
|
122
|
-
"host_export_cursor_state",
|
|
123
|
-
"host_export_dead_letters",
|
|
124
|
-
"host_export_outbox",
|
|
125
|
-
"import_batches",
|
|
126
|
-
"integration_oauth_state_nonces",
|
|
127
|
-
"knowledge_change_proposals",
|
|
128
|
-
"knowledge_claim_evidence",
|
|
129
|
-
"knowledge_claim_relations",
|
|
130
|
-
"knowledge_claim_reviews",
|
|
131
|
-
"knowledge_claims",
|
|
132
|
-
"knowledge_document_versions",
|
|
133
|
-
"knowledge_entities",
|
|
134
|
-
"knowledge_entity_aliases",
|
|
135
|
-
"knowledge_facts",
|
|
136
|
-
"knowledge_lifecycle_events",
|
|
137
|
-
"knowledge_memories",
|
|
138
|
-
"knowledge_memory_lifecycle_events",
|
|
139
|
-
"knowledge_memory_relationships",
|
|
140
|
-
"knowledge_operation_receipts",
|
|
141
|
-
"knowledge_providers",
|
|
142
|
-
"knowledge_source_acl_versions",
|
|
143
|
-
"knowledge_source_objects",
|
|
144
|
-
"knowledge_sources",
|
|
145
|
-
"knowledge_sync_runs",
|
|
146
|
-
"machine_metrics_latest",
|
|
147
|
-
"machine_metrics_series",
|
|
148
|
-
"machine_removal_operations",
|
|
149
|
-
"model_call_facts",
|
|
150
|
-
"new_session_drafts",
|
|
151
|
-
"pack_installations",
|
|
152
|
-
"preference_registry_events",
|
|
153
|
-
"preference_registry_preferences",
|
|
154
|
-
"preference_registry_revisions",
|
|
155
|
-
"preference_registry_snapshots",
|
|
156
|
-
"retained_screenshot_artifacts",
|
|
157
|
-
"rig_changes",
|
|
158
|
-
"rig_versions",
|
|
159
|
-
"rigs",
|
|
160
|
-
"sandbox_checkpoint_artifacts",
|
|
161
|
-
"sandbox_lease_holders",
|
|
162
|
-
"sandbox_leases",
|
|
163
|
-
"sandbox_pty_sessions",
|
|
164
|
-
"sandbox_retained_processes",
|
|
165
|
-
"sandbox_session_envelopes",
|
|
166
|
-
"sandbox_workspace_mutation_admissions",
|
|
167
|
-
"sandboxes",
|
|
168
|
-
"scheduled_task_runs",
|
|
169
|
-
"scheduled_tasks",
|
|
170
|
-
"session_attempt_interruptions",
|
|
171
|
-
"session_command_receipts",
|
|
172
|
-
"session_events",
|
|
173
|
-
"session_goals",
|
|
174
|
-
"session_history_items",
|
|
175
|
-
"session_human_input_requests",
|
|
176
|
-
"session_list_snapshots",
|
|
177
|
-
"session_mcp_servers",
|
|
178
|
-
"session_pending_tool_calls",
|
|
179
|
-
"session_pins",
|
|
180
|
-
"session_realtime_connections",
|
|
181
|
-
"session_realtime_context_projections",
|
|
182
|
-
"session_realtime_entries",
|
|
183
|
-
"session_realtime_modes",
|
|
184
|
-
"session_recordings",
|
|
185
|
-
"session_spawn_denials",
|
|
186
|
-
"session_stream_acknowledgments",
|
|
187
|
-
"session_system_update_outbox",
|
|
188
|
-
"session_system_updates",
|
|
189
|
-
"session_turn_attempts",
|
|
190
|
-
"session_turns",
|
|
191
|
-
"session_workflow_wake_outbox",
|
|
192
|
-
"sessions",
|
|
193
|
-
"slack_bot_delete_operations",
|
|
194
|
-
"slack_bot_post_operations",
|
|
195
|
-
"slack_bot_user_links",
|
|
196
|
-
"slack_interaction_inbox",
|
|
197
|
-
"slack_interaction_progress_deliveries",
|
|
198
|
-
"slack_interactions",
|
|
199
|
-
"social_connections",
|
|
200
|
-
"social_posts",
|
|
201
|
-
"transcription_recording_chunks",
|
|
202
|
-
"transcription_recording_objects",
|
|
203
|
-
"transcription_recording_segments",
|
|
204
|
-
"transcription_recordings",
|
|
205
|
-
"usage_events",
|
|
206
|
-
"workspace_artifact_events",
|
|
207
|
-
"workspace_artifact_versions",
|
|
208
|
-
"workspace_artifacts",
|
|
209
|
-
"workspace_captures",
|
|
210
|
-
"workspace_control_events",
|
|
211
|
-
"workspace_inference_controls",
|
|
212
|
-
"workspace_instruction_policy_activation_events",
|
|
213
|
-
"workspace_instruction_policy_heads",
|
|
214
|
-
"workspace_instruction_policy_onboarding_proposals",
|
|
215
|
-
"workspace_instruction_policy_revisions",
|
|
216
|
-
"workspace_instruction_policy_snapshots",
|
|
217
|
-
"workspace_model_policies",
|
|
218
|
-
"workspace_packs",
|
|
219
|
-
"workspace_screenshot_quotas",
|
|
220
|
-
"workspace_session_activity_revisions",
|
|
221
|
-
"workspace_variable_set_variables",
|
|
222
|
-
"workspace_variable_sets"
|
|
223
|
-
];
|
|
224
|
-
var NON_RLS_RUNTIME_TABLES = [
|
|
225
|
-
"auth_identities",
|
|
226
|
-
"auth_rate_limits",
|
|
227
|
-
"auth_sessions",
|
|
228
|
-
"auth_users",
|
|
229
|
-
"auth_verifications",
|
|
230
|
-
"integration_oauth_clients",
|
|
231
|
-
"managed_accounts",
|
|
232
|
-
"nested_agent_depth_configuration",
|
|
233
|
-
"stripe_webhook_events",
|
|
234
|
-
"workspace_memberships",
|
|
235
|
-
"workspaces"
|
|
236
|
-
];
|
|
237
|
-
var RUNTIME_FULL_DML_TABLES = [
|
|
238
|
-
"agent_run_states",
|
|
239
|
-
"api_keys",
|
|
240
|
-
"audit_events",
|
|
241
|
-
"auth_identities",
|
|
242
|
-
"auth_rate_limits",
|
|
243
|
-
"auth_sessions",
|
|
244
|
-
"auth_users",
|
|
245
|
-
"auth_verifications",
|
|
246
|
-
"billing_customers",
|
|
247
|
-
"capability_catalog_items",
|
|
248
|
-
"capability_installations",
|
|
249
|
-
"codex_apps_settings",
|
|
250
|
-
"codex_capacity_waiters",
|
|
251
|
-
"codex_credential_leases",
|
|
252
|
-
"codex_reset_redemption_attempts",
|
|
253
|
-
"codex_rotation_settings",
|
|
254
|
-
"codex_subscription_credentials",
|
|
255
|
-
"composer_drafts",
|
|
256
|
-
"connection_disconnect_operations",
|
|
257
|
-
"connections",
|
|
258
|
-
"connector_action_policies",
|
|
259
|
-
"connector_action_requests",
|
|
260
|
-
"credit_ledger_entries",
|
|
261
|
-
"device_enrollment_requests",
|
|
262
|
-
"document_bases",
|
|
263
|
-
"document_chunks",
|
|
264
|
-
"documents",
|
|
265
|
-
"enrollments",
|
|
266
|
-
"file_uploads",
|
|
267
|
-
"files",
|
|
268
|
-
"github_installation_repositories",
|
|
269
|
-
"github_installations",
|
|
270
|
-
"import_batches",
|
|
271
|
-
"integration_oauth_clients",
|
|
272
|
-
"integration_oauth_state_nonces",
|
|
273
|
-
"knowledge_memories",
|
|
274
|
-
"machine_metrics_latest",
|
|
275
|
-
"machine_metrics_series",
|
|
276
|
-
"machine_removal_operations",
|
|
277
|
-
"managed_accounts",
|
|
278
|
-
"model_call_facts",
|
|
279
|
-
"new_session_drafts",
|
|
280
|
-
"pack_installations",
|
|
281
|
-
"retained_screenshot_artifacts",
|
|
282
|
-
"rig_changes",
|
|
283
|
-
"rig_versions",
|
|
284
|
-
"rigs",
|
|
285
|
-
"sandbox_checkpoint_artifacts",
|
|
286
|
-
"sandbox_lease_holders",
|
|
287
|
-
"sandbox_leases",
|
|
288
|
-
"sandbox_pty_sessions",
|
|
289
|
-
"sandbox_retained_processes",
|
|
290
|
-
"sandbox_session_envelopes",
|
|
291
|
-
"sandbox_workspace_mutation_admissions",
|
|
292
|
-
"sandboxes",
|
|
293
|
-
"scheduled_task_runs",
|
|
294
|
-
"scheduled_tasks",
|
|
295
|
-
"session_attempt_interruptions",
|
|
296
|
-
"session_command_receipts",
|
|
297
|
-
"session_events",
|
|
298
|
-
"session_goals",
|
|
299
|
-
"session_history_items",
|
|
300
|
-
"session_human_input_requests",
|
|
301
|
-
"session_list_snapshots",
|
|
302
|
-
"session_mcp_servers",
|
|
303
|
-
"session_pending_tool_calls",
|
|
304
|
-
"session_pins",
|
|
305
|
-
"session_realtime_connections",
|
|
306
|
-
"session_realtime_context_projections",
|
|
307
|
-
"session_realtime_entries",
|
|
308
|
-
"session_realtime_modes",
|
|
309
|
-
"session_recordings",
|
|
310
|
-
"session_stream_acknowledgments",
|
|
311
|
-
"session_system_update_outbox",
|
|
312
|
-
"session_system_updates",
|
|
313
|
-
"session_turn_attempts",
|
|
314
|
-
"session_turns",
|
|
315
|
-
"session_workflow_wake_outbox",
|
|
316
|
-
"sessions",
|
|
317
|
-
"slack_bot_delete_operations",
|
|
318
|
-
"slack_bot_post_operations",
|
|
319
|
-
"slack_bot_user_links",
|
|
320
|
-
"slack_interaction_inbox",
|
|
321
|
-
"slack_interaction_progress_deliveries",
|
|
322
|
-
"slack_interactions",
|
|
323
|
-
"social_connections",
|
|
324
|
-
"social_posts",
|
|
325
|
-
"stripe_webhook_events",
|
|
326
|
-
"transcription_recording_chunks",
|
|
327
|
-
"transcription_recording_objects",
|
|
328
|
-
"transcription_recording_segments",
|
|
329
|
-
"transcription_recordings",
|
|
330
|
-
"usage_events",
|
|
331
|
-
"workspace_artifacts",
|
|
332
|
-
"workspace_captures",
|
|
333
|
-
"workspace_control_events",
|
|
334
|
-
"workspace_inference_controls",
|
|
335
|
-
"workspace_instruction_policy_heads",
|
|
336
|
-
"workspace_memberships",
|
|
337
|
-
"workspace_model_policies",
|
|
338
|
-
"workspace_packs",
|
|
339
|
-
"workspace_screenshot_quotas",
|
|
340
|
-
"workspace_session_activity_revisions",
|
|
341
|
-
"workspace_variable_set_variables",
|
|
342
|
-
"workspace_variable_sets",
|
|
343
|
-
"workspaces"
|
|
344
|
-
];
|
|
345
|
-
var RUNTIME_READ_ONLY_TABLES = [
|
|
346
|
-
"knowledge_lifecycle_events",
|
|
347
|
-
"knowledge_memory_lifecycle_events",
|
|
348
|
-
"knowledge_memory_relationships",
|
|
349
|
-
"nested_agent_depth_configuration",
|
|
350
|
-
"preference_registry_events",
|
|
351
|
-
"preference_registry_snapshots",
|
|
352
|
-
"workspace_instruction_policy_snapshots"
|
|
353
|
-
];
|
|
354
|
-
var RUNTIME_READ_INSERT_TABLES = [
|
|
355
|
-
"knowledge_change_proposals",
|
|
356
|
-
"knowledge_claim_evidence",
|
|
357
|
-
"knowledge_claim_relations",
|
|
358
|
-
"knowledge_claim_reviews",
|
|
359
|
-
"knowledge_claims",
|
|
360
|
-
"knowledge_document_versions",
|
|
361
|
-
"knowledge_entities",
|
|
362
|
-
"knowledge_entity_aliases",
|
|
363
|
-
"knowledge_facts",
|
|
364
|
-
"knowledge_operation_receipts",
|
|
365
|
-
"knowledge_providers",
|
|
366
|
-
"knowledge_source_acl_versions",
|
|
367
|
-
"knowledge_source_objects",
|
|
368
|
-
"knowledge_sources",
|
|
369
|
-
"knowledge_sync_runs",
|
|
370
|
-
"preference_registry_preferences",
|
|
371
|
-
"preference_registry_revisions",
|
|
372
|
-
"session_spawn_denials",
|
|
373
|
-
"workspace_artifact_events",
|
|
374
|
-
"workspace_artifact_versions",
|
|
375
|
-
"workspace_instruction_policy_activation_events",
|
|
376
|
-
"workspace_instruction_policy_onboarding_proposals",
|
|
377
|
-
"workspace_instruction_policy_revisions"
|
|
378
|
-
];
|
|
379
|
-
var PROTECTED_NO_DIRECT_DML_TABLES = [
|
|
380
|
-
"host_export_config",
|
|
381
|
-
"host_export_consumers",
|
|
382
|
-
"host_export_cursor_state",
|
|
383
|
-
"host_export_dead_letters",
|
|
384
|
-
"host_export_outbox"
|
|
385
|
-
];
|
|
386
|
-
var FULL_DML_PRIVILEGES = ["SELECT", "INSERT", "UPDATE", "DELETE"];
|
|
387
|
-
var RUNTIME_TABLE_PRIVILEGES = Object.freeze({
|
|
388
|
-
...Object.fromEntries(RUNTIME_FULL_DML_TABLES.map((table) => [table, FULL_DML_PRIVILEGES])),
|
|
389
|
-
...Object.fromEntries(RUNTIME_READ_ONLY_TABLES.map((table) => [table, ["SELECT"]])),
|
|
390
|
-
...Object.fromEntries(
|
|
391
|
-
RUNTIME_READ_INSERT_TABLES.map((table) => [table, ["SELECT", "INSERT"]])
|
|
392
|
-
)
|
|
393
|
-
});
|
|
394
|
-
var RUNTIME_DML_TABLES = Object.freeze(
|
|
395
|
-
Object.keys(RUNTIME_TABLE_PRIVILEGES).sort((a, b) => a.localeCompare(b))
|
|
396
|
-
);
|
|
397
|
-
var RuntimeDatabasePostureError = class extends Error {
|
|
398
|
-
violations;
|
|
399
|
-
constructor(violations) {
|
|
400
|
-
super(`Runtime database posture check failed: ${violations.join("; ")}`);
|
|
401
|
-
this.name = "RuntimeDatabasePostureError";
|
|
402
|
-
this.violations = violations;
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
function resultRows(result) {
|
|
406
|
-
if (Array.isArray(result)) {
|
|
407
|
-
return result;
|
|
408
|
-
}
|
|
409
|
-
const rows = result?.rows;
|
|
410
|
-
if (Array.isArray(rows)) {
|
|
411
|
-
return rows;
|
|
412
|
-
}
|
|
413
|
-
throw new Error("Runtime database posture query returned an unsupported result shape");
|
|
414
|
-
}
|
|
415
|
-
function sorted(values) {
|
|
416
|
-
return [...values].sort((a, b) => a.localeCompare(b));
|
|
417
|
-
}
|
|
418
|
-
function difference(left, right) {
|
|
419
|
-
return sorted([...left].filter((value) => !right.has(value)));
|
|
420
|
-
}
|
|
421
|
-
async function inspectRuntimeDatabasePosture(db, options) {
|
|
422
|
-
const targetSchema = options.targetSchema?.trim() || "public";
|
|
423
|
-
return await db.transaction(
|
|
424
|
-
async (tx) => {
|
|
425
|
-
const identityRows = resultRows(
|
|
426
|
-
await tx.execute(sql`
|
|
427
|
-
select
|
|
428
|
-
current_user::text as current_user,
|
|
429
|
-
session_user::text as session_user,
|
|
430
|
-
pg_get_userbyid(d.datdba)::text as database_owner,
|
|
431
|
-
has_database_privilege(current_user, d.oid, 'CONNECT') as can_connect_database,
|
|
432
|
-
has_database_privilege(current_user, d.oid, 'CREATE') as can_create_in_database,
|
|
433
|
-
current_setting('row_security')::text as row_security,
|
|
434
|
-
r.rolcanlogin,
|
|
435
|
-
r.rolsuper,
|
|
436
|
-
r.rolinherit,
|
|
437
|
-
r.rolcreaterole,
|
|
438
|
-
r.rolcreatedb,
|
|
439
|
-
r.rolreplication,
|
|
440
|
-
r.rolbypassrls
|
|
441
|
-
from pg_roles r
|
|
442
|
-
join pg_database d on d.datname = current_database()
|
|
443
|
-
where r.rolname = current_user
|
|
444
|
-
`)
|
|
445
|
-
);
|
|
446
|
-
const identity = identityRows[0];
|
|
447
|
-
if (!identity) {
|
|
448
|
-
throw new Error("Runtime database posture could not resolve the current PostgreSQL role");
|
|
449
|
-
}
|
|
450
|
-
const mappedIdentity = {
|
|
451
|
-
currentUser: identity.current_user,
|
|
452
|
-
sessionUser: identity.session_user,
|
|
453
|
-
databaseOwner: identity.database_owner,
|
|
454
|
-
canConnectDatabase: identity.can_connect_database,
|
|
455
|
-
canCreateInDatabase: identity.can_create_in_database,
|
|
456
|
-
rowSecurity: identity.row_security,
|
|
457
|
-
canLogin: identity.rolcanlogin,
|
|
458
|
-
superuser: identity.rolsuper,
|
|
459
|
-
inherit: identity.rolinherit,
|
|
460
|
-
createRole: identity.rolcreaterole,
|
|
461
|
-
createDatabase: identity.rolcreatedb,
|
|
462
|
-
replication: identity.rolreplication,
|
|
463
|
-
bypassRls: identity.rolbypassrls
|
|
464
|
-
};
|
|
465
|
-
if (options.rlsStrategy === "scoped") {
|
|
466
|
-
return {
|
|
467
|
-
identity: mappedIdentity,
|
|
468
|
-
memberships: [],
|
|
469
|
-
schemas: [],
|
|
470
|
-
ownedSchemas: [],
|
|
471
|
-
ownedRelations: [],
|
|
472
|
-
tables: [],
|
|
473
|
-
privateRoutines: []
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
const relationshipRows = resultRows(
|
|
477
|
-
await tx.execute(sql.raw(roleRelationshipsCatalogQuery("current_user")))
|
|
478
|
-
);
|
|
479
|
-
const memberships = classifyRoleRelationships(relationshipRows).unsafeRelationships;
|
|
480
|
-
const schemas = resultRows(
|
|
481
|
-
await tx.execute(sql`
|
|
482
|
-
select
|
|
483
|
-
n.nspname::text as name,
|
|
484
|
-
pg_get_userbyid(n.nspowner)::text as owner,
|
|
485
|
-
has_schema_privilege(current_user, n.oid, 'USAGE') as usage,
|
|
486
|
-
has_schema_privilege(current_user, n.oid, 'CREATE') as create
|
|
487
|
-
from pg_namespace n
|
|
488
|
-
where n.nspname in (${targetSchema}, 'opengeni_private')
|
|
489
|
-
order by n.nspname
|
|
490
|
-
`)
|
|
491
|
-
);
|
|
492
|
-
const ownedSchemas = resultRows(
|
|
493
|
-
await tx.execute(sql`
|
|
494
|
-
select n.nspname::text as name
|
|
495
|
-
from pg_namespace n
|
|
496
|
-
join pg_roles r on r.oid = n.nspowner
|
|
497
|
-
where r.rolname = current_user
|
|
498
|
-
and n.nspname <> 'information_schema'
|
|
499
|
-
and n.nspname !~ '^pg_'
|
|
500
|
-
order by n.nspname
|
|
501
|
-
`)
|
|
502
|
-
).map((row) => row.name);
|
|
503
|
-
const ownedRelations = resultRows(
|
|
504
|
-
await tx.execute(sql`
|
|
505
|
-
select (n.nspname || '.' || c.relname)::text as name
|
|
506
|
-
from pg_class c
|
|
507
|
-
join pg_namespace n on n.oid = c.relnamespace
|
|
508
|
-
join pg_roles r on r.oid = c.relowner
|
|
509
|
-
where r.rolname = current_user
|
|
510
|
-
and c.relkind in ('r', 'p', 'S', 'v', 'm', 'f')
|
|
511
|
-
and n.nspname <> 'information_schema'
|
|
512
|
-
and n.nspname !~ '^pg_'
|
|
513
|
-
order by n.nspname, c.relname
|
|
514
|
-
`)
|
|
515
|
-
).map((row) => row.name);
|
|
516
|
-
const tables = resultRows(
|
|
517
|
-
await tx.execute(sql`
|
|
518
|
-
select
|
|
519
|
-
c.relname::text as name,
|
|
520
|
-
pg_get_userbyid(c.relowner)::text as owner,
|
|
521
|
-
c.relrowsecurity as rls_enabled,
|
|
522
|
-
c.relforcerowsecurity as rls_forced,
|
|
523
|
-
row_security_active(c.oid) as rls_active,
|
|
524
|
-
(select count(*)::int from pg_policy policy where policy.polrelid = c.oid) as policy_count,
|
|
525
|
-
has_table_privilege(current_user, c.oid, 'SELECT') as can_select,
|
|
526
|
-
has_table_privilege(current_user, c.oid, 'INSERT') as can_insert,
|
|
527
|
-
has_table_privilege(current_user, c.oid, 'UPDATE') as can_update,
|
|
528
|
-
has_table_privilege(current_user, c.oid, 'DELETE') as can_delete,
|
|
529
|
-
has_table_privilege(current_user, c.oid, 'TRUNCATE') as can_truncate,
|
|
530
|
-
has_table_privilege(current_user, c.oid, 'REFERENCES') as can_references,
|
|
531
|
-
has_table_privilege(current_user, c.oid, 'TRIGGER') as can_trigger
|
|
532
|
-
from pg_class c
|
|
533
|
-
join pg_namespace n on n.oid = c.relnamespace
|
|
534
|
-
where n.nspname = ${targetSchema}
|
|
535
|
-
and c.relkind in ('r', 'p')
|
|
536
|
-
order by c.relname
|
|
537
|
-
`)
|
|
538
|
-
).map((row) => ({
|
|
539
|
-
name: row.name,
|
|
540
|
-
owner: row.owner,
|
|
541
|
-
rlsEnabled: row.rls_enabled,
|
|
542
|
-
rlsForced: row.rls_forced,
|
|
543
|
-
rlsActive: row.rls_active,
|
|
544
|
-
policyCount: row.policy_count,
|
|
545
|
-
select: row.can_select,
|
|
546
|
-
insert: row.can_insert,
|
|
547
|
-
update: row.can_update,
|
|
548
|
-
delete: row.can_delete,
|
|
549
|
-
truncate: row.can_truncate,
|
|
550
|
-
references: row.can_references,
|
|
551
|
-
trigger: row.can_trigger
|
|
552
|
-
}));
|
|
553
|
-
const privateRoutines = resultRows(
|
|
554
|
-
await tx.execute(sql`
|
|
555
|
-
select
|
|
556
|
-
(p.proname || '(' || pg_get_function_identity_arguments(p.oid) || ')')::text as name,
|
|
557
|
-
pg_get_userbyid(p.proowner)::text as owner,
|
|
558
|
-
has_function_privilege(current_user, p.oid, 'EXECUTE') as can_execute
|
|
559
|
-
from pg_proc p
|
|
560
|
-
join pg_namespace n on n.oid = p.pronamespace
|
|
561
|
-
where n.nspname = 'opengeni_private'
|
|
562
|
-
and p.prokind in ('f', 'p')
|
|
563
|
-
order by p.proname, pg_get_function_identity_arguments(p.oid)
|
|
564
|
-
`)
|
|
565
|
-
).map((row) => ({
|
|
566
|
-
name: row.name,
|
|
567
|
-
owner: row.owner,
|
|
568
|
-
execute: row.can_execute
|
|
569
|
-
}));
|
|
570
|
-
return {
|
|
571
|
-
identity: mappedIdentity,
|
|
572
|
-
memberships,
|
|
573
|
-
schemas,
|
|
574
|
-
ownedSchemas,
|
|
575
|
-
ownedRelations,
|
|
576
|
-
tables,
|
|
577
|
-
privateRoutines
|
|
578
|
-
};
|
|
579
|
-
},
|
|
580
|
-
{ isolationLevel: "repeatable read", accessMode: "read only" }
|
|
581
|
-
);
|
|
582
|
-
}
|
|
583
|
-
function evaluateRuntimeDatabasePosture(posture, options) {
|
|
584
|
-
const violations = [];
|
|
585
|
-
const identity = posture.identity;
|
|
586
|
-
if (!identity.currentUser || !identity.sessionUser) {
|
|
587
|
-
violations.push("database identity is empty");
|
|
588
|
-
}
|
|
589
|
-
if (identity.currentUser !== identity.sessionUser) {
|
|
590
|
-
violations.push(
|
|
591
|
-
`current_user ${identity.currentUser} does not match session_user ${identity.sessionUser}`
|
|
592
|
-
);
|
|
593
|
-
}
|
|
594
|
-
if (!identity.canConnectDatabase) {
|
|
595
|
-
violations.push("runtime role lacks CONNECT on the current database");
|
|
596
|
-
}
|
|
597
|
-
if (options.rlsStrategy === "scoped") {
|
|
598
|
-
return violations;
|
|
599
|
-
}
|
|
600
|
-
const expectedRole = options.expectedRole?.trim() || "opengeni_app";
|
|
601
|
-
const targetSchema = options.targetSchema?.trim() || "public";
|
|
602
|
-
const protectedTables = new Set(options.protectedTables ?? FORCE_RLS_TABLES);
|
|
603
|
-
const tablePrivileges = options.tablePrivileges ?? RUNTIME_TABLE_PRIVILEGES;
|
|
604
|
-
const directRuntimeTables = new Set(Object.keys(tablePrivileges));
|
|
605
|
-
const protectedNoDirectDmlTables = new Set(
|
|
606
|
-
options.protectedNoDirectDmlTables ?? (options.protectedTables ? [] : PROTECTED_NO_DIRECT_DML_TABLES)
|
|
607
|
-
);
|
|
608
|
-
if (identity.currentUser !== expectedRole || identity.sessionUser !== expectedRole) {
|
|
609
|
-
violations.push(
|
|
610
|
-
`runtime identity must be ${expectedRole} (current_user=${identity.currentUser}, session_user=${identity.sessionUser})`
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
if (!identity.canLogin) violations.push("runtime role is not LOGIN");
|
|
614
|
-
if (identity.superuser) violations.push("runtime role is SUPERUSER");
|
|
615
|
-
if (identity.bypassRls) violations.push("runtime role has BYPASSRLS");
|
|
616
|
-
if (identity.createRole) violations.push("runtime role has CREATEROLE");
|
|
617
|
-
if (identity.createDatabase) violations.push("runtime role has CREATEDB");
|
|
618
|
-
if (identity.replication) violations.push("runtime role has REPLICATION");
|
|
619
|
-
if (identity.inherit) violations.push("runtime role must be NOINHERIT");
|
|
620
|
-
if (identity.databaseOwner === expectedRole) violations.push("runtime role owns the database");
|
|
621
|
-
if (identity.canCreateInDatabase) {
|
|
622
|
-
violations.push("runtime role has CREATE on the current database");
|
|
623
|
-
}
|
|
624
|
-
if (identity.rowSecurity.toLowerCase() !== "on") {
|
|
625
|
-
violations.push(`row_security is ${identity.rowSecurity}, expected on`);
|
|
626
|
-
}
|
|
627
|
-
if (posture.memberships.length > 0) {
|
|
628
|
-
violations.push(`runtime role has memberships: ${sorted(posture.memberships).join(", ")}`);
|
|
629
|
-
}
|
|
630
|
-
if (posture.ownedSchemas.length > 0) {
|
|
631
|
-
violations.push(`runtime role owns schemas: ${sorted(posture.ownedSchemas).join(", ")}`);
|
|
632
|
-
}
|
|
633
|
-
if (posture.ownedRelations.length > 0) {
|
|
634
|
-
violations.push(`runtime role owns relations: ${sorted(posture.ownedRelations).join(", ")}`);
|
|
635
|
-
}
|
|
636
|
-
for (const schemaName of /* @__PURE__ */ new Set([targetSchema, "opengeni_private"])) {
|
|
637
|
-
const schema = posture.schemas.find((candidate) => candidate.name === schemaName);
|
|
638
|
-
if (!schema) {
|
|
639
|
-
violations.push(`required schema ${schemaName} is missing`);
|
|
640
|
-
continue;
|
|
641
|
-
}
|
|
642
|
-
if (schema.owner === expectedRole) {
|
|
643
|
-
violations.push(`runtime role owns schema ${schemaName}`);
|
|
644
|
-
}
|
|
645
|
-
if (!schema.usage) violations.push(`runtime role lacks USAGE on schema ${schemaName}`);
|
|
646
|
-
if (schema.create) violations.push(`runtime role has CREATE on schema ${schemaName}`);
|
|
647
|
-
}
|
|
648
|
-
const tableByName = new Map(posture.tables.map((table) => [table.name, table]));
|
|
649
|
-
const actualRlsTables = new Set(
|
|
650
|
-
posture.tables.filter((table) => table.rlsEnabled).map((table) => table.name)
|
|
651
|
-
);
|
|
652
|
-
const classifiedProtectedTables = /* @__PURE__ */ new Set([
|
|
653
|
-
...directRuntimeTables,
|
|
654
|
-
...protectedNoDirectDmlTables
|
|
655
|
-
]);
|
|
656
|
-
const unclassifiedProtectedTables = difference(protectedTables, classifiedProtectedTables);
|
|
657
|
-
if (unclassifiedProtectedTables.length > 0) {
|
|
658
|
-
violations.push(
|
|
659
|
-
`protected tables lack an explicit privilege class: ${unclassifiedProtectedTables.join(", ")}`
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
const protectedNoDirectDmlOverlap = difference(
|
|
663
|
-
protectedNoDirectDmlTables,
|
|
664
|
-
new Set([...protectedNoDirectDmlTables].filter((table) => !directRuntimeTables.has(table)))
|
|
665
|
-
);
|
|
666
|
-
if (protectedNoDirectDmlOverlap.length > 0) {
|
|
667
|
-
violations.push(
|
|
668
|
-
`protected no-direct-DML tables also declare runtime privileges: ${protectedNoDirectDmlOverlap.join(", ")}`
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
const nonProtectedNoDirectDmlTables = difference(protectedNoDirectDmlTables, protectedTables);
|
|
672
|
-
if (nonProtectedNoDirectDmlTables.length > 0) {
|
|
673
|
-
violations.push(
|
|
674
|
-
`no-direct-DML tables are absent from the protected contract: ${nonProtectedNoDirectDmlTables.join(", ")}`
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
const catalogTables = new Set(tableByName.keys());
|
|
678
|
-
const missingRuntimeTables = difference(directRuntimeTables, catalogTables);
|
|
679
|
-
if (missingRuntimeTables.length > 0) {
|
|
680
|
-
violations.push(`runtime privilege tables are missing: ${missingRuntimeTables.join(", ")}`);
|
|
681
|
-
}
|
|
682
|
-
const missingTables = difference(protectedTables, catalogTables);
|
|
683
|
-
if (missingTables.length > 0) {
|
|
684
|
-
violations.push(`protected tables are missing: ${missingTables.join(", ")}`);
|
|
685
|
-
}
|
|
686
|
-
const undeclaredRlsTables = difference(actualRlsTables, protectedTables);
|
|
687
|
-
if (undeclaredRlsTables.length > 0) {
|
|
688
|
-
violations.push(
|
|
689
|
-
`RLS tables are absent from the declared contract: ${undeclaredRlsTables.join(", ")}`
|
|
690
|
-
);
|
|
691
|
-
}
|
|
692
|
-
for (const table of posture.tables) {
|
|
693
|
-
const privileges = [
|
|
694
|
-
["SELECT", table.select],
|
|
695
|
-
["INSERT", table.insert],
|
|
696
|
-
["UPDATE", table.update],
|
|
697
|
-
["DELETE", table.delete],
|
|
698
|
-
["TRUNCATE", table.truncate],
|
|
699
|
-
["REFERENCES", table.references],
|
|
700
|
-
["TRIGGER", table.trigger]
|
|
701
|
-
];
|
|
702
|
-
const expectedPrivileges = new Set(tablePrivileges[table.name] ?? []);
|
|
703
|
-
if (table.owner === expectedRole) {
|
|
704
|
-
violations.push(`runtime role owns table ${table.name}`);
|
|
705
|
-
}
|
|
706
|
-
const missingPrivileges = privileges.filter(([privilege, granted]) => expectedPrivileges.has(privilege) && !granted).map(([privilege]) => privilege);
|
|
707
|
-
if (missingPrivileges.length > 0) {
|
|
708
|
-
violations.push(
|
|
709
|
-
`table ${table.name} lacks required runtime privileges: ${missingPrivileges.join(", ")}`
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
const excessPrivileges = privileges.filter(([privilege, granted]) => !expectedPrivileges.has(privilege) && granted).map(([privilege]) => privilege);
|
|
713
|
-
if (excessPrivileges.length > 0) {
|
|
714
|
-
violations.push(
|
|
715
|
-
`table ${table.name} grants excess runtime privileges: ${excessPrivileges.join(", ")}`
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
for (const tableName of protectedTables) {
|
|
720
|
-
const table = tableByName.get(tableName);
|
|
721
|
-
if (!table) continue;
|
|
722
|
-
if (!table.rlsEnabled) violations.push(`table ${tableName} does not ENABLE RLS`);
|
|
723
|
-
if (!table.rlsForced) violations.push(`table ${tableName} does not FORCE RLS`);
|
|
724
|
-
if (!table.rlsActive) violations.push(`table ${tableName} has inactive RLS for runtime role`);
|
|
725
|
-
if (table.policyCount < 1) violations.push(`table ${tableName} has no RLS policy`);
|
|
726
|
-
}
|
|
727
|
-
if (posture.privateRoutines.length === 0) {
|
|
728
|
-
violations.push("opengeni_private has no helper routines");
|
|
729
|
-
}
|
|
730
|
-
for (const routine of posture.privateRoutines) {
|
|
731
|
-
if (routine.owner === expectedRole) {
|
|
732
|
-
violations.push(`runtime role owns private routine ${routine.name}`);
|
|
733
|
-
}
|
|
734
|
-
if (!routine.execute) {
|
|
735
|
-
violations.push(`runtime role lacks EXECUTE on private routine ${routine.name}`);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
return violations;
|
|
739
|
-
}
|
|
740
|
-
async function assertRuntimeDatabasePosture(db, options) {
|
|
741
|
-
const posture = await inspectRuntimeDatabasePosture(db, options);
|
|
742
|
-
const violations = evaluateRuntimeDatabasePosture(posture, options);
|
|
743
|
-
if (violations.length > 0) {
|
|
744
|
-
throw new RuntimeDatabasePostureError(violations);
|
|
745
|
-
}
|
|
746
|
-
return posture;
|
|
747
|
-
}
|
|
748
|
-
function runtimeDatabaseReadyCheck(db, options) {
|
|
749
|
-
return async () => {
|
|
750
|
-
await assertRuntimeDatabasePosture(db, options);
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// src/provision-roles.ts
|
|
755
|
-
async function provisionRoles(adminConnection, options = {}) {
|
|
756
|
-
const schema = validateIdentifier("targetSchema", options.targetSchema ?? "public");
|
|
757
|
-
const rlsStrategy = options.rlsStrategy ?? "force";
|
|
758
|
-
const appRole = validateIdentifier(
|
|
759
|
-
"appRole",
|
|
760
|
-
options.appRole ?? (process.env.OPENGENI_APP_DATABASE_USER?.trim() || "opengeni_app")
|
|
761
|
-
);
|
|
762
|
-
const appPassword = options.appPassword ?? process.env.OPENGENI_APP_DATABASE_PASSWORD;
|
|
763
|
-
const hostExportRole = validateIdentifier(
|
|
764
|
-
"hostExportRole",
|
|
765
|
-
options.hostExportRole ?? (process.env.OPENGENI_HOST_EXPORT_DATABASE_USER?.trim() || "opengeni_host_exporter")
|
|
766
|
-
);
|
|
767
|
-
const hostExportPassword = options.hostExportPassword ?? process.env.OPENGENI_HOST_EXPORT_DATABASE_PASSWORD;
|
|
768
|
-
const temporalRole = validateIdentifier(
|
|
769
|
-
"temporalRole",
|
|
770
|
-
options.temporalRole ?? (process.env.OPENGENI_TEMPORAL_DATABASE_USER?.trim() || "opengeni_temporal")
|
|
771
|
-
);
|
|
772
|
-
const temporalPassword = options.temporalPassword ?? process.env.OPENGENI_TEMPORAL_DATABASE_PASSWORD;
|
|
773
|
-
const temporalDatabases = (options.temporalDatabases ?? commaSeparated(process.env.OPENGENI_TEMPORAL_DATABASES ?? "temporal,temporal_visibility")).map((name) => validateIdentifier("temporalDatabases", name));
|
|
774
|
-
const sql2 = postgres(adminConnection, { max: 1 });
|
|
775
|
-
try {
|
|
776
|
-
let provisionedAppRole = null;
|
|
777
|
-
if (rlsStrategy === "force") {
|
|
778
|
-
if (!appPassword) {
|
|
779
|
-
throw new Error(
|
|
780
|
-
"OPENGENI_APP_DATABASE_PASSWORD (or appPassword) is required for rlsStrategy 'force'"
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
await assertAppRoleSafeToNormalize(sql2, appRole);
|
|
784
|
-
await ensureRestrictedAppLoginRole(sql2, appRole, appPassword);
|
|
785
|
-
provisionedAppRole = appRole;
|
|
786
|
-
}
|
|
787
|
-
if (temporalPassword) {
|
|
788
|
-
await ensureLoginRole(sql2, temporalRole, temporalPassword);
|
|
789
|
-
for (const database of temporalDatabases) {
|
|
790
|
-
await ensureDatabase(sql2, database, temporalRole);
|
|
791
|
-
await grantTemporalRoleInDatabase(adminConnection, database, temporalRole);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
if (hostExportPassword) {
|
|
795
|
-
await ensureLoginRole(sql2, hostExportRole, hostExportPassword);
|
|
796
|
-
await grantHostExportRoleIfSchemaExists(sql2, hostExportRole);
|
|
797
|
-
}
|
|
798
|
-
if (rlsStrategy === "force") {
|
|
799
|
-
await grantAppRoleIfSchemaExists(sql2, appRole, schema);
|
|
800
|
-
}
|
|
801
|
-
return {
|
|
802
|
-
appRole: provisionedAppRole,
|
|
803
|
-
hostExportRole: hostExportPassword ? hostExportRole : null,
|
|
804
|
-
temporalRole: temporalPassword ? temporalRole : null,
|
|
805
|
-
temporalDatabases: temporalPassword ? temporalDatabases : [],
|
|
806
|
-
schema,
|
|
807
|
-
rlsStrategy
|
|
808
|
-
};
|
|
809
|
-
} finally {
|
|
810
|
-
await sql2.end();
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
async function grantHostExportRoleIfSchemaExists(sql2, role) {
|
|
814
|
-
await sql2.unsafe(`
|
|
815
|
-
DO $$
|
|
816
|
-
BEGIN
|
|
817
|
-
IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'opengeni_host_export') THEN
|
|
818
|
-
EXECUTE format('GRANT USAGE ON SCHEMA opengeni_host_export TO %I', ${literal(role)});
|
|
819
|
-
EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA opengeni_host_export TO %I', ${literal(role)});
|
|
820
|
-
EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA opengeni_host_export GRANT EXECUTE ON FUNCTIONS TO %I', ${literal(role)});
|
|
821
|
-
END IF;
|
|
822
|
-
END $$;
|
|
823
|
-
`);
|
|
824
|
-
}
|
|
825
|
-
async function ensureLoginRole(sql2, role, password) {
|
|
826
|
-
await sql2.unsafe(`
|
|
827
|
-
DO $$
|
|
828
|
-
BEGIN
|
|
829
|
-
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = ${literal(role)}) THEN
|
|
830
|
-
EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', ${literal(role)}, ${literal(password)});
|
|
831
|
-
ELSE
|
|
832
|
-
EXECUTE format('ALTER ROLE %I LOGIN PASSWORD %L', ${literal(role)}, ${literal(password)});
|
|
833
|
-
END IF;
|
|
834
|
-
END $$;
|
|
835
|
-
`);
|
|
836
|
-
}
|
|
837
|
-
async function ensureRestrictedAppLoginRole(sql2, role, password) {
|
|
838
|
-
await sql2.unsafe(`
|
|
839
|
-
DO $$
|
|
840
|
-
BEGIN
|
|
841
|
-
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = ${literal(role)}) THEN
|
|
842
|
-
EXECUTE format(
|
|
843
|
-
'CREATE ROLE %I WITH LOGIN NOSUPERUSER NOBYPASSRLS NOCREATEROLE NOCREATEDB NOREPLICATION NOINHERIT PASSWORD %L',
|
|
844
|
-
${literal(role)},
|
|
845
|
-
${literal(password)}
|
|
846
|
-
);
|
|
847
|
-
ELSIF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = current_user AND rolsuper) THEN
|
|
848
|
-
EXECUTE format(
|
|
849
|
-
'ALTER ROLE %I WITH LOGIN NOSUPERUSER NOBYPASSRLS NOCREATEROLE NOCREATEDB NOREPLICATION NOINHERIT PASSWORD %L',
|
|
850
|
-
${literal(role)},
|
|
851
|
-
${literal(password)}
|
|
852
|
-
);
|
|
853
|
-
ELSE
|
|
854
|
-
-- PostgreSQL requires elevated attributes merely to name SUPERUSER,
|
|
855
|
-
-- BYPASSRLS, REPLICATION, or CREATEDB in ALTER ROLE, even when setting them
|
|
856
|
-
-- false. The preflight has already proved those protected attributes are
|
|
857
|
-
-- false, so a managed CREATEROLE administrator converges only the
|
|
858
|
-
-- attributes it may change.
|
|
859
|
-
EXECUTE format(
|
|
860
|
-
'ALTER ROLE %I WITH LOGIN NOCREATEROLE NOINHERIT PASSWORD %L',
|
|
861
|
-
${literal(role)},
|
|
862
|
-
${literal(password)}
|
|
863
|
-
);
|
|
864
|
-
END IF;
|
|
865
|
-
END $$;
|
|
866
|
-
`);
|
|
867
|
-
}
|
|
868
|
-
async function assertAppRoleSafeToNormalize(sql2, role) {
|
|
869
|
-
const exists = await sql2`
|
|
870
|
-
select exists(select 1 from pg_roles where rolname = ${role}) as exists
|
|
871
|
-
`;
|
|
872
|
-
if (!exists[0]?.exists) {
|
|
873
|
-
return;
|
|
874
|
-
}
|
|
875
|
-
const [roleAttributes] = await sql2`
|
|
876
|
-
select
|
|
877
|
-
administrator.rolsuper as administrator_superuser,
|
|
878
|
-
app.rolsuper as app_superuser,
|
|
879
|
-
app.rolbypassrls as app_bypass_rls,
|
|
880
|
-
app.rolcreatedb as app_create_database,
|
|
881
|
-
app.rolreplication as app_replication
|
|
882
|
-
from pg_roles app
|
|
883
|
-
join pg_roles administrator on administrator.rolname = current_user
|
|
884
|
-
where app.rolname = ${role}
|
|
885
|
-
`;
|
|
886
|
-
if (roleAttributes && !roleAttributes.administrator_superuser) {
|
|
887
|
-
const protectedAttributes = [
|
|
888
|
-
roleAttributes.app_superuser ? "SUPERUSER" : null,
|
|
889
|
-
roleAttributes.app_bypass_rls ? "BYPASSRLS" : null,
|
|
890
|
-
roleAttributes.app_create_database ? "CREATEDB" : null,
|
|
891
|
-
roleAttributes.app_replication ? "REPLICATION" : null
|
|
892
|
-
].filter((attribute) => attribute !== null);
|
|
893
|
-
if (protectedAttributes.length > 0) {
|
|
894
|
-
throw new Error(
|
|
895
|
-
`Refusing to normalize app role ${role}: a non-superuser administrator cannot clear protected attributes (${protectedAttributes.join(", ")})`
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
const relationshipRows = await sql2.unsafe(
|
|
900
|
-
roleRelationshipsCatalogQuery("$1"),
|
|
901
|
-
[role]
|
|
902
|
-
);
|
|
903
|
-
const { unsafeRelationships } = classifyRoleRelationships(relationshipRows);
|
|
904
|
-
if (unsafeRelationships.length > 0) {
|
|
905
|
-
throw new Error(
|
|
906
|
-
`Refusing to normalize app role ${role}: remove privilege-bearing role relationships first (${unsafeRelationships.join(", ")})`
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
const ownedObjects = await sql2`
|
|
910
|
-
select ('database:' || d.datname)::text as object_name
|
|
911
|
-
from pg_database d
|
|
912
|
-
join pg_roles owner on owner.oid = d.datdba
|
|
913
|
-
where owner.rolname = ${role}
|
|
914
|
-
union all
|
|
915
|
-
select ('schema:' || n.nspname)::text as object_name
|
|
916
|
-
from pg_namespace n
|
|
917
|
-
join pg_roles owner on owner.oid = n.nspowner
|
|
918
|
-
where owner.rolname = ${role}
|
|
919
|
-
and n.nspname <> 'information_schema'
|
|
920
|
-
and n.nspname !~ '^pg_'
|
|
921
|
-
union all
|
|
922
|
-
select ('relation:' || n.nspname || '.' || c.relname)::text as object_name
|
|
923
|
-
from pg_class c
|
|
924
|
-
join pg_namespace n on n.oid = c.relnamespace
|
|
925
|
-
join pg_roles owner on owner.oid = c.relowner
|
|
926
|
-
where owner.rolname = ${role}
|
|
927
|
-
and n.nspname <> 'information_schema'
|
|
928
|
-
and n.nspname !~ '^pg_'
|
|
929
|
-
union all
|
|
930
|
-
select (
|
|
931
|
-
'routine:' || n.nspname || '.' || p.proname || '(' ||
|
|
932
|
-
pg_get_function_identity_arguments(p.oid) || ')'
|
|
933
|
-
)::text as object_name
|
|
934
|
-
from pg_proc p
|
|
935
|
-
join pg_namespace n on n.oid = p.pronamespace
|
|
936
|
-
join pg_roles owner on owner.oid = p.proowner
|
|
937
|
-
where owner.rolname = ${role}
|
|
938
|
-
and n.nspname <> 'information_schema'
|
|
939
|
-
and n.nspname !~ '^pg_'
|
|
940
|
-
order by object_name
|
|
941
|
-
`;
|
|
942
|
-
if (ownedObjects.length > 0) {
|
|
943
|
-
throw new Error(
|
|
944
|
-
`Refusing to normalize app role ${role}: transfer owned objects first (${ownedObjects.map((row) => row.object_name).join(", ")})`
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
async function ensureDatabase(sql2, database, owner) {
|
|
949
|
-
const existing = await sql2`
|
|
950
|
-
select exists(select 1 from pg_database where datname = ${database}) as exists
|
|
951
|
-
`;
|
|
952
|
-
if (!existing[0]?.exists) {
|
|
953
|
-
await sql2.unsafe(`CREATE DATABASE ${identifier(database)} OWNER ${identifier(owner)}`);
|
|
954
|
-
}
|
|
955
|
-
await sql2.unsafe(
|
|
956
|
-
`GRANT ALL PRIVILEGES ON DATABASE ${identifier(database)} TO ${identifier(owner)}`
|
|
957
|
-
);
|
|
958
|
-
}
|
|
959
|
-
async function grantTemporalRoleInDatabase(adminConnection, database, role) {
|
|
960
|
-
const databaseUrl = databaseUrlFor(adminConnection, database);
|
|
961
|
-
const databaseSql = postgres(databaseUrl, { max: 1 });
|
|
962
|
-
try {
|
|
963
|
-
await databaseSql.unsafe(`GRANT USAGE, CREATE ON SCHEMA public TO ${identifier(role)}`);
|
|
964
|
-
} finally {
|
|
965
|
-
await databaseSql.end();
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
async function grantAppRoleIfSchemaExists(sql2, role, schema) {
|
|
969
|
-
const runtimeFullDmlTables = `ARRAY[${RUNTIME_FULL_DML_TABLES.map(literal).join(", ")}]`;
|
|
970
|
-
const runtimeReadOnlyTables = `ARRAY[${RUNTIME_READ_ONLY_TABLES.map(literal).join(", ")}]`;
|
|
971
|
-
const runtimeReadInsertTables = `ARRAY[${RUNTIME_READ_INSERT_TABLES.map(literal).join(", ")}]`;
|
|
972
|
-
await sql2.unsafe(`
|
|
973
|
-
DO $$
|
|
974
|
-
DECLARE
|
|
975
|
-
owner_role text := current_user;
|
|
976
|
-
runtime_table text;
|
|
977
|
-
BEGIN
|
|
978
|
-
EXECUTE format('REVOKE CREATE ON DATABASE %I FROM %I', current_database(), ${literal(role)});
|
|
979
|
-
IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = ${literal(schema)}) THEN
|
|
980
|
-
EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});
|
|
981
|
-
EXECUTE format('REVOKE CREATE ON SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});
|
|
982
|
-
EXECUTE format('REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA %I FROM %I', ${literal(schema)}, ${literal(role)});
|
|
983
|
-
FOREACH runtime_table IN ARRAY ${runtimeFullDmlTables} LOOP
|
|
984
|
-
IF to_regclass(format('%I.%I', ${literal(schema)}, runtime_table)) IS NOT NULL THEN
|
|
985
|
-
EXECUTE format(
|
|
986
|
-
'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.%I TO %I',
|
|
987
|
-
${literal(schema)},
|
|
988
|
-
runtime_table,
|
|
989
|
-
${literal(role)}
|
|
990
|
-
);
|
|
991
|
-
END IF;
|
|
992
|
-
END LOOP;
|
|
993
|
-
FOREACH runtime_table IN ARRAY ${runtimeReadOnlyTables} LOOP
|
|
994
|
-
IF to_regclass(format('%I.%I', ${literal(schema)}, runtime_table)) IS NOT NULL THEN
|
|
995
|
-
EXECUTE format(
|
|
996
|
-
'GRANT SELECT ON TABLE %I.%I TO %I',
|
|
997
|
-
${literal(schema)},
|
|
998
|
-
runtime_table,
|
|
999
|
-
${literal(role)}
|
|
1000
|
-
);
|
|
1001
|
-
END IF;
|
|
1002
|
-
END LOOP;
|
|
1003
|
-
FOREACH runtime_table IN ARRAY ${runtimeReadInsertTables} LOOP
|
|
1004
|
-
IF to_regclass(format('%I.%I', ${literal(schema)}, runtime_table)) IS NOT NULL THEN
|
|
1005
|
-
EXECUTE format(
|
|
1006
|
-
'GRANT SELECT, INSERT ON TABLE %I.%I TO %I',
|
|
1007
|
-
${literal(schema)},
|
|
1008
|
-
runtime_table,
|
|
1009
|
-
${literal(role)}
|
|
1010
|
-
);
|
|
1011
|
-
END IF;
|
|
1012
|
-
END LOOP;
|
|
1013
|
-
-- Migration 0110 creates this target-schema-local SECURITY DEFINER
|
|
1014
|
-
-- capability before opengeni_app may exist. Re-converge its exact EXECUTE
|
|
1015
|
-
-- grant here so the supported migrate-then-provision order is equivalent to
|
|
1016
|
-
-- provisioning the role before that migration.
|
|
1017
|
-
IF to_regprocedure(
|
|
1018
|
-
format('%I.lock_nested_agent_depth_configuration()', ${literal(schema)})
|
|
1019
|
-
) IS NOT NULL THEN
|
|
1020
|
-
EXECUTE format(
|
|
1021
|
-
'GRANT EXECUTE ON FUNCTION %I.lock_nested_agent_depth_configuration() TO %I',
|
|
1022
|
-
${literal(schema)},
|
|
1023
|
-
${literal(role)}
|
|
1024
|
-
);
|
|
1025
|
-
END IF;
|
|
1026
|
-
IF to_regprocedure(
|
|
1027
|
-
format(
|
|
1028
|
-
'%I.preference_registry_apply_lifecycle(text,uuid,integer,uuid,uuid,text,uuid,text,text)',
|
|
1029
|
-
${literal(schema)}
|
|
1030
|
-
)
|
|
1031
|
-
) IS NOT NULL THEN
|
|
1032
|
-
EXECUTE format(
|
|
1033
|
-
'GRANT EXECUTE ON FUNCTION %I.preference_registry_apply_lifecycle(text, uuid, integer, uuid, uuid, text, uuid, text, text) TO %I',
|
|
1034
|
-
${literal(schema)},
|
|
1035
|
-
${literal(role)}
|
|
1036
|
-
);
|
|
1037
|
-
END IF;
|
|
1038
|
-
IF to_regprocedure(
|
|
1039
|
-
format('%I.preference_registry_lock_heads(uuid[])', ${literal(schema)})
|
|
1040
|
-
) IS NOT NULL THEN
|
|
1041
|
-
EXECUTE format(
|
|
1042
|
-
'GRANT EXECUTE ON FUNCTION %I.preference_registry_lock_heads(uuid[]) TO %I',
|
|
1043
|
-
${literal(schema)},
|
|
1044
|
-
${literal(role)}
|
|
1045
|
-
);
|
|
1046
|
-
END IF;
|
|
1047
|
-
IF to_regprocedure(
|
|
1048
|
-
format(
|
|
1049
|
-
'%I.knowledge_memory_apply_operation(jsonb,text,text,text,uuid,uuid,uuid,integer)',
|
|
1050
|
-
${literal(schema)}
|
|
1051
|
-
)
|
|
1052
|
-
) IS NOT NULL THEN
|
|
1053
|
-
EXECUTE format(
|
|
1054
|
-
'GRANT EXECUTE ON FUNCTION %I.knowledge_memory_apply_operation(jsonb, text, text, text, uuid, uuid, uuid, integer) TO %I',
|
|
1055
|
-
${literal(schema)},
|
|
1056
|
-
${literal(role)}
|
|
1057
|
-
);
|
|
1058
|
-
END IF;
|
|
1059
|
-
IF to_regprocedure(
|
|
1060
|
-
format(
|
|
1061
|
-
'%I.knowledge_memory_revert_operation(uuid,uuid,text,text,text,uuid,uuid,uuid,integer)',
|
|
1062
|
-
${literal(schema)}
|
|
1063
|
-
)
|
|
1064
|
-
) IS NOT NULL THEN
|
|
1065
|
-
EXECUTE format(
|
|
1066
|
-
'GRANT EXECUTE ON FUNCTION %I.knowledge_memory_revert_operation(uuid, uuid, text, text, text, uuid, uuid, uuid, integer) TO %I',
|
|
1067
|
-
${literal(schema)},
|
|
1068
|
-
${literal(role)}
|
|
1069
|
-
);
|
|
1070
|
-
END IF;
|
|
1071
|
-
IF to_regprocedure(
|
|
1072
|
-
format(
|
|
1073
|
-
'%I.preference_registry_get_or_create_snapshot(uuid,uuid,uuid,uuid,uuid,integer)',
|
|
1074
|
-
${literal(schema)}
|
|
1075
|
-
)
|
|
1076
|
-
) IS NOT NULL THEN
|
|
1077
|
-
EXECUTE format(
|
|
1078
|
-
'GRANT EXECUTE ON FUNCTION %I.preference_registry_get_or_create_snapshot(uuid, uuid, uuid, uuid, uuid, integer) TO %I',
|
|
1079
|
-
${literal(schema)},
|
|
1080
|
-
${literal(role)}
|
|
1081
|
-
);
|
|
1082
|
-
END IF;
|
|
1083
|
-
IF to_regprocedure(
|
|
1084
|
-
format(
|
|
1085
|
-
'%I.workspace_instruction_policy_get_or_create_snapshot(uuid,uuid,uuid,uuid,uuid,integer)',
|
|
1086
|
-
${literal(schema)}
|
|
1087
|
-
)
|
|
1088
|
-
) IS NOT NULL THEN
|
|
1089
|
-
EXECUTE format(
|
|
1090
|
-
'GRANT EXECUTE ON FUNCTION %I.workspace_instruction_policy_get_or_create_snapshot(uuid, uuid, uuid, uuid, uuid, integer) TO %I',
|
|
1091
|
-
${literal(schema)},
|
|
1092
|
-
${literal(role)}
|
|
1093
|
-
);
|
|
1094
|
-
END IF;
|
|
1095
|
-
IF to_regprocedure(
|
|
1096
|
-
format(
|
|
1097
|
-
'%I.scoped_knowledge_apply_lifecycle(uuid,text,uuid,text,bigint,text,text,text,text,text,text)',
|
|
1098
|
-
${literal(schema)}
|
|
1099
|
-
)
|
|
1100
|
-
) IS NOT NULL THEN
|
|
1101
|
-
EXECUTE format(
|
|
1102
|
-
'GRANT EXECUTE ON FUNCTION %I.scoped_knowledge_apply_lifecycle(uuid, text, uuid, text, bigint, text, text, text, text, text, text) TO %I',
|
|
1103
|
-
${literal(schema)},
|
|
1104
|
-
${literal(role)}
|
|
1105
|
-
);
|
|
1106
|
-
END IF;
|
|
1107
|
-
IF to_regprocedure(
|
|
1108
|
-
format(
|
|
1109
|
-
'%I.scoped_knowledge_advance_source_acl(uuid,uuid,bigint,bigint,uuid,text,text,text,text,text,text)',
|
|
1110
|
-
${literal(schema)}
|
|
1111
|
-
)
|
|
1112
|
-
) IS NOT NULL THEN
|
|
1113
|
-
EXECUTE format(
|
|
1114
|
-
'GRANT EXECUTE ON FUNCTION %I.scoped_knowledge_advance_source_acl(uuid, uuid, bigint, bigint, uuid, text, text, text, text, text, text) TO %I',
|
|
1115
|
-
${literal(schema)},
|
|
1116
|
-
${literal(role)}
|
|
1117
|
-
);
|
|
1118
|
-
END IF;
|
|
1119
|
-
IF to_regprocedure(
|
|
1120
|
-
format(
|
|
1121
|
-
'%I.scoped_knowledge_complete_sync(uuid,uuid,text,text,timestamptz,jsonb,text,text,text)',
|
|
1122
|
-
${literal(schema)}
|
|
1123
|
-
)
|
|
1124
|
-
) IS NOT NULL THEN
|
|
1125
|
-
EXECUTE format(
|
|
1126
|
-
'GRANT EXECUTE ON FUNCTION %I.scoped_knowledge_complete_sync(uuid, uuid, text, text, timestamptz, jsonb, text, text, text) TO %I',
|
|
1127
|
-
${literal(schema)},
|
|
1128
|
-
${literal(role)}
|
|
1129
|
-
);
|
|
1130
|
-
END IF;
|
|
1131
|
-
IF to_regprocedure(
|
|
1132
|
-
format(
|
|
1133
|
-
'%I.scoped_knowledge_advance_object_version(uuid,uuid,bigint,bigint,uuid,text,text,text,text,text,text)',
|
|
1134
|
-
${literal(schema)}
|
|
1135
|
-
)
|
|
1136
|
-
) IS NOT NULL THEN
|
|
1137
|
-
EXECUTE format(
|
|
1138
|
-
'GRANT EXECUTE ON FUNCTION %I.scoped_knowledge_advance_object_version(uuid, uuid, bigint, bigint, uuid, text, text, text, text, text, text) TO %I',
|
|
1139
|
-
${literal(schema)},
|
|
1140
|
-
${literal(role)}
|
|
1141
|
-
);
|
|
1142
|
-
END IF;
|
|
1143
|
-
EXECUTE format('GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA %I TO %I', ${literal(schema)}, ${literal(role)});
|
|
1144
|
-
EXECUTE format(
|
|
1145
|
-
'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I REVOKE ALL PRIVILEGES ON TABLES FROM %I',
|
|
1146
|
-
owner_role,
|
|
1147
|
-
${literal(schema)},
|
|
1148
|
-
${literal(role)}
|
|
1149
|
-
);
|
|
1150
|
-
EXECUTE format(
|
|
1151
|
-
'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA %I GRANT USAGE, SELECT ON SEQUENCES TO %I',
|
|
1152
|
-
owner_role,
|
|
1153
|
-
${literal(schema)},
|
|
1154
|
-
${literal(role)}
|
|
1155
|
-
);
|
|
1156
|
-
END IF;
|
|
1157
|
-
IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'opengeni_private') THEN
|
|
1158
|
-
EXECUTE format('GRANT USAGE ON SCHEMA opengeni_private TO %I', ${literal(role)});
|
|
1159
|
-
EXECUTE format('REVOKE CREATE ON SCHEMA opengeni_private FROM %I', ${literal(role)});
|
|
1160
|
-
EXECUTE format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA opengeni_private TO %I', ${literal(role)});
|
|
1161
|
-
EXECUTE format(
|
|
1162
|
-
'ALTER DEFAULT PRIVILEGES FOR ROLE %I IN SCHEMA opengeni_private GRANT EXECUTE ON FUNCTIONS TO %I',
|
|
1163
|
-
owner_role,
|
|
1164
|
-
${literal(role)}
|
|
1165
|
-
);
|
|
1166
|
-
END IF;
|
|
1167
|
-
END $$;
|
|
1168
|
-
`);
|
|
1169
|
-
}
|
|
1170
|
-
function commaSeparated(value) {
|
|
1171
|
-
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
1172
|
-
}
|
|
1173
|
-
function validateIdentifier(name, value) {
|
|
1174
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
|
|
1175
|
-
throw new Error(`${name} contains an invalid Postgres identifier: ${value}`);
|
|
1176
|
-
}
|
|
1177
|
-
return value;
|
|
1178
|
-
}
|
|
1179
|
-
function identifier(value) {
|
|
1180
|
-
return `"${value.replace(/"/g, '""')}"`;
|
|
1181
|
-
}
|
|
1182
|
-
function literal(value) {
|
|
1183
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
1184
|
-
}
|
|
1185
|
-
function databaseUrlFor(value, database) {
|
|
1186
|
-
const url = new URL(value);
|
|
1187
|
-
url.pathname = `/${database}`;
|
|
1188
|
-
return url.toString();
|
|
1189
|
-
}
|
|
1190
|
-
if (import.meta.main) {
|
|
1191
|
-
const adminUrl = process.env.OPENGENI_MIGRATIONS_DATABASE_URL ?? process.env.OPENGENI_DATABASE_ADMIN_URL ?? process.env.OPENGENI_DATABASE_URL;
|
|
1192
|
-
if (!adminUrl) {
|
|
1193
|
-
throw new Error(
|
|
1194
|
-
"OPENGENI_MIGRATIONS_DATABASE_URL, OPENGENI_DATABASE_ADMIN_URL, or OPENGENI_DATABASE_URL is required"
|
|
1195
|
-
);
|
|
1196
|
-
}
|
|
1197
|
-
const result = await provisionRoles(adminUrl, {
|
|
1198
|
-
...process.env.OPENGENI_DB_SCHEMA?.trim() ? { targetSchema: process.env.OPENGENI_DB_SCHEMA.trim() } : {}
|
|
1199
|
-
});
|
|
1200
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
export {
|
|
1204
|
-
FORCE_RLS_TABLES,
|
|
1205
|
-
NON_RLS_RUNTIME_TABLES,
|
|
1206
|
-
RUNTIME_FULL_DML_TABLES,
|
|
1207
|
-
RUNTIME_READ_ONLY_TABLES,
|
|
1208
|
-
RUNTIME_READ_INSERT_TABLES,
|
|
1209
|
-
PROTECTED_NO_DIRECT_DML_TABLES,
|
|
1210
|
-
RUNTIME_TABLE_PRIVILEGES,
|
|
1211
|
-
RUNTIME_DML_TABLES,
|
|
1212
|
-
RuntimeDatabasePostureError,
|
|
1213
|
-
inspectRuntimeDatabasePosture,
|
|
1214
|
-
evaluateRuntimeDatabasePosture,
|
|
1215
|
-
assertRuntimeDatabasePosture,
|
|
1216
|
-
runtimeDatabaseReadyCheck,
|
|
1217
|
-
provisionRoles
|
|
1218
|
-
};
|
|
1219
|
-
//# sourceMappingURL=chunk-VHBFHMPC.js.map
|