@opengeni/db 1.0.1 → 3.2.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
- package/dist/chunk-2EH4WRHL.js.map +1 -0
- package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
- package/dist/chunk-6HMM2R5T.js +390 -0
- package/dist/chunk-6HMM2R5T.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
- package/dist/chunk-AZHW7R5V.js.map +1 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
- package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
- package/dist/chunk-KRLPEMLH.js.map +1 -0
- package/dist/chunk-NAB6FKFN.js +516 -0
- package/dist/chunk-NAB6FKFN.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-UZNXG44K.js +4508 -0
- package/dist/chunk-UZNXG44K.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +85 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +23 -4
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1606 -81
- package/dist/index.js +21898 -8147
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +29 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +43 -4
- package/dist/session-realtime-ledger.d.ts +2 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +268 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +28 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
- package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
- package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
- package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
- package/drizzle/0346_document_migration_audit_surface.sql +454 -0
- package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +167 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +87 -2
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +11676 -1550
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +995 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +905 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1317 -72
- package/src/schema.ts +3316 -428
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +441 -85
- package/src/session-realtime-context.ts +4 -6
- package/src/session-realtime-ledger.ts +28 -14
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +410 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +867 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +138 -11
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Migration 0298: read-only organization tenancy PARITY seam (phase E).
|
|
3
|
+
--
|
|
4
|
+
-- Phase D shipped `inventory_organization_tenancy` (migration 0285): content-
|
|
5
|
+
-- free counts of every legacy-attribution population. Phase E needs the next
|
|
6
|
+
-- thing an operator can gate a cutover on - a shadow parity CHECK that proves
|
|
7
|
+
-- the structural tenancy invariants actually hold before any authority is
|
|
8
|
+
-- activated:
|
|
9
|
+
--
|
|
10
|
+
-- * organization / membership / workspace consistency;
|
|
11
|
+
-- * one personal workspace per active membership, and that workspace must
|
|
12
|
+
-- stay membership-row-free (a persisted `workspace_memberships` row there
|
|
13
|
+
-- is exactly how the owner-only projection would widen into delegable
|
|
14
|
+
-- access);
|
|
15
|
+
-- * stable authority uniqueness (one owning membership per user resource);
|
|
16
|
+
-- * provider-account collision rules (a disputed login binding must have
|
|
17
|
+
-- disputed its identity; an identity's active binding must be its own);
|
|
18
|
+
-- * session ownership provenance; and
|
|
19
|
+
-- * zero partial delegations (mode/session/epoch fences, live authority,
|
|
20
|
+
-- live owning membership, and a session fence that is never ahead of the
|
|
21
|
+
-- session it fences).
|
|
22
|
+
--
|
|
23
|
+
-- Plus the read-only shadow comparison between the legacy effective scope
|
|
24
|
+
-- (everything workspace-owned) and the proposed effective scope (the row's
|
|
25
|
+
-- declared authority): `user_scoped_resource_live_anchor` reports every row
|
|
26
|
+
-- that CLAIMS user authority without a live anchor. Such a row must resolve to
|
|
27
|
+
-- workspace authority or deny - never to user authority. The seam reports; it
|
|
28
|
+
-- never repairs, never widens, and never resolves an ambiguity.
|
|
29
|
+
--
|
|
30
|
+
-- STRICTLY READ-ONLY. The function's only writes are the claim/release of its
|
|
31
|
+
-- own transaction-local private capability row - never any inspected table.
|
|
32
|
+
-- Output is content-free: bounded row UUIDs as failure evidence, integers
|
|
33
|
+
-- everywhere else. No subjects, names, keys, credentials, or values.
|
|
34
|
+
--
|
|
35
|
+
-- FORCE RLS + SECURITY DEFINER note: identical posture to 0285. Every
|
|
36
|
+
-- inspected table is FORCE ROW LEVEL SECURITY and most of their ordinary
|
|
37
|
+
-- policies gate on an EXACT single workspace_id (or, for the four
|
|
38
|
+
-- organization-tenancy authority tables and the canonical identity tables, on
|
|
39
|
+
-- `USING (false)` / a lifecycle marker), which an organization-wide read can
|
|
40
|
+
-- never satisfy. Rather than widen any of those policies, this migration takes
|
|
41
|
+
-- 0254/0285's established capability-row pattern and gives the parity seam its
|
|
42
|
+
-- OWN transaction-scoped, migration-owner-only capability. It is deliberately
|
|
43
|
+
-- separate from 0285's capability so that `inventory_organization_tenancy`
|
|
44
|
+
-- gains no visibility it was not reviewed for. Account scoping is enforced by
|
|
45
|
+
-- this function's own `account_id = p_organization_id` predicates.
|
|
46
|
+
|
|
47
|
+
SET LOCAL lock_timeout = '5s';
|
|
48
|
+
SET LOCAL statement_timeout = '5min';
|
|
49
|
+
|
|
50
|
+
CREATE TABLE opengeni_private.organization_tenancy_parity_capabilities (
|
|
51
|
+
"backend_pid" integer NOT NULL,
|
|
52
|
+
"transaction_id" xid8 NOT NULL,
|
|
53
|
+
CONSTRAINT "organization_tenancy_parity_capabilities_pk" PRIMARY KEY (
|
|
54
|
+
"backend_pid", "transaction_id"
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
REVOKE ALL ON TABLE opengeni_private.organization_tenancy_parity_capabilities FROM PUBLIC;
|
|
58
|
+
|
|
59
|
+
CREATE OR REPLACE FUNCTION opengeni_private.organization_tenancy_parity_capability_active()
|
|
60
|
+
RETURNS boolean
|
|
61
|
+
LANGUAGE sql
|
|
62
|
+
STABLE
|
|
63
|
+
SECURITY DEFINER
|
|
64
|
+
SET search_path = pg_catalog
|
|
65
|
+
AS $capability_active$
|
|
66
|
+
SELECT EXISTS (
|
|
67
|
+
SELECT 1
|
|
68
|
+
FROM opengeni_private.organization_tenancy_parity_capabilities capability
|
|
69
|
+
WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
|
|
70
|
+
AND capability.transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
71
|
+
)
|
|
72
|
+
$capability_active$;
|
|
73
|
+
REVOKE ALL ON FUNCTION
|
|
74
|
+
opengeni_private.organization_tenancy_parity_capability_active() FROM PUBLIC;
|
|
75
|
+
|
|
76
|
+
DO $organization_tenancy_parity_capability_policies$
|
|
77
|
+
DECLARE
|
|
78
|
+
data_schema text := current_schema();
|
|
79
|
+
migration_owner text := current_user;
|
|
80
|
+
table_name text;
|
|
81
|
+
BEGIN
|
|
82
|
+
FOREACH table_name IN ARRAY ARRAY[
|
|
83
|
+
'sessions',
|
|
84
|
+
'workspace_variable_sets',
|
|
85
|
+
'rigs',
|
|
86
|
+
'enrollments',
|
|
87
|
+
'connections',
|
|
88
|
+
'documents',
|
|
89
|
+
'codex_subscription_credentials',
|
|
90
|
+
'sandbox_workspace_mutation_admissions',
|
|
91
|
+
'sandbox_retained_processes',
|
|
92
|
+
'organization_memberships',
|
|
93
|
+
'organization_user_resource_authorities',
|
|
94
|
+
'organization_user_resource_grants',
|
|
95
|
+
'connection_use_audit_facts',
|
|
96
|
+
'canonical_human_identities',
|
|
97
|
+
'canonical_human_identity_subjects',
|
|
98
|
+
'canonical_human_login_bindings'
|
|
99
|
+
] LOOP
|
|
100
|
+
-- Existence-guarded on purpose. A maintenance migration earlier in the
|
|
101
|
+
-- chain (0264, which introduces connection_use_audit_facts) can legitimately
|
|
102
|
+
-- be skipped or aborted, and the chain still runs forward over that partial
|
|
103
|
+
-- history. Creating a policy on an absent relation would break the whole
|
|
104
|
+
-- chain there; the parity seam simply cannot be executed against such a
|
|
105
|
+
-- database, which is already true of every other reader of that table.
|
|
106
|
+
IF to_regclass(format('%I.%I', data_schema, table_name)) IS NOT NULL THEN
|
|
107
|
+
EXECUTE format(
|
|
108
|
+
'CREATE POLICY organization_tenancy_parity_capability_read ON %I.%I '
|
|
109
|
+
|| 'FOR SELECT USING (current_user = %L AND '
|
|
110
|
+
|| 'opengeni_private.organization_tenancy_parity_capability_active())',
|
|
111
|
+
data_schema,
|
|
112
|
+
table_name,
|
|
113
|
+
migration_owner
|
|
114
|
+
);
|
|
115
|
+
END IF;
|
|
116
|
+
END LOOP;
|
|
117
|
+
END
|
|
118
|
+
$organization_tenancy_parity_capability_policies$;
|
|
119
|
+
|
|
120
|
+
CREATE OR REPLACE FUNCTION check_organization_tenancy_parity(
|
|
121
|
+
p_organization_id uuid,
|
|
122
|
+
p_evidence_limit integer DEFAULT 10,
|
|
123
|
+
p_observation_window_days integer DEFAULT 30
|
|
124
|
+
) RETURNS jsonb
|
|
125
|
+
LANGUAGE plpgsql
|
|
126
|
+
SECURITY DEFINER
|
|
127
|
+
SET search_path FROM CURRENT
|
|
128
|
+
SET statement_timeout = '60s'
|
|
129
|
+
AS $$
|
|
130
|
+
DECLARE
|
|
131
|
+
result jsonb;
|
|
132
|
+
evidence_limit integer := least(greatest(coalesce(p_evidence_limit, 10), 0), 50);
|
|
133
|
+
window_days integer := least(greatest(coalesce(p_observation_window_days, 30), 1), 365);
|
|
134
|
+
BEGIN
|
|
135
|
+
IF p_organization_id IS NULL THEN
|
|
136
|
+
RAISE EXCEPTION 'tenancy parity check requires an organization id'
|
|
137
|
+
USING ERRCODE = '22004';
|
|
138
|
+
END IF;
|
|
139
|
+
IF p_organization_id IS DISTINCT FROM nullif(
|
|
140
|
+
pg_catalog.current_setting('opengeni.account_id', true), ''
|
|
141
|
+
)::uuid
|
|
142
|
+
THEN
|
|
143
|
+
RAISE EXCEPTION 'tenancy parity scope mismatch'
|
|
144
|
+
USING ERRCODE = '42501';
|
|
145
|
+
END IF;
|
|
146
|
+
|
|
147
|
+
INSERT INTO opengeni_private.organization_tenancy_parity_capabilities (
|
|
148
|
+
backend_pid, transaction_id
|
|
149
|
+
) VALUES (pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id())
|
|
150
|
+
ON CONFLICT DO NOTHING;
|
|
151
|
+
|
|
152
|
+
WITH gate_catalog(gate_id) AS (
|
|
153
|
+
VALUES
|
|
154
|
+
('membership_personal_workspace_pointer'::text),
|
|
155
|
+
('membership_personal_workspace_exclusive'),
|
|
156
|
+
('membership_personal_workspace_same_organization'),
|
|
157
|
+
('personal_workspace_has_no_membership_row'),
|
|
158
|
+
('authority_resource_single_owner'),
|
|
159
|
+
('grant_delegation_fence_complete'),
|
|
160
|
+
('grant_owner_membership_active'),
|
|
161
|
+
('grant_authority_live'),
|
|
162
|
+
('grant_session_fence_not_ahead'),
|
|
163
|
+
('session_owner_provenance_paired'),
|
|
164
|
+
('session_owner_subject_matches_membership'),
|
|
165
|
+
('session_owner_membership_same_organization'),
|
|
166
|
+
('login_binding_dispute_propagated'),
|
|
167
|
+
('identity_active_binding_owned'),
|
|
168
|
+
('user_scoped_resource_live_anchor')
|
|
169
|
+
),
|
|
170
|
+
-- Canonical human identities are organization-INDEPENDENT by design, so an
|
|
171
|
+
-- organization-scoped parity report may only inspect the identities that
|
|
172
|
+
-- actually anchor a subject of THIS organization. `user:<auth_user_id>` is
|
|
173
|
+
-- the canonical subject encoding used by organization_memberships.
|
|
174
|
+
scoped_identities AS (
|
|
175
|
+
SELECT DISTINCT identity_subject.identity_id
|
|
176
|
+
FROM canonical_human_identity_subjects identity_subject
|
|
177
|
+
JOIN organization_memberships membership
|
|
178
|
+
ON membership.account_id = p_organization_id
|
|
179
|
+
AND membership.subject_id = 'user:' || identity_subject.auth_user_id
|
|
180
|
+
),
|
|
181
|
+
violations(gate_id, evidence_id) AS (
|
|
182
|
+
-- An active membership must identify a personal workspace.
|
|
183
|
+
SELECT 'membership_personal_workspace_pointer'::text, membership.id
|
|
184
|
+
FROM organization_memberships membership
|
|
185
|
+
WHERE membership.account_id = p_organization_id
|
|
186
|
+
AND membership.status = 'active'
|
|
187
|
+
AND membership.personal_workspace_id IS NULL
|
|
188
|
+
|
|
189
|
+
UNION ALL
|
|
190
|
+
-- A personal workspace may identify at most one organization membership.
|
|
191
|
+
SELECT 'membership_personal_workspace_exclusive', membership.id
|
|
192
|
+
FROM organization_memberships membership
|
|
193
|
+
WHERE membership.account_id = p_organization_id
|
|
194
|
+
AND membership.personal_workspace_id IS NOT NULL
|
|
195
|
+
AND EXISTS (
|
|
196
|
+
SELECT 1 FROM organization_memberships other
|
|
197
|
+
WHERE other.account_id = p_organization_id
|
|
198
|
+
AND other.id <> membership.id
|
|
199
|
+
AND other.personal_workspace_id = membership.personal_workspace_id
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
UNION ALL
|
|
203
|
+
-- The personal workspace must belong to the same organization.
|
|
204
|
+
SELECT 'membership_personal_workspace_same_organization', membership.id
|
|
205
|
+
FROM organization_memberships membership
|
|
206
|
+
WHERE membership.account_id = p_organization_id
|
|
207
|
+
AND membership.personal_workspace_id IS NOT NULL
|
|
208
|
+
AND NOT EXISTS (
|
|
209
|
+
SELECT 1 FROM workspaces workspace
|
|
210
|
+
WHERE workspace.id = membership.personal_workspace_id
|
|
211
|
+
AND workspace.account_id = p_organization_id
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
UNION ALL
|
|
215
|
+
-- A managed personal workspace deliberately has NO workspace_memberships
|
|
216
|
+
-- row: the owner-only access projection is derived, not delegable. A
|
|
217
|
+
-- persisted row there is precisely how membership CRUD and the subject-
|
|
218
|
+
-- membership fallback would widen it. Evidence is the workspace id.
|
|
219
|
+
SELECT DISTINCT 'personal_workspace_has_no_membership_row',
|
|
220
|
+
membership.personal_workspace_id
|
|
221
|
+
FROM organization_memberships membership
|
|
222
|
+
JOIN workspace_memberships workspace_membership
|
|
223
|
+
ON workspace_membership.account_id = p_organization_id
|
|
224
|
+
AND workspace_membership.workspace_id = membership.personal_workspace_id
|
|
225
|
+
WHERE membership.account_id = p_organization_id
|
|
226
|
+
AND membership.personal_workspace_id IS NOT NULL
|
|
227
|
+
|
|
228
|
+
UNION ALL
|
|
229
|
+
-- Stable authority uniqueness. The physical unique index is per
|
|
230
|
+
-- (account, membership, kind, resource), so two DIFFERENT memberships can
|
|
231
|
+
-- still claim the same resource - the ambiguity that must never be
|
|
232
|
+
-- resolved toward either user.
|
|
233
|
+
SELECT 'authority_resource_single_owner', authority.id
|
|
234
|
+
FROM organization_user_resource_authorities authority
|
|
235
|
+
WHERE authority.account_id = p_organization_id
|
|
236
|
+
AND authority.status <> 'revoked'
|
|
237
|
+
AND EXISTS (
|
|
238
|
+
SELECT 1 FROM organization_user_resource_authorities rival
|
|
239
|
+
WHERE rival.account_id = p_organization_id
|
|
240
|
+
AND rival.status <> 'revoked'
|
|
241
|
+
AND rival.resource_kind = authority.resource_kind
|
|
242
|
+
AND rival.resource_id = authority.resource_id
|
|
243
|
+
AND rival.organization_membership_id
|
|
244
|
+
<> authority.organization_membership_id
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
UNION ALL
|
|
248
|
+
-- Zero partial delegations: `always` carries neither fence, once/session
|
|
249
|
+
-- carry both.
|
|
250
|
+
SELECT 'grant_delegation_fence_complete', resource_grant.id
|
|
251
|
+
FROM organization_user_resource_grants resource_grant
|
|
252
|
+
WHERE resource_grant.account_id = p_organization_id
|
|
253
|
+
AND NOT (
|
|
254
|
+
(resource_grant.mode = 'always'
|
|
255
|
+
AND resource_grant.session_id IS NULL
|
|
256
|
+
AND resource_grant.authority_epoch IS NULL)
|
|
257
|
+
OR (resource_grant.mode IN ('once', 'session')
|
|
258
|
+
AND resource_grant.session_id IS NOT NULL
|
|
259
|
+
AND resource_grant.authority_epoch IS NOT NULL
|
|
260
|
+
AND resource_grant.authority_epoch > 0)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
UNION ALL
|
|
264
|
+
-- A live personal delegation owned by a human whose organization
|
|
265
|
+
-- membership is suspended or revoked is a fallback to user authority.
|
|
266
|
+
-- Suspension revokes personal-resource grants and offboarding runs the
|
|
267
|
+
-- same teardown, so the drained state is the normal one.
|
|
268
|
+
SELECT 'grant_owner_membership_active', resource_grant.id
|
|
269
|
+
FROM organization_user_resource_grants resource_grant
|
|
270
|
+
JOIN organization_memberships membership
|
|
271
|
+
ON membership.id = resource_grant.owner_organization_membership_id
|
|
272
|
+
AND membership.account_id = resource_grant.account_id
|
|
273
|
+
WHERE resource_grant.account_id = p_organization_id
|
|
274
|
+
AND resource_grant.status = 'active'
|
|
275
|
+
AND membership.status <> 'active'
|
|
276
|
+
|
|
277
|
+
UNION ALL
|
|
278
|
+
-- An active grant over a revoked authority.
|
|
279
|
+
SELECT 'grant_authority_live', resource_grant.id
|
|
280
|
+
FROM organization_user_resource_grants resource_grant
|
|
281
|
+
JOIN organization_user_resource_authorities authority
|
|
282
|
+
ON authority.id = resource_grant.authority_id
|
|
283
|
+
AND authority.account_id = resource_grant.account_id
|
|
284
|
+
WHERE resource_grant.account_id = p_organization_id
|
|
285
|
+
AND resource_grant.status = 'active'
|
|
286
|
+
AND authority.status = 'revoked'
|
|
287
|
+
|
|
288
|
+
UNION ALL
|
|
289
|
+
-- A session-fenced grant whose epoch is AHEAD of the session it fences
|
|
290
|
+
-- would survive the epoch advance that is supposed to revoke it. Epochs
|
|
291
|
+
-- are monotonic and the fence is copied from the session at issuance, so
|
|
292
|
+
-- `grant.authority_epoch <= session.authority_epoch` always holds.
|
|
293
|
+
SELECT 'grant_session_fence_not_ahead', resource_grant.id
|
|
294
|
+
FROM organization_user_resource_grants resource_grant
|
|
295
|
+
JOIN sessions session_row
|
|
296
|
+
ON session_row.id = resource_grant.session_id
|
|
297
|
+
AND session_row.workspace_id = resource_grant.workspace_id
|
|
298
|
+
WHERE resource_grant.account_id = p_organization_id
|
|
299
|
+
AND resource_grant.session_id IS NOT NULL
|
|
300
|
+
AND resource_grant.authority_epoch > session_row.authority_epoch
|
|
301
|
+
|
|
302
|
+
UNION ALL
|
|
303
|
+
-- Session owner provenance is paired, and a user_private session always
|
|
304
|
+
-- has an owner.
|
|
305
|
+
SELECT 'session_owner_provenance_paired', session_row.id
|
|
306
|
+
FROM sessions session_row
|
|
307
|
+
WHERE session_row.account_id = p_organization_id
|
|
308
|
+
AND (
|
|
309
|
+
(session_row.owner_organization_membership_id IS NULL)
|
|
310
|
+
<> (session_row.owner_subject_id IS NULL)
|
|
311
|
+
OR (session_row.visibility = 'user_private'
|
|
312
|
+
AND session_row.owner_organization_membership_id IS NULL)
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
UNION ALL
|
|
316
|
+
-- The denormalized owner subject must still name the owning membership.
|
|
317
|
+
SELECT 'session_owner_subject_matches_membership', session_row.id
|
|
318
|
+
FROM sessions session_row
|
|
319
|
+
JOIN organization_memberships membership
|
|
320
|
+
ON membership.id = session_row.owner_organization_membership_id
|
|
321
|
+
AND membership.account_id = session_row.account_id
|
|
322
|
+
WHERE session_row.account_id = p_organization_id
|
|
323
|
+
AND session_row.owner_subject_id IS DISTINCT FROM membership.subject_id
|
|
324
|
+
|
|
325
|
+
UNION ALL
|
|
326
|
+
-- The owning membership must exist in this organization.
|
|
327
|
+
SELECT 'session_owner_membership_same_organization', session_row.id
|
|
328
|
+
FROM sessions session_row
|
|
329
|
+
WHERE session_row.account_id = p_organization_id
|
|
330
|
+
AND session_row.owner_organization_membership_id IS NOT NULL
|
|
331
|
+
AND NOT EXISTS (
|
|
332
|
+
SELECT 1 FROM organization_memberships membership
|
|
333
|
+
WHERE membership.id = session_row.owner_organization_membership_id
|
|
334
|
+
AND membership.account_id = p_organization_id
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
UNION ALL
|
|
338
|
+
-- Provider-account collision rule: the physical unique index makes a
|
|
339
|
+
-- second binding for one (provider, provider_account) impossible, so the
|
|
340
|
+
-- collision is recorded by DISPUTING the existing binding and BOTH
|
|
341
|
+
-- identities. A disputed binding whose identity is still usable is a
|
|
342
|
+
-- collision that never fenced its identity.
|
|
343
|
+
SELECT 'login_binding_dispute_propagated', binding.id
|
|
344
|
+
FROM canonical_human_login_bindings binding
|
|
345
|
+
JOIN scoped_identities scoped ON scoped.identity_id = binding.identity_id
|
|
346
|
+
JOIN canonical_human_identities identity ON identity.id = binding.identity_id
|
|
347
|
+
WHERE binding.status = 'disputed'
|
|
348
|
+
AND identity.status <> 'disputed'
|
|
349
|
+
|
|
350
|
+
UNION ALL
|
|
351
|
+
-- The FK on active_login_binding_id proves existence, never ownership.
|
|
352
|
+
SELECT 'identity_active_binding_owned', identity.id
|
|
353
|
+
FROM canonical_human_identities identity
|
|
354
|
+
JOIN scoped_identities scoped ON scoped.identity_id = identity.id
|
|
355
|
+
WHERE identity.active_login_binding_id IS NOT NULL
|
|
356
|
+
AND NOT EXISTS (
|
|
357
|
+
SELECT 1 FROM canonical_human_login_bindings binding
|
|
358
|
+
WHERE binding.id = identity.active_login_binding_id
|
|
359
|
+
AND binding.identity_id = identity.id
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
UNION ALL
|
|
363
|
+
-- Shadow comparison, connections lane: legacy effective scope is
|
|
364
|
+
-- workspace; the proposed effective scope is `user`. A row claiming user
|
|
365
|
+
-- authority without a live authority AND a live owning membership has no
|
|
366
|
+
-- reachable user resolution - it must fall back to workspace or deny.
|
|
367
|
+
SELECT 'user_scoped_resource_live_anchor', connection_row.id
|
|
368
|
+
FROM connections connection_row
|
|
369
|
+
WHERE connection_row.account_id = p_organization_id
|
|
370
|
+
AND connection_row.authority_scope = 'user'
|
|
371
|
+
AND NOT EXISTS (
|
|
372
|
+
SELECT 1
|
|
373
|
+
FROM organization_user_resource_authorities authority
|
|
374
|
+
JOIN organization_memberships membership
|
|
375
|
+
ON membership.id = authority.organization_membership_id
|
|
376
|
+
AND membership.account_id = authority.account_id
|
|
377
|
+
WHERE authority.id = connection_row.authority_id
|
|
378
|
+
AND authority.account_id = p_organization_id
|
|
379
|
+
AND authority.status = 'active'
|
|
380
|
+
AND membership.status = 'active'
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
UNION ALL
|
|
384
|
+
SELECT 'user_scoped_resource_live_anchor', variable_set.id
|
|
385
|
+
FROM workspace_variable_sets variable_set
|
|
386
|
+
WHERE variable_set.account_id = p_organization_id
|
|
387
|
+
AND variable_set.authority_scope = 'user'
|
|
388
|
+
AND NOT EXISTS (
|
|
389
|
+
SELECT 1
|
|
390
|
+
FROM organization_user_resource_authorities authority
|
|
391
|
+
JOIN organization_memberships membership
|
|
392
|
+
ON membership.id = authority.organization_membership_id
|
|
393
|
+
AND membership.account_id = authority.account_id
|
|
394
|
+
WHERE authority.id = variable_set.authority_id
|
|
395
|
+
AND authority.account_id = p_organization_id
|
|
396
|
+
AND authority.status = 'active'
|
|
397
|
+
AND membership.status = 'active'
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
UNION ALL
|
|
401
|
+
SELECT 'user_scoped_resource_live_anchor', rig_row.id
|
|
402
|
+
FROM rigs rig_row
|
|
403
|
+
WHERE rig_row.account_id = p_organization_id
|
|
404
|
+
AND rig_row.authority_scope = 'user'
|
|
405
|
+
AND NOT EXISTS (
|
|
406
|
+
SELECT 1
|
|
407
|
+
FROM organization_user_resource_authorities authority
|
|
408
|
+
JOIN organization_memberships membership
|
|
409
|
+
ON membership.id = authority.organization_membership_id
|
|
410
|
+
AND membership.account_id = authority.account_id
|
|
411
|
+
WHERE authority.id = rig_row.authority_id
|
|
412
|
+
AND authority.account_id = p_organization_id
|
|
413
|
+
AND authority.status = 'active'
|
|
414
|
+
AND membership.status = 'active'
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
UNION ALL
|
|
418
|
+
SELECT 'user_scoped_resource_live_anchor', enrollment.id
|
|
419
|
+
FROM enrollments enrollment
|
|
420
|
+
WHERE enrollment.account_id = p_organization_id
|
|
421
|
+
AND enrollment.authority_scope = 'user'
|
|
422
|
+
AND NOT EXISTS (
|
|
423
|
+
SELECT 1
|
|
424
|
+
FROM organization_user_resource_authorities authority
|
|
425
|
+
JOIN organization_memberships membership
|
|
426
|
+
ON membership.id = authority.organization_membership_id
|
|
427
|
+
AND membership.account_id = authority.account_id
|
|
428
|
+
WHERE authority.id = enrollment.authority_id
|
|
429
|
+
AND authority.account_id = p_organization_id
|
|
430
|
+
AND authority.status = 'active'
|
|
431
|
+
AND membership.status = 'active'
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
UNION ALL
|
|
435
|
+
-- Documents carry the same claim as authority_kind='personal' WITH a
|
|
436
|
+
-- non-null authority_id (a null authority_id there is the separate,
|
|
437
|
+
-- deliberately bounded legacy lane reported under `lanes`).
|
|
438
|
+
SELECT 'user_scoped_resource_live_anchor', document.id
|
|
439
|
+
FROM documents document
|
|
440
|
+
WHERE document.account_id = p_organization_id
|
|
441
|
+
AND document.authority_kind = 'personal'
|
|
442
|
+
AND document.authority_id IS NOT NULL
|
|
443
|
+
AND NOT EXISTS (
|
|
444
|
+
SELECT 1
|
|
445
|
+
FROM organization_user_resource_authorities authority
|
|
446
|
+
JOIN organization_memberships membership
|
|
447
|
+
ON membership.id = authority.organization_membership_id
|
|
448
|
+
AND membership.account_id = authority.account_id
|
|
449
|
+
WHERE authority.id = document.authority_id
|
|
450
|
+
AND authority.account_id = p_organization_id
|
|
451
|
+
AND authority.status = 'active'
|
|
452
|
+
AND membership.status = 'active'
|
|
453
|
+
)
|
|
454
|
+
),
|
|
455
|
+
aggregated AS (
|
|
456
|
+
SELECT
|
|
457
|
+
violation.gate_id,
|
|
458
|
+
count(*)::int AS violation_count,
|
|
459
|
+
(pg_catalog.array_agg(
|
|
460
|
+
violation.evidence_id ORDER BY violation.evidence_id
|
|
461
|
+
))[1:evidence_limit] AS evidence
|
|
462
|
+
FROM violations violation
|
|
463
|
+
GROUP BY violation.gate_id
|
|
464
|
+
)
|
|
465
|
+
SELECT pg_catalog.jsonb_build_object(
|
|
466
|
+
'schemaVersion', 1,
|
|
467
|
+
'organizationId', p_organization_id,
|
|
468
|
+
'evidenceLimit', evidence_limit,
|
|
469
|
+
'observationWindowDays', window_days,
|
|
470
|
+
'gates', (
|
|
471
|
+
SELECT pg_catalog.jsonb_object_agg(
|
|
472
|
+
catalog.gate_id,
|
|
473
|
+
pg_catalog.jsonb_build_object(
|
|
474
|
+
'violations', coalesce(aggregated.violation_count, 0),
|
|
475
|
+
'evidence', pg_catalog.to_jsonb(
|
|
476
|
+
coalesce(aggregated.evidence, ARRAY[]::uuid[])
|
|
477
|
+
),
|
|
478
|
+
'truncated', coalesce(aggregated.violation_count, 0)
|
|
479
|
+
> coalesce(pg_catalog.array_length(aggregated.evidence, 1), 0)
|
|
480
|
+
)
|
|
481
|
+
)
|
|
482
|
+
FROM gate_catalog catalog
|
|
483
|
+
LEFT JOIN aggregated ON aggregated.gate_id = catalog.gate_id
|
|
484
|
+
),
|
|
485
|
+
-- Compatibility lanes. These are populations the phase-D backfill drains;
|
|
486
|
+
-- they are NOT invariant violations and must never be read as such.
|
|
487
|
+
'lanes', pg_catalog.jsonb_build_object(
|
|
488
|
+
'connectionsLegacyUser', (
|
|
489
|
+
SELECT count(*)::int FROM connections connection_row
|
|
490
|
+
WHERE connection_row.account_id = p_organization_id
|
|
491
|
+
AND connection_row.authority_scope = 'legacy_user'
|
|
492
|
+
),
|
|
493
|
+
-- Workspace-writer admissions and retained processes are immutable
|
|
494
|
+
-- history: they are settled by UPDATE, nothing in the tree DELETEs them,
|
|
495
|
+
-- and 0277's one-shot backfill only reached rows whose actor was a turn
|
|
496
|
+
-- (`actor_kind`/`owner_actor_kind` = 'turn'), so every pre-0277 `direct:`
|
|
497
|
+
-- or `process:` admission keeps the legacy_unattributed sentinel
|
|
498
|
+
-- permanently. An all-time count of them therefore can never drain, and
|
|
499
|
+
-- marking it drainable would make cutoverReady structurally unreachable
|
|
500
|
+
-- for any deployment carrying one. Same treatment as the connection-use
|
|
501
|
+
-- lane below: report the bounded recent window, where zero IS reachable
|
|
502
|
+
-- and the honest signal is "the lane stopped being exercised".
|
|
503
|
+
'workspaceWriterAdmissionsLegacyUnattributedInWindow', (
|
|
504
|
+
SELECT count(*)::int FROM sandbox_workspace_mutation_admissions admission
|
|
505
|
+
WHERE admission.account_id = p_organization_id
|
|
506
|
+
AND admission.initiator_kind = 'legacy_unattributed'
|
|
507
|
+
AND admission.admitted_at
|
|
508
|
+
>= pg_catalog.now() - pg_catalog.make_interval(days => window_days)
|
|
509
|
+
),
|
|
510
|
+
'workspaceWriterProcessesLegacyUnattributedInWindow', (
|
|
511
|
+
SELECT count(*)::int FROM sandbox_retained_processes retained_process
|
|
512
|
+
WHERE retained_process.account_id = p_organization_id
|
|
513
|
+
AND retained_process.initiator_kind = 'legacy_unattributed'
|
|
514
|
+
AND retained_process.started_at
|
|
515
|
+
>= pg_catalog.now() - pg_catalog.make_interval(days => window_days)
|
|
516
|
+
),
|
|
517
|
+
'documentsLegacyPersonalNullAuthority', (
|
|
518
|
+
SELECT count(*)::int FROM documents document
|
|
519
|
+
WHERE document.account_id = p_organization_id
|
|
520
|
+
AND document.authority_kind = 'personal'
|
|
521
|
+
AND document.authority_id IS NULL
|
|
522
|
+
),
|
|
523
|
+
'codexCredentialsUnattributedConnector', (
|
|
524
|
+
SELECT count(*)::int FROM codex_subscription_credentials credential
|
|
525
|
+
WHERE credential.account_id = p_organization_id
|
|
526
|
+
AND credential.connected_by_subject_id IS NULL
|
|
527
|
+
),
|
|
528
|
+
'workspaceMemberSubjectsWithoutMembershipAnchor', (
|
|
529
|
+
SELECT count(DISTINCT workspace_membership.subject_id)::int
|
|
530
|
+
FROM workspace_memberships workspace_membership
|
|
531
|
+
JOIN workspaces workspace ON workspace.id = workspace_membership.workspace_id
|
|
532
|
+
WHERE workspace.account_id = p_organization_id
|
|
533
|
+
AND workspace_membership.subject_id LIKE 'user:%'
|
|
534
|
+
AND NOT EXISTS (
|
|
535
|
+
SELECT 1 FROM organization_memberships membership
|
|
536
|
+
WHERE membership.account_id = p_organization_id
|
|
537
|
+
AND membership.subject_id = workspace_membership.subject_id
|
|
538
|
+
)
|
|
539
|
+
),
|
|
540
|
+
-- The DRAINABLE refinement of "ownerless sessions": a session whose
|
|
541
|
+
-- creating subject would be attributed by today's write fence but whose
|
|
542
|
+
-- owner is still null. Total ownerlessness is deliberately NOT reported
|
|
543
|
+
-- as drainable - a service/API-key session is permanently ownerless.
|
|
544
|
+
'sessionsAttributableButUnattributed', (
|
|
545
|
+
SELECT count(*)::int FROM sessions session_row
|
|
546
|
+
WHERE session_row.account_id = p_organization_id
|
|
547
|
+
AND session_row.owner_organization_membership_id IS NULL
|
|
548
|
+
AND session_row.created_by_kind = 'subject'
|
|
549
|
+
AND session_row.created_by_subject_id IS NOT NULL
|
|
550
|
+
AND EXISTS (
|
|
551
|
+
SELECT 1 FROM organization_memberships membership
|
|
552
|
+
WHERE membership.account_id = p_organization_id
|
|
553
|
+
AND membership.subject_id = session_row.created_by_subject_id
|
|
554
|
+
AND membership.status = 'active'
|
|
555
|
+
)
|
|
556
|
+
AND EXISTS (
|
|
557
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
558
|
+
WHERE workspace_membership.account_id = p_organization_id
|
|
559
|
+
AND workspace_membership.workspace_id = session_row.workspace_id
|
|
560
|
+
AND workspace_membership.subject_id = session_row.created_by_subject_id
|
|
561
|
+
)
|
|
562
|
+
),
|
|
563
|
+
-- Connection-use audit facts are immutable history, so the all-time
|
|
564
|
+
-- count can never drain. The reachable-zero signal is "was the bounded
|
|
565
|
+
-- pre-snapshot/legacy resolution lane exercised inside the observation
|
|
566
|
+
-- window".
|
|
567
|
+
'connectionUseLegacyResolutionsInWindow', (
|
|
568
|
+
SELECT count(*)::int FROM connection_use_audit_facts audit_fact
|
|
569
|
+
WHERE audit_fact.account_id = p_organization_id
|
|
570
|
+
AND audit_fact.authority_scope = 'legacy_user'
|
|
571
|
+
AND audit_fact.occurred_at
|
|
572
|
+
>= pg_catalog.now() - pg_catalog.make_interval(days => window_days)
|
|
573
|
+
)
|
|
574
|
+
)
|
|
575
|
+
) INTO result;
|
|
576
|
+
|
|
577
|
+
DELETE FROM opengeni_private.organization_tenancy_parity_capabilities
|
|
578
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
579
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
|
|
580
|
+
RETURN result;
|
|
581
|
+
EXCEPTION WHEN OTHERS THEN
|
|
582
|
+
DELETE FROM opengeni_private.organization_tenancy_parity_capabilities
|
|
583
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
584
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned();
|
|
585
|
+
RAISE;
|
|
586
|
+
END
|
|
587
|
+
$$;
|
|
588
|
+
|
|
589
|
+
REVOKE ALL ON FUNCTION
|
|
590
|
+
check_organization_tenancy_parity(uuid, integer, integer) FROM PUBLIC;
|
|
591
|
+
DO $tenancy_parity_grant$
|
|
592
|
+
BEGIN
|
|
593
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
594
|
+
GRANT EXECUTE ON FUNCTION
|
|
595
|
+
check_organization_tenancy_parity(uuid, integer, integer) TO opengeni_app;
|
|
596
|
+
-- The predicate is a read-only boolean over a transaction-local
|
|
597
|
+
-- bookkeeping row and MUST be independently grantable: an ordinary
|
|
598
|
+
-- opengeni_app-effective role touching ANY table this capability protects
|
|
599
|
+
-- needs EXECUTE on every predicate function those tables' policies
|
|
600
|
+
-- reference, or the unrelated read fails closed with a permission error
|
|
601
|
+
-- instead of the policy simply evaluating false. Same explicit grant as
|
|
602
|
+
-- 0254 and 0285 - provisionRoles' blanket schema-wide sweep is not
|
|
603
|
+
-- sufficient for a database whose test/deploy path never runs that step.
|
|
604
|
+
GRANT EXECUTE ON FUNCTION
|
|
605
|
+
opengeni_private.organization_tenancy_parity_capability_active()
|
|
606
|
+
TO opengeni_app;
|
|
607
|
+
END IF;
|
|
608
|
+
END
|
|
609
|
+
$tenancy_parity_grant$;
|