@opengeni/db 5.0.1 → 6.0.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/automations.d.ts +0 -2
- package/dist/canonical-human-identities.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-components.d.ts +5 -13
- package/dist/capability-integrations.d.ts +23 -8
- package/dist/capability-reconciliation.d.ts +5 -0
- package/dist/capability-reconciliation.js +21 -0
- package/dist/capability-reconciliation.js.map +1 -0
- package/dist/{chunk-6T2GGYWE.js → chunk-4UJCXBR2.js} +4 -5800
- package/dist/chunk-4UJCXBR2.js.map +1 -0
- package/dist/{chunk-MWMXWHJD.js → chunk-7JCDSMX2.js} +2 -2
- package/dist/chunk-AJA2ZIUW.js +219 -0
- package/dist/chunk-AJA2ZIUW.js.map +1 -0
- package/dist/chunk-CDAT6EKW.js +247 -0
- package/dist/chunk-CDAT6EKW.js.map +1 -0
- package/dist/{chunk-DPONPYJD.js → chunk-CJI6G254.js} +70 -18
- package/dist/chunk-CJI6G254.js.map +1 -0
- package/dist/chunk-G32NVMRJ.js +276 -0
- package/dist/chunk-G32NVMRJ.js.map +1 -0
- package/dist/chunk-GABXCG3N.js +5811 -0
- package/dist/chunk-GABXCG3N.js.map +1 -0
- package/dist/{chunk-ERKVSPDX.js → chunk-LVY33EWL.js} +3 -3
- package/dist/{chunk-45CP2YKY.js → chunk-MAMBI2HR.js} +9 -3
- package/dist/{chunk-45CP2YKY.js.map → chunk-MAMBI2HR.js.map} +1 -1
- package/dist/{chunk-F73UKJY6.js → chunk-MN5PZMEK.js} +91 -108
- package/dist/chunk-MN5PZMEK.js.map +1 -0
- package/dist/chunk-PJI4MUZ7.js +131 -0
- package/dist/chunk-PJI4MUZ7.js.map +1 -0
- package/dist/{chunk-T6BWIGWJ.js → chunk-RIYHP2WU.js} +2 -2
- package/dist/{chunk-7HMK3B45.js → chunk-RL2MUXM3.js} +16 -7
- package/dist/chunk-RL2MUXM3.js.map +1 -0
- package/dist/{chunk-HQDVI2DB.js → chunk-SYKHLPTK.js} +4 -3
- package/dist/{chunk-HQDVI2DB.js.map → chunk-SYKHLPTK.js.map} +1 -1
- package/dist/{chunk-T6YLHJLS.js → chunk-XIXVVBW2.js} +198 -51
- package/dist/chunk-XIXVVBW2.js.map +1 -0
- package/dist/chunk-ZPJ6V2VG.js +156 -0
- package/dist/chunk-ZPJ6V2VG.js.map +1 -0
- package/dist/codex-capacity-recovery.d.ts +12 -0
- package/dist/codex-failure-eligibility.d.ts +9 -0
- package/dist/company-profile-schema.d.ts +4 -2
- package/dist/connect-attempts.d.ts +15 -0
- package/dist/connect-attempts.js +25 -0
- package/dist/connect-attempts.js.map +1 -0
- package/dist/connection-authority.d.ts +7 -63
- package/dist/connection-refresh-lock.d.ts +4 -0
- package/dist/connection-token-resolver.d.ts +8 -43
- package/dist/database-timing.d.ts +10 -0
- package/dist/database.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +9 -0
- package/dist/editable-artifacts.js +7 -5
- package/dist/index.d.ts +447 -78
- package/dist/index.js +12705 -12872
- package/dist/index.js.map +1 -1
- package/dist/insights-usage-bundle.d.ts +3 -0
- package/dist/integration-bindings.d.ts +1 -1
- package/dist/integration-facets.d.ts +18 -1
- package/dist/knowledge-entries-schema.d.ts +2 -0
- package/dist/knowledge-entries.d.ts +1 -0
- package/dist/knowledge-indexing.d.ts +1 -0
- package/dist/live-session-attempt.d.ts +35 -137
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-account-bindings.d.ts +4 -0
- package/dist/mcp-oauth.d.ts +11 -0
- package/dist/mcp-operations.js +2 -2
- package/dist/migrate.js +1 -1
- package/dist/organization-integration-policy.d.ts +51 -0
- package/dist/organization-integration-policy.js +18 -0
- package/dist/organization-integration-policy.js.map +1 -0
- package/dist/organization-usage.d.ts +20 -0
- package/dist/plugin-packages.d.ts +9 -12
- package/dist/pr-review.d.ts +0 -11
- package/dist/preference-registry-schema.d.ts +17 -0
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +124 -6
- package/dist/retained-provider-commands.js +29 -97
- package/dist/retained-provider-commands.js.map +1 -1
- package/dist/retired-session-create-metadata.d.ts +4 -0
- package/dist/runtime-posture.d.ts +7 -5
- package/dist/schema.d.ts +876 -656
- package/dist/schema.js +5 -7
- package/dist/session-admission-block.d.ts +32 -0
- package/dist/session-attempt-writers.d.ts +4 -0
- package/dist/session-background-commands.js +5 -4
- package/dist/session-command-output.js +8 -126
- package/dist/session-command-output.js.map +1 -1
- package/dist/session-control.d.ts +5 -1
- package/dist/session-event-slices.d.ts +10 -0
- package/dist/session-event-slices.js +8 -222
- package/dist/session-event-slices.js.map +1 -1
- package/dist/session-file-attachments.d.ts +28 -0
- package/dist/session-goal-reports.d.ts +43 -0
- package/dist/session-mcp-approval.d.ts +4 -0
- package/dist/session-mcp-credential-rotation.js +5 -4
- package/dist/session-mcp-credential-rotation.js.map +1 -1
- package/dist/session-message-search.d.ts +22 -0
- package/dist/session-queue-commands.d.ts +2 -6
- package/dist/session-retry.d.ts +36 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/skill-source-release-impact.d.ts +18 -0
- package/dist/skill-source-release.d.ts +9 -0
- package/dist/skills.d.ts +7 -1
- package/dist/user-resource-authority.d.ts +0 -8
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +5 -4
- package/dist/workspace-tool-defaults.js.map +1 -1
- package/drizzle/0468_knowledge_relationship_projection.sql +30 -0
- package/drizzle/0469_knowledge_source_discovery.sql +85 -0
- package/drizzle/0470_organization_integration_policy.sql +151 -0
- package/drizzle/0471_usage_events_account_recent_index.sql +4 -0
- package/drizzle/0472_usage_events_workspace_recent_index.sql +4 -0
- package/drizzle/0473_organization_usage_analytical_capability.sql +237 -0
- package/drizzle/0474_goal_report_requirements.sql +156 -0
- package/drizzle/0475_session_event_history_statistics.sql +15 -0
- package/drizzle/0476_session_event_history_policy_planning.sql +29 -0
- package/drizzle/0477_managed_sign_in_methods.sql +303 -0
- package/drizzle/0478_sender_owned_connections.sql +2772 -0
- package/drizzle/0479_connection_create_idempotency.sql +16 -0
- package/drizzle/0480_private_session_organization_key_administration.sql +154 -0
- package/drizzle/0481_personal_connection_visibility_cleanup.sql +45 -0
- package/drizzle/0482_remove_packs.sql +417 -0
- package/drizzle/0483_preclaim_admission_block.sql +85 -0
- package/drizzle/0484_insights_usage_projection.sql +211 -0
- package/drizzle/0485_inherited_mcp_approval_policies.sql +6 -0
- package/drizzle/0486_reasoning_configuration_message_forks.sql +35 -0
- package/drizzle/0487_skill_catalog_message_forks.sql +26 -0
- package/drizzle/0488_permanent_skill_removal.sql +207 -0
- package/drizzle/0489_modal_command_byte_offsets.sql +17 -0
- package/drizzle/0490_periodic_capture_attempt_cadence.sql +6 -0
- package/drizzle/0491_warm_capture_holder_reclamation.sql +57 -0
- package/drizzle/0492_codex_accepted_source_authority.sql +249 -0
- package/drizzle/0493_stopping_command_error_containment.sql +58 -0
- package/drizzle/0494_mcp_account_bindings.sql +334 -0
- package/drizzle/0495_consented_sandbox_recovery.sql +220 -0
- package/drizzle/0496_supervised_command_settlement.sql +386 -0
- package/drizzle/0497_modal_inventory_owner_read_capability.sql +112 -0
- package/drizzle/0498_enrollment_membership_fence.sql +87 -0
- package/drizzle/0499_session_attachment_access.sql +224 -0
- package/drizzle/0500_session_execution_epoch.sql +37 -0
- package/drizzle/0501_session_sharing_execution.sql +591 -0
- package/drizzle/0502_active_message_boundary_forks.sql +30 -0
- package/package.json +17 -5
- package/src/automations.ts +17 -73
- package/src/canonical-human-identities.ts +17 -5
- package/src/capability-components.ts +7 -24
- package/src/capability-integrations.ts +301 -21
- package/src/capability-reconciliation.ts +19 -0
- package/src/codex-capacity-recovery.ts +47 -0
- package/src/codex-failure-eligibility.ts +93 -0
- package/src/connect-attempts.ts +49 -18
- package/src/connection-authority.ts +12 -158
- package/src/connection-refresh-lock.ts +22 -0
- package/src/connection-token-resolver.ts +58 -530
- package/src/database-timing.ts +43 -0
- package/src/database.ts +61 -26
- package/src/editable-artifacts.ts +1 -1
- package/src/index.ts +3578 -1273
- package/src/insights-model-bundle.ts +19 -7
- package/src/insights-usage-bundle.ts +16 -19
- package/src/integration-bindings.ts +2 -7
- package/src/integration-facets.ts +73 -6
- package/src/knowledge-entries.ts +8 -1
- package/src/live-session-attempt.ts +0 -22
- package/src/mcp-account-bindings.ts +12 -0
- package/src/mcp-oauth.ts +26 -0
- package/src/organization-integration-policy.ts +235 -0
- package/src/organization-usage.ts +83 -0
- package/src/plugin-packages.ts +226 -42
- package/src/pr-review.ts +7 -69
- package/src/preference-registry-schema.ts +1 -0
- package/src/provision-roles.ts +50 -37
- package/src/retained-provider-commands.ts +286 -9
- package/src/retired-session-create-metadata.ts +16 -0
- package/src/runtime-posture.ts +217 -13
- package/src/schema.ts +104 -120
- package/src/session-admission-block.ts +29 -0
- package/src/session-attempt-writers.ts +48 -0
- package/src/session-control.ts +21 -5
- package/src/session-event-slices.ts +76 -26
- package/src/session-file-attachments.ts +66 -0
- package/src/session-goal-reports.ts +255 -0
- package/src/session-mcp-approval.ts +30 -0
- package/src/session-message-search.ts +348 -0
- package/src/session-queue-commands.ts +60 -36
- package/src/session-retry.ts +319 -0
- package/src/skill-config-migration.ts +11 -2
- package/src/skill-source-release-impact.ts +33 -0
- package/src/skill-source-release.ts +67 -26
- package/src/skills.ts +44 -6
- package/src/user-resource-authority.ts +0 -56
- package/dist/chunk-6T2GGYWE.js.map +0 -1
- package/dist/chunk-7HMK3B45.js.map +0 -1
- package/dist/chunk-DPONPYJD.js.map +0 -1
- package/dist/chunk-F73UKJY6.js.map +0 -1
- package/dist/chunk-T6YLHJLS.js.map +0 -1
- package/dist/host-mcp-binding-match.d.ts +0 -5
- package/dist/host-mcp-bindings.d.ts +0 -79
- package/dist/host-mcp-resolvers.d.ts +0 -47
- package/dist/host-mcp-task-authority.d.ts +0 -43
- package/dist/host-selection-identity.d.ts +0 -6
- package/dist/pack-components.d.ts +0 -83
- package/dist/pack-installations.d.ts +0 -86
- package/src/host-mcp-binding-match.ts +0 -34
- package/src/host-mcp-bindings.ts +0 -777
- package/src/host-mcp-resolvers.ts +0 -266
- package/src/host-mcp-task-authority.ts +0 -384
- package/src/host-selection-identity.ts +0 -29
- package/src/pack-components.ts +0 -980
- package/src/pack-installations.ts +0 -950
- /package/dist/{chunk-MWMXWHJD.js.map → chunk-7JCDSMX2.js.map} +0 -0
- /package/dist/{chunk-ERKVSPDX.js.map → chunk-LVY33EWL.js.map} +0 -0
- /package/dist/{chunk-T6BWIGWJ.js.map → chunk-RIYHP2WU.js.map} +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- New auth-account insertion guards and runtime routine contract. Drain old APIs
|
|
3
|
+
-- before applying; no provider wildcard writer may bypass the new boundary.
|
|
4
|
+
SET LOCAL lock_timeout = '5s';
|
|
5
|
+
SET LOCAL statement_timeout = '5min';
|
|
6
|
+
DO $drain$
|
|
7
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
8
|
+
BEGIN
|
|
9
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' OR jsonb_array_length(roles) NOT BETWEEN 1 AND 16
|
|
10
|
+
OR EXISTS (SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
|
|
11
|
+
OR btrim(item #>> '{}') = '' OR item #>> '{}' <> btrim(item #>> '{}') OR octet_length(item #>> '{}') > 63)
|
|
12
|
+
OR EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
|
|
13
|
+
WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
|
|
14
|
+
RAISE EXCEPTION 'sign-in method migration requires stopped application roles' USING ERRCODE = '55000';
|
|
15
|
+
END IF;
|
|
16
|
+
END $drain$;
|
|
17
|
+
|
|
18
|
+
ALTER TABLE auth_identities ADD COLUMN managed_link_intent_id text;
|
|
19
|
+
|
|
20
|
+
CREATE TABLE managed_sign_in_method_operations (
|
|
21
|
+
operation_id uuid PRIMARY KEY,
|
|
22
|
+
auth_user_id text NOT NULL REFERENCES auth_users(id) ON DELETE CASCADE,
|
|
23
|
+
request_digest text NOT NULL CHECK (request_digest ~ '^[0-9a-f]{64}$'),
|
|
24
|
+
kind text NOT NULL CHECK (kind IN ('password','disconnect','connect','automatic_link','recovery')),
|
|
25
|
+
provider text NOT NULL CHECK (provider IN ('password','google','github')),
|
|
26
|
+
notification_email text NOT NULL,
|
|
27
|
+
notification_status text NOT NULL DEFAULT 'pending' CHECK (notification_status IN ('pending','sent','failed','outcome_unknown')),
|
|
28
|
+
available_at timestamptz NOT NULL DEFAULT now(),
|
|
29
|
+
lease_id uuid,
|
|
30
|
+
lease_until timestamptz,
|
|
31
|
+
first_attempt_at timestamptz,
|
|
32
|
+
retry_until timestamptz,
|
|
33
|
+
sender text,
|
|
34
|
+
provider_scope text,
|
|
35
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
36
|
+
);
|
|
37
|
+
ALTER TABLE managed_sign_in_method_operations ENABLE ROW LEVEL SECURITY;
|
|
38
|
+
ALTER TABLE managed_sign_in_method_operations FORCE ROW LEVEL SECURITY;
|
|
39
|
+
CREATE POLICY managed_sign_in_method_lifecycle ON managed_sign_in_method_operations
|
|
40
|
+
USING (current_setting('opengeni.canonical_human_identity_lifecycle',true)='active')
|
|
41
|
+
WITH CHECK (current_setting('opengeni.canonical_human_identity_lifecycle',true)='active');
|
|
42
|
+
REVOKE ALL ON managed_sign_in_method_operations FROM PUBLIC;
|
|
43
|
+
|
|
44
|
+
CREATE FUNCTION replay_managed_sign_in_method(p_user text,p_session text,p_request jsonb)
|
|
45
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
46
|
+
DECLARE prior managed_sign_in_method_operations%ROWTYPE; revision_value bigint;
|
|
47
|
+
previous_marker text := current_setting('opengeni.canonical_human_identity_lifecycle',true);
|
|
48
|
+
BEGIN
|
|
49
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle','active',true);
|
|
50
|
+
SELECT * INTO prior FROM managed_sign_in_method_operations WHERE operation_id=(p_request->>'operationId')::uuid;
|
|
51
|
+
IF NOT FOUND THEN
|
|
52
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
53
|
+
RETURN NULL;
|
|
54
|
+
END IF;
|
|
55
|
+
IF prior.auth_user_id IS DISTINCT FROM p_user OR prior.request_digest IS DISTINCT FROM p_request->>'requestDigest' THEN
|
|
56
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_OPERATION_REUSED' USING ERRCODE='23505';
|
|
57
|
+
END IF;
|
|
58
|
+
SELECT i.identity_revision INTO revision_value FROM canonical_human_identities i
|
|
59
|
+
JOIN canonical_human_identity_subjects s ON s.identity_id=i.id WHERE s.auth_user_id=p_user AND s.status='active';
|
|
60
|
+
PERFORM managed_sign_in_method_authority(p_user,p_session,p_request || jsonb_build_object('expectedIdentityRevision',revision_value));
|
|
61
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
62
|
+
RETURN jsonb_build_object('reauthenticationRequired',true,'notification',CASE WHEN prior.notification_status='pending' THEN 'outcome_unknown' ELSE prior.notification_status END);
|
|
63
|
+
END $body$;
|
|
64
|
+
REVOKE ALL ON FUNCTION replay_managed_sign_in_method(text,text,jsonb) FROM PUBLIC;
|
|
65
|
+
|
|
66
|
+
-- These routines are not authentication. Only the managed-cookie API may supply
|
|
67
|
+
-- the canonical subject/session and actor stamp; provider callbacks additionally
|
|
68
|
+
-- require Better Auth's consumed, database-backed OAuth state and real proof.
|
|
69
|
+
CREATE FUNCTION managed_sign_in_method_authority(p_user text, p_session text, p_request jsonb)
|
|
70
|
+
RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
71
|
+
DECLARE identity_row canonical_human_identities%ROWTYPE; stamp jsonb := p_request->'actorFence';
|
|
72
|
+
BEGIN
|
|
73
|
+
IF stamp IS NOT NULL AND stamp <> 'null'::jsonb THEN
|
|
74
|
+
IF coalesce((p_request->>'callback')::boolean, false) THEN
|
|
75
|
+
PERFORM managed_auth_actor_mutation_lease_acquire(stamp->>'authorityHash', (stamp->>'actorEpoch')::bigint, (stamp->>'requestId')::uuid, 30);
|
|
76
|
+
END IF;
|
|
77
|
+
PERFORM managed_auth_actor_mutation_fence(stamp->>'authorityHash', (stamp->>'actorEpoch')::bigint, (stamp->>'requestId')::uuid);
|
|
78
|
+
END IF;
|
|
79
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle', 'active', true);
|
|
80
|
+
SELECT i.* INTO identity_row FROM canonical_human_identities i
|
|
81
|
+
JOIN canonical_human_identity_subjects s ON s.identity_id = i.id
|
|
82
|
+
WHERE s.auth_user_id = p_user AND s.status = 'active' FOR UPDATE OF i;
|
|
83
|
+
IF NOT FOUND OR identity_row.status <> 'active'
|
|
84
|
+
OR NOT validate_canonical_human_session(p_session, p_user, false) THEN
|
|
85
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_AUTHENTICATION_REQUIRED' USING ERRCODE = '42501';
|
|
86
|
+
END IF;
|
|
87
|
+
IF identity_row.id IS DISTINCT FROM (p_request->>'expectedIdentityId')::uuid THEN
|
|
88
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_IDENTITY_CHANGED' USING ERRCODE='40001';
|
|
89
|
+
END IF;
|
|
90
|
+
IF NOT EXISTS (SELECT 1 FROM auth_users WHERE id = p_user AND email_verified) THEN
|
|
91
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_EMAIL_NOT_VERIFIED' USING ERRCODE = '42501';
|
|
92
|
+
END IF;
|
|
93
|
+
IF NOT EXISTS (SELECT 1 FROM auth_sessions WHERE id = p_session AND user_id = p_user
|
|
94
|
+
AND created_at > clock_timestamp() - interval '5 minutes' AND created_at <= clock_timestamp()) THEN
|
|
95
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_REAUTHENTICATION_REQUIRED' USING ERRCODE = '42501';
|
|
96
|
+
END IF;
|
|
97
|
+
IF identity_row.identity_revision IS DISTINCT FROM (p_request->>'expectedIdentityRevision')::bigint THEN
|
|
98
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_REVISION_CONFLICT' USING ERRCODE = '40001';
|
|
99
|
+
END IF;
|
|
100
|
+
RETURN identity_row.identity_revision;
|
|
101
|
+
END $body$;
|
|
102
|
+
REVOKE ALL ON FUNCTION managed_sign_in_method_authority(text,text,jsonb) FROM PUBLIC;
|
|
103
|
+
|
|
104
|
+
CREATE FUNCTION mutate_managed_sign_in_method(p_user text, p_session text, p_request jsonb)
|
|
105
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
106
|
+
DECLARE revision_value bigint; identity_value uuid; binding_value uuid; provider_value text := p_request->>'provider';
|
|
107
|
+
operation_value text := p_request->>'kind'; account_value text; stamp jsonb := p_request->'actorFence';
|
|
108
|
+
previous_marker text := current_setting('opengeni.canonical_human_identity_lifecycle',true);
|
|
109
|
+
replay_value jsonb; existing_intent jsonb; intent_expiry timestamptz;
|
|
110
|
+
BEGIN
|
|
111
|
+
replay_value := replay_managed_sign_in_method(p_user,p_session,p_request);
|
|
112
|
+
IF replay_value IS NOT NULL THEN RETURN replay_value; END IF;
|
|
113
|
+
revision_value := managed_sign_in_method_authority(p_user, p_session, p_request);
|
|
114
|
+
SELECT identity_id INTO identity_value FROM canonical_human_identity_subjects WHERE auth_user_id = p_user AND status = 'active';
|
|
115
|
+
IF operation_value = 'connect' AND provider_value IN ('google','github') THEN
|
|
116
|
+
IF EXISTS (SELECT 1 FROM auth_identities WHERE user_id = p_user AND provider_id = provider_value) THEN
|
|
117
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_ALREADY_CONNECTED' USING ERRCODE = '23505';
|
|
118
|
+
END IF;
|
|
119
|
+
INSERT INTO auth_verifications(id, identifier, value, expires_at, created_at, updated_at)
|
|
120
|
+
VALUES (p_request->>'operationId', 'managed-sign-in:' || (p_request->>'operationId'),
|
|
121
|
+
(p_request || jsonb_build_object('authUserId',p_user,'authSessionId',p_session))::text,
|
|
122
|
+
clock_timestamp() + interval '5 minutes', clock_timestamp(), clock_timestamp()) ON CONFLICT(id) DO NOTHING;
|
|
123
|
+
SELECT value::jsonb,expires_at INTO existing_intent,intent_expiry FROM auth_verifications WHERE id=p_request->>'operationId' FOR UPDATE;
|
|
124
|
+
IF existing_intent->>'authUserId' IS DISTINCT FROM p_user OR existing_intent->>'requestDigest' IS DISTINCT FROM p_request->>'requestDigest' THEN
|
|
125
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_OPERATION_REUSED' USING ERRCODE='23505';
|
|
126
|
+
END IF;
|
|
127
|
+
IF intent_expiry<=clock_timestamp() THEN RAISE EXCEPTION 'SIGN_IN_METHOD_CONNECT_EXPIRED' USING ERRCODE='42501'; END IF;
|
|
128
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
129
|
+
RETURN jsonb_build_object('intentId',p_request->>'operationId');
|
|
130
|
+
ELSIF operation_value = 'disconnect' AND provider_value IN ('google','github') THEN
|
|
131
|
+
SELECT b.id INTO binding_value FROM canonical_human_login_bindings b
|
|
132
|
+
JOIN auth_identities a ON a.provider_id=b.provider_id AND a.account_id=b.provider_account_id AND a.user_id=p_user
|
|
133
|
+
WHERE b.identity_id=identity_value AND b.status='active' AND b.provider_id=provider_value;
|
|
134
|
+
IF binding_value IS NULL THEN RAISE EXCEPTION 'SIGN_IN_METHOD_NOT_CONNECTED' USING ERRCODE='P0002'; END IF;
|
|
135
|
+
IF NOT EXISTS (SELECT 1 FROM canonical_human_login_bindings b JOIN auth_identities a
|
|
136
|
+
ON a.provider_id=b.provider_id AND a.account_id=b.provider_account_id AND a.user_id=p_user
|
|
137
|
+
WHERE b.identity_id=identity_value AND b.status='active' AND b.provider_id<>provider_value
|
|
138
|
+
AND (p_request->'usableProviders') ? a.provider_id
|
|
139
|
+
AND (a.provider_id IN ('google','github') OR (a.provider_id='credential' AND length(a.password)>0))) THEN
|
|
140
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_LAST_USABLE_METHOD' USING ERRCODE='42501';
|
|
141
|
+
END IF;
|
|
142
|
+
PERFORM apply_canonical_human_identity_operation((p_request->>'operationId')::uuid,p_user,revision_value,'unlink',binding_value,NULL,NULL,'Disconnect personal sign-in method');
|
|
143
|
+
ELSIF operation_value = 'password' THEN
|
|
144
|
+
IF coalesce(length(p_request->>'passwordHash'),0) < 32 THEN RAISE EXCEPTION 'SIGN_IN_METHOD_INVALID_PASSWORD' USING ERRCODE='22023'; END IF;
|
|
145
|
+
SELECT id INTO account_value FROM auth_identities WHERE user_id=p_user AND provider_id='credential' FOR UPDATE;
|
|
146
|
+
IF account_value IS NOT NULL THEN
|
|
147
|
+
-- The API proves the current password, and this comparison fences changes
|
|
148
|
+
-- between that expensive proof and the transaction's credential update.
|
|
149
|
+
IF (SELECT password FROM auth_identities WHERE id=account_value) IS DISTINCT FROM p_request->>'expectedPasswordHash' THEN
|
|
150
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_PASSWORD_CHANGED' USING ERRCODE='40001';
|
|
151
|
+
END IF;
|
|
152
|
+
UPDATE auth_identities SET password=p_request->>'passwordHash', updated_at=clock_timestamp() WHERE id=account_value;
|
|
153
|
+
UPDATE canonical_human_identities SET identity_revision=identity_revision+1, auth_revision=auth_revision+1, updated_at=clock_timestamp() WHERE id=identity_value;
|
|
154
|
+
DELETE FROM auth_sessions WHERE identity_id=identity_value;
|
|
155
|
+
ELSE
|
|
156
|
+
IF p_request->>'expectedPasswordHash' IS NOT NULL THEN RAISE EXCEPTION 'SIGN_IN_METHOD_PASSWORD_CHANGED' USING ERRCODE='40001'; END IF;
|
|
157
|
+
INSERT INTO auth_identities(id,user_id,provider_id,account_id,password,created_at,updated_at)
|
|
158
|
+
VALUES (gen_random_uuid()::text,p_user,'credential',p_user,p_request->>'passwordHash',clock_timestamp(),clock_timestamp());
|
|
159
|
+
PERFORM apply_canonical_human_identity_operation((p_request->>'operationId')::uuid,p_user,revision_value,'link',NULL,'credential',p_user,'Set personal sign-in password');
|
|
160
|
+
END IF;
|
|
161
|
+
ELSE RAISE EXCEPTION 'SIGN_IN_METHOD_INVALID_REQUEST' USING ERRCODE='22023';
|
|
162
|
+
END IF;
|
|
163
|
+
INSERT INTO managed_sign_in_method_operations(operation_id,auth_user_id,request_digest,kind,provider,notification_email)
|
|
164
|
+
SELECT (p_request->>'operationId')::uuid,p_user,p_request->>'requestDigest',operation_value,
|
|
165
|
+
CASE WHEN operation_value='password' THEN 'password' ELSE provider_value END,email FROM auth_users WHERE id=p_user AND email_verified;
|
|
166
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
167
|
+
RETURN jsonb_build_object('reauthenticationRequired',true);
|
|
168
|
+
END $body$;
|
|
169
|
+
REVOKE ALL ON FUNCTION mutate_managed_sign_in_method(text,text,jsonb) FROM PUBLIC;
|
|
170
|
+
|
|
171
|
+
CREATE FUNCTION guard_managed_social_account() RETURNS trigger
|
|
172
|
+
LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
173
|
+
DECLARE identity_value uuid; revision_value bigint; intent jsonb; previous_marker text := current_setting('opengeni.canonical_human_identity_lifecycle',true);
|
|
174
|
+
BEGIN
|
|
175
|
+
IF NEW.provider_id NOT IN ('google','github') THEN RETURN NEW; END IF;
|
|
176
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle','active',true);
|
|
177
|
+
IF NEW.managed_link_intent_id IS NOT NULL THEN
|
|
178
|
+
UPDATE auth_verifications SET expires_at=clock_timestamp() WHERE identifier='managed-sign-in:' || NEW.managed_link_intent_id
|
|
179
|
+
AND expires_at>clock_timestamp() RETURNING value::jsonb INTO intent;
|
|
180
|
+
IF intent IS NULL OR intent->>'authUserId' IS DISTINCT FROM NEW.user_id OR intent->>'provider' IS DISTINCT FROM NEW.provider_id THEN
|
|
181
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_CONNECT_PROOF_REQUIRED' USING ERRCODE='42501';
|
|
182
|
+
END IF;
|
|
183
|
+
revision_value := managed_sign_in_method_authority(NEW.user_id,intent->>'authSessionId',intent || '{"callback":true}'::jsonb);
|
|
184
|
+
END IF;
|
|
185
|
+
SELECT i.id,i.identity_revision INTO identity_value,revision_value FROM canonical_human_identities i
|
|
186
|
+
JOIN canonical_human_identity_subjects s ON s.identity_id=i.id WHERE s.auth_user_id=NEW.user_id AND s.status='active' FOR UPDATE OF i;
|
|
187
|
+
PERFORM pg_advisory_xact_lock(hashtextextended('canonical-human-binding:' || NEW.provider_id || ':' || NEW.account_id,0));
|
|
188
|
+
IF EXISTS (SELECT 1 FROM canonical_human_login_bindings WHERE provider_id=NEW.provider_id AND provider_account_id=NEW.account_id AND identity_id IS DISTINCT FROM identity_value) THEN
|
|
189
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_ACCOUNT_COLLISION' USING ERRCODE='23505';
|
|
190
|
+
END IF;
|
|
191
|
+
IF identity_value IS NOT NULL THEN
|
|
192
|
+
IF NOT EXISTS (SELECT 1 FROM auth_users WHERE id=NEW.user_id AND email_verified) THEN
|
|
193
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_EMAIL_NOT_VERIFIED' USING ERRCODE='42501';
|
|
194
|
+
END IF;
|
|
195
|
+
IF EXISTS (SELECT 1 FROM auth_identities WHERE user_id=NEW.user_id AND provider_id=NEW.provider_id) THEN
|
|
196
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_ALREADY_CONNECTED' USING ERRCODE='23505';
|
|
197
|
+
END IF;
|
|
198
|
+
IF intent IS NULL AND EXISTS (SELECT 1 FROM canonical_human_login_bindings WHERE identity_id=identity_value AND provider_id=NEW.provider_id AND status<>'active') THEN
|
|
199
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_EXPLICIT_RECONNECT_REQUIRED' USING ERRCODE='42501';
|
|
200
|
+
END IF;
|
|
201
|
+
END IF;
|
|
202
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
203
|
+
RETURN NEW;
|
|
204
|
+
END $body$;
|
|
205
|
+
REVOKE ALL ON FUNCTION guard_managed_social_account() FROM PUBLIC;
|
|
206
|
+
CREATE TRIGGER managed_social_account_guard BEFORE INSERT ON auth_identities FOR EACH ROW EXECUTE FUNCTION guard_managed_social_account();
|
|
207
|
+
|
|
208
|
+
CREATE FUNCTION bind_managed_social_account() RETURNS trigger
|
|
209
|
+
LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
210
|
+
DECLARE revision_value bigint; intent jsonb; previous_marker text := current_setting('opengeni.canonical_human_identity_lifecycle',true);
|
|
211
|
+
BEGIN
|
|
212
|
+
IF NEW.provider_id NOT IN ('google','github') THEN RETURN NEW; END IF;
|
|
213
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle','active',true);
|
|
214
|
+
SELECT i.identity_revision INTO revision_value FROM canonical_human_identities i JOIN canonical_human_identity_subjects s
|
|
215
|
+
ON s.identity_id=i.id WHERE s.auth_user_id=NEW.user_id AND s.status='active';
|
|
216
|
+
IF revision_value IS NOT NULL THEN
|
|
217
|
+
PERFORM apply_canonical_human_identity_operation(gen_random_uuid(),NEW.user_id,revision_value,'link',NULL,NEW.provider_id,NEW.account_id,'Link verified social sign-in method');
|
|
218
|
+
END IF;
|
|
219
|
+
IF NEW.managed_link_intent_id IS NOT NULL THEN
|
|
220
|
+
DELETE FROM auth_verifications WHERE identifier='managed-sign-in:' || NEW.managed_link_intent_id RETURNING value::jsonb INTO intent;
|
|
221
|
+
END IF;
|
|
222
|
+
IF intent IS NOT NULL OR (SELECT count(*) FROM auth_identities WHERE user_id=NEW.user_id)>1 THEN
|
|
223
|
+
INSERT INTO managed_sign_in_method_operations(operation_id,auth_user_id,request_digest,kind,provider,notification_email)
|
|
224
|
+
SELECT coalesce(NEW.managed_link_intent_id,NEW.id)::uuid,NEW.user_id,
|
|
225
|
+
coalesce(intent->>'requestDigest',encode(digest(NEW.id,'sha256'),'hex')),
|
|
226
|
+
CASE WHEN intent IS NULL THEN 'automatic_link' ELSE 'connect' END,NEW.provider_id,email
|
|
227
|
+
FROM auth_users WHERE id=NEW.user_id AND email_verified;
|
|
228
|
+
END IF;
|
|
229
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
230
|
+
RETURN NEW;
|
|
231
|
+
END $body$;
|
|
232
|
+
REVOKE ALL ON FUNCTION bind_managed_social_account() FROM PUBLIC;
|
|
233
|
+
CREATE TRIGGER managed_social_account_binding AFTER INSERT ON auth_identities FOR EACH ROW EXECUTE FUNCTION bind_managed_social_account();
|
|
234
|
+
|
|
235
|
+
-- The sender/scope and provider idempotency deadline are frozen on first claim.
|
|
236
|
+
-- Unknown deliveries are retried only inside that guarantee; a provider switch
|
|
237
|
+
-- or an expired guarantee is retained for operator reconciliation, never resent.
|
|
238
|
+
CREATE FUNCTION claim_managed_sign_in_notification(p_operation uuid,p_sender text,p_scope text,p_retention integer)
|
|
239
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
240
|
+
DECLARE item managed_sign_in_method_operations%ROWTYPE; previous_marker text := current_setting('opengeni.canonical_human_identity_lifecycle',true);
|
|
241
|
+
BEGIN
|
|
242
|
+
IF p_retention<1 OR p_sender IS NULL OR p_scope IS NULL THEN RETURN NULL; END IF;
|
|
243
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle','active',true);
|
|
244
|
+
SELECT * INTO item FROM managed_sign_in_method_operations WHERE notification_status<>'sent'
|
|
245
|
+
AND (p_operation IS NULL OR operation_id=p_operation) AND available_at<=clock_timestamp()
|
|
246
|
+
AND (lease_until IS NULL OR lease_until<=clock_timestamp())
|
|
247
|
+
AND (first_attempt_at IS NULL OR (retry_until>clock_timestamp() AND sender=p_sender AND provider_scope=p_scope))
|
|
248
|
+
ORDER BY created_at,operation_id FOR UPDATE SKIP LOCKED LIMIT 1;
|
|
249
|
+
IF NOT FOUND THEN
|
|
250
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true); RETURN NULL;
|
|
251
|
+
END IF;
|
|
252
|
+
UPDATE managed_sign_in_method_operations SET lease_id=gen_random_uuid(),lease_until=clock_timestamp()+interval '2 minutes',
|
|
253
|
+
first_attempt_at=coalesce(first_attempt_at,clock_timestamp()),retry_until=coalesce(retry_until,clock_timestamp()+make_interval(secs=>p_retention)),
|
|
254
|
+
sender=coalesce(sender,p_sender),provider_scope=coalesce(provider_scope,p_scope),notification_status='outcome_unknown'
|
|
255
|
+
WHERE operation_id=item.operation_id RETURNING * INTO item;
|
|
256
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
257
|
+
RETURN jsonb_build_object('operationId',item.operation_id,'leaseId',item.lease_id,'email',item.notification_email,'provider',item.provider,'sender',item.sender);
|
|
258
|
+
END $body$;
|
|
259
|
+
REVOKE ALL ON FUNCTION claim_managed_sign_in_notification(uuid,text,text,integer) FROM PUBLIC;
|
|
260
|
+
|
|
261
|
+
CREATE FUNCTION settle_managed_sign_in_notification(p_operation uuid,p_lease uuid,p_status text)
|
|
262
|
+
RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
263
|
+
DECLARE applied boolean; previous_marker text := current_setting('opengeni.canonical_human_identity_lifecycle',true);
|
|
264
|
+
BEGIN
|
|
265
|
+
IF p_status NOT IN ('sent','failed','outcome_unknown') THEN RETURN false; END IF;
|
|
266
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle','active',true);
|
|
267
|
+
UPDATE managed_sign_in_method_operations SET notification_status=p_status,lease_id=NULL,lease_until=NULL,
|
|
268
|
+
available_at=clock_timestamp()+interval '1 minute' WHERE operation_id=p_operation AND lease_id=p_lease;
|
|
269
|
+
applied := FOUND;
|
|
270
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
271
|
+
RETURN applied;
|
|
272
|
+
END $body$;
|
|
273
|
+
REVOKE ALL ON FUNCTION settle_managed_sign_in_notification(uuid,uuid,text) FROM PUBLIC;
|
|
274
|
+
|
|
275
|
+
-- Compatibility recovery completion is not a second settings mutation surface.
|
|
276
|
+
-- It accepts only a fresh recovery-only session proving the exact pending
|
|
277
|
+
-- binding. The caller holds its actor fence across this and the old lifecycle.
|
|
278
|
+
CREATE FUNCTION assert_managed_sign_in_recovery(p_user text,p_session text,p_binding uuid,p_request jsonb)
|
|
279
|
+
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
280
|
+
DECLARE identity_row canonical_human_identities%ROWTYPE; provider_value text;
|
|
281
|
+
previous_marker text := current_setting('opengeni.canonical_human_identity_lifecycle',true);
|
|
282
|
+
BEGIN
|
|
283
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle','active',true);
|
|
284
|
+
SELECT i.* INTO identity_row FROM canonical_human_identities i JOIN canonical_human_identity_subjects s ON s.identity_id=i.id
|
|
285
|
+
WHERE s.auth_user_id=p_user AND s.status='active' FOR UPDATE OF i;
|
|
286
|
+
SELECT provider_id INTO provider_value FROM canonical_human_login_bindings
|
|
287
|
+
WHERE id=p_binding AND identity_id=identity_row.id AND status='recovery_pending';
|
|
288
|
+
IF identity_row.status IS DISTINCT FROM 'recovery_required' OR provider_value IS NULL
|
|
289
|
+
OR NOT validate_canonical_human_session(p_session,p_user,true)
|
|
290
|
+
OR NOT EXISTS (SELECT 1 FROM auth_sessions WHERE id=p_session AND user_id=p_user AND login_binding_id=p_binding
|
|
291
|
+
AND created_at>clock_timestamp()-interval '5 minutes' AND created_at<=clock_timestamp())
|
|
292
|
+
OR NOT EXISTS (SELECT 1 FROM auth_users WHERE id=p_user AND email_verified) THEN
|
|
293
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_REAUTHENTICATION_REQUIRED' USING ERRCODE='42501';
|
|
294
|
+
END IF;
|
|
295
|
+
IF identity_row.identity_revision IS DISTINCT FROM (p_request->>'expectedIdentityRevision')::bigint THEN
|
|
296
|
+
RAISE EXCEPTION 'SIGN_IN_METHOD_REVISION_CONFLICT' USING ERRCODE='40001';
|
|
297
|
+
END IF;
|
|
298
|
+
INSERT INTO managed_sign_in_method_operations(operation_id,auth_user_id,request_digest,kind,provider,notification_email)
|
|
299
|
+
SELECT (p_request->>'operationId')::uuid,p_user,p_request->>'requestDigest','recovery',
|
|
300
|
+
CASE WHEN provider_value='credential' THEN 'password' ELSE provider_value END,email FROM auth_users WHERE id=p_user;
|
|
301
|
+
PERFORM set_config('opengeni.canonical_human_identity_lifecycle',coalesce(previous_marker,''),true);
|
|
302
|
+
END $body$;
|
|
303
|
+
REVOKE ALL ON FUNCTION assert_managed_sign_in_recovery(text,text,uuid,jsonb) FROM PUBLIC;
|