@openlup/platform 0.15.0 → 0.16.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/CHANGELOG.md +40 -0
- package/README.md +350 -3
- package/dist/identity/adminAuthBinding.d.ts +28 -0
- package/dist/identity/adminAuthBinding.js +66 -0
- package/dist/identity/adminAuthorization.d.ts +34 -0
- package/dist/identity/adminAuthorization.js +72 -0
- package/dist/identity/adminContracts.d.ts +70 -0
- package/dist/identity/adminContracts.js +43 -0
- package/dist/identity/adminMagicLinkHandler.d.ts +39 -0
- package/dist/identity/adminMagicLinkHandler.js +94 -0
- package/dist/identity/adminMeHandler.d.ts +15 -0
- package/dist/identity/adminMeHandler.js +49 -0
- package/dist/identity/adminPreflight.d.ts +16 -0
- package/dist/identity/adminPreflight.js +38 -0
- package/dist/identity/adminUserActionPorts.d.ts +31 -0
- package/dist/identity/adminUserActionPorts.js +51 -0
- package/dist/identity/adminUserHandlers.d.ts +38 -0
- package/dist/identity/adminUserHandlers.js +140 -0
- package/dist/identity/authVerifyUrl.d.ts +6 -0
- package/dist/identity/authVerifyUrl.js +54 -0
- package/dist/identity/contracts/contracts.d.ts +8 -0
- package/dist/identity/contracts/contracts.js +2 -0
- package/dist/identity/contracts/emailTrust.d.ts +3 -0
- package/dist/identity/contracts/emailTrust.js +4 -0
- package/dist/identity/contracts/index.d.ts +8 -0
- package/dist/identity/contracts/index.js +8 -0
- package/dist/identity/contracts/oidc.d.ts +3 -0
- package/dist/identity/contracts/oidc.js +9 -0
- package/dist/identity/contracts/operatorAuth.d.ts +35 -0
- package/dist/identity/contracts/operatorAuth.js +53 -0
- package/dist/identity/contracts/operatorInvitations.d.ts +37 -0
- package/dist/identity/contracts/operatorInvitations.js +1 -0
- package/dist/identity/contracts/ports.d.ts +22 -0
- package/dist/identity/contracts/ports.js +1 -0
- package/dist/identity/contracts/privacy.d.ts +62 -0
- package/dist/identity/contracts/privacy.js +99 -0
- package/dist/identity/contracts/providerList.d.ts +3 -0
- package/dist/identity/contracts/providerList.js +11 -0
- package/dist/identity/contracts/types.d.ts +16 -0
- package/dist/identity/contracts/types.js +4 -0
- package/dist/identity/contribution.d.ts +8 -0
- package/dist/identity/contribution.js +22 -0
- package/dist/identity/customerAuth.d.ts +8 -0
- package/dist/identity/customerAuth.js +1 -0
- package/dist/identity/customerAuthorization.d.ts +5 -0
- package/dist/identity/customerAuthorization.js +22 -0
- package/dist/identity/customerContracts.d.ts +42 -0
- package/dist/identity/customerContracts.js +43 -0
- package/dist/identity/customerIdentityBinding.d.ts +44 -0
- package/dist/identity/customerIdentityBinding.js +60 -0
- package/dist/identity/customerIdentityVerifier.d.ts +8 -0
- package/dist/identity/customerIdentityVerifier.js +101 -0
- package/dist/identity/customerMagicLinkHandler.d.ts +38 -0
- package/dist/identity/customerMagicLinkHandler.js +115 -0
- package/dist/identity/customerMeHandler.d.ts +11 -0
- package/dist/identity/customerMeHandler.js +46 -0
- package/dist/identity/customerPorts.d.ts +4 -0
- package/dist/identity/customerPorts.js +1 -0
- package/dist/identity/customerSessionBinding.d.ts +38 -0
- package/dist/identity/customerSessionBinding.js +80 -0
- package/dist/identity/customerSessionIssuer.d.ts +10 -0
- package/dist/identity/customerSessionIssuer.js +57 -0
- package/dist/identity/customerVerifyOtpHandler.d.ts +8 -0
- package/dist/identity/customerVerifyOtpHandler.js +41 -0
- package/dist/identity/directOperatorInvitationStore.d.ts +19 -0
- package/dist/identity/directOperatorInvitationStore.js +193 -0
- package/dist/identity/emailTrustPolicy.d.ts +14 -0
- package/dist/identity/emailTrustPolicy.js +16 -0
- package/dist/identity/identityPersonalDataContributor.d.ts +7 -0
- package/dist/identity/identityPersonalDataContributor.js +46 -0
- package/dist/identity/identityReadiness.d.ts +10 -0
- package/dist/identity/identityReadiness.js +25 -0
- package/dist/identity/index.d.ts +67 -0
- package/dist/identity/index.js +66 -0
- package/dist/identity/inviteAdminUserUseCase.d.ts +89 -0
- package/dist/identity/inviteAdminUserUseCase.js +75 -0
- package/dist/identity/jwtLibrary.d.ts +54 -0
- package/dist/identity/jwtLibrary.js +1 -0
- package/dist/identity/machineActorWriteFence.d.ts +4 -0
- package/dist/identity/machineActorWriteFence.js +13 -0
- package/dist/identity/magicLinkPorts.d.ts +28 -0
- package/dist/identity/magicLinkPorts.js +1 -0
- package/dist/identity/managedAccountLinkStore.d.ts +2 -0
- package/dist/identity/managedAccountLinkStore.js +93 -0
- package/dist/identity/managedAdminAuthVerifier.d.ts +7 -0
- package/dist/identity/managedAdminAuthVerifier.js +27 -0
- package/dist/identity/managedAdminUserGateways.d.ts +123 -0
- package/dist/identity/managedAdminUserGateways.js +127 -0
- package/dist/identity/managedClient.d.ts +90 -0
- package/dist/identity/managedClient.js +10 -0
- package/dist/identity/managedClientAuthLink.d.ts +3 -0
- package/dist/identity/managedClientAuthLink.js +115 -0
- package/dist/identity/managedCustomerMe.d.ts +13 -0
- package/dist/identity/managedCustomerMe.js +30 -0
- package/dist/identity/managedEmailTrustPolicy.d.ts +11 -0
- package/dist/identity/managedEmailTrustPolicy.js +105 -0
- package/dist/identity/managedIdentityVerifier.d.ts +2 -0
- package/dist/identity/managedIdentityVerifier.js +16 -0
- package/dist/identity/managedPrivacy.d.ts +4 -0
- package/dist/identity/managedPrivacy.js +39 -0
- package/dist/identity/messages.d.ts +29 -0
- package/dist/identity/messages.js +39 -0
- package/dist/identity/oidcIdentityVerifier.d.ts +45 -0
- package/dist/identity/oidcIdentityVerifier.js +309 -0
- package/dist/identity/oidcPrincipal.d.ts +2 -0
- package/dist/identity/oidcPrincipal.js +10 -0
- package/dist/identity/operatorAdminAuth.d.ts +6 -0
- package/dist/identity/operatorAdminAuth.js +13 -0
- package/dist/identity/operatorAuthHandlers.d.ts +18 -0
- package/dist/identity/operatorAuthHandlers.js +71 -0
- package/dist/identity/operatorIdentityVerifier.d.ts +8 -0
- package/dist/identity/operatorIdentityVerifier.js +19 -0
- package/dist/identity/operatorInvitationLifecycle.d.ts +7 -0
- package/dist/identity/operatorInvitationLifecycle.js +15 -0
- package/dist/identity/operatorInvitationPorts.d.ts +31 -0
- package/dist/identity/operatorInvitationPorts.js +1 -0
- package/dist/identity/operatorPorts.d.ts +37 -0
- package/dist/identity/operatorPorts.js +1 -0
- package/dist/identity/operatorSession.d.ts +13 -0
- package/dist/identity/operatorSession.js +31 -0
- package/dist/identity/operatorTokenAdminAuth.d.ts +15 -0
- package/dist/identity/operatorTokenAdminAuth.js +50 -0
- package/dist/identity/personalDataRegistry.d.ts +4 -0
- package/dist/identity/personalDataRegistry.js +11 -0
- package/dist/identity/platformAdminAuth.d.ts +15 -0
- package/dist/identity/platformAdminAuth.js +1 -0
- package/dist/identity/ports.d.ts +127 -0
- package/dist/identity/ports.js +38 -0
- package/dist/identity/postgresAccountLinkStore.d.ts +7 -0
- package/dist/identity/postgresAccountLinkStore.js +52 -0
- package/dist/identity/postgresCustomerMe.d.ts +3 -0
- package/dist/identity/postgresCustomerMe.js +21 -0
- package/dist/identity/postgresCustomerRecovery.d.ts +11 -0
- package/dist/identity/postgresCustomerRecovery.js +57 -0
- package/dist/identity/postgresOperatorIdentity.d.ts +5 -0
- package/dist/identity/postgresOperatorIdentity.js +137 -0
- package/dist/identity/postgresPrivacy.d.ts +7 -0
- package/dist/identity/postgresPrivacy.js +126 -0
- package/dist/identity/privacyHandlers.d.ts +10 -0
- package/dist/identity/privacyHandlers.js +35 -0
- package/dist/identity/privacyPorts.d.ts +72 -0
- package/dist/identity/privacyPorts.js +8 -0
- package/dist/identity/privacyUseCases.d.ts +5 -0
- package/dist/identity/privacyUseCases.js +55 -0
- package/dist/identity/reconcileAccountForPrincipal.d.ts +20 -0
- package/dist/identity/reconcileAccountForPrincipal.js +66 -0
- package/dist/identity/reconcileAccountHandler.d.ts +11 -0
- package/dist/identity/reconcileAccountHandler.js +30 -0
- package/dist/identity/removeAdminUserUseCase.d.ts +31 -0
- package/dist/identity/removeAdminUserUseCase.js +60 -0
- package/dist/identity/response.d.ts +3 -0
- package/dist/identity/response.js +2 -0
- package/dist/identity/sql.d.ts +44 -0
- package/dist/identity/sql.js +10 -0
- package/package.json +12 -2
- package/release-gates.json +22 -0
- package/sql/20260810180001_self_host_actor_auth.sql +40 -0
- package/sql/20260810204057_customer_identity_read.sql +31 -0
- package/sql/20260814090000_customer_selfservice_recovery.sql +477 -0
- package/sql/20260901143000_admin_membership_authority_foundation.sql +234 -0
- package/sql/20261018120000_identity_operator_invitations.sql +42 -0
- package/sql/20261018180000_identity_operator_challenges.sql +56 -0
- package/sql/20261018190000_identity_privacy_erasures.sql +15 -0
- package/src/identity/adminAuthBinding.ts +76 -0
- package/src/identity/adminAuthorization.ts +106 -0
- package/src/identity/adminContracts.ts +52 -0
- package/src/identity/adminMagicLinkHandler.ts +132 -0
- package/src/identity/adminMeHandler.ts +64 -0
- package/src/identity/adminPreflight.ts +46 -0
- package/src/identity/adminUserActionPorts.ts +78 -0
- package/src/identity/adminUserHandlers.ts +174 -0
- package/src/identity/authVerifyUrl.ts +56 -0
- package/src/identity/contracts/contracts.ts +15 -0
- package/src/identity/contracts/emailTrust.ts +5 -0
- package/src/identity/contracts/index.ts +8 -0
- package/src/identity/contracts/oidc.ts +10 -0
- package/src/identity/contracts/operatorAuth.ts +61 -0
- package/src/identity/contracts/operatorInvitations.ts +32 -0
- package/src/identity/contracts/ports.ts +25 -0
- package/src/identity/contracts/privacy.ts +86 -0
- package/src/identity/contracts/providerList.ts +13 -0
- package/src/identity/contracts/types.ts +25 -0
- package/src/identity/contribution.ts +29 -0
- package/src/identity/customerAuth.ts +3 -0
- package/src/identity/customerAuthorization.ts +38 -0
- package/src/identity/customerContracts.ts +52 -0
- package/src/identity/customerIdentityBinding.ts +125 -0
- package/src/identity/customerIdentityVerifier.ts +141 -0
- package/src/identity/customerMagicLinkHandler.ts +171 -0
- package/src/identity/customerMeHandler.ts +64 -0
- package/src/identity/customerPorts.ts +2 -0
- package/src/identity/customerSessionBinding.ts +114 -0
- package/src/identity/customerSessionIssuer.ts +71 -0
- package/src/identity/customerVerifyOtpHandler.ts +44 -0
- package/src/identity/directOperatorInvitationStore.ts +178 -0
- package/src/identity/emailTrustPolicy.ts +16 -0
- package/src/identity/identityPersonalDataContributor.ts +48 -0
- package/src/identity/identityReadiness.ts +26 -0
- package/src/identity/index.ts +69 -0
- package/src/identity/inviteAdminUserUseCase.ts +134 -0
- package/src/identity/jwtLibrary.ts +24 -0
- package/src/identity/machineActorWriteFence.ts +19 -0
- package/src/identity/magicLinkPorts.ts +7 -0
- package/src/identity/managedAccountLinkStore.ts +105 -0
- package/src/identity/managedAdminAuthVerifier.ts +40 -0
- package/src/identity/managedAdminUserGateways.ts +164 -0
- package/src/identity/managedClient.ts +58 -0
- package/src/identity/managedClientAuthLink.ts +120 -0
- package/src/identity/managedCustomerMe.ts +52 -0
- package/src/identity/managedEmailTrustPolicy.ts +90 -0
- package/src/identity/managedIdentityVerifier.ts +20 -0
- package/src/identity/managedPrivacy.ts +37 -0
- package/src/identity/messages.ts +41 -0
- package/src/identity/oidcIdentityVerifier.ts +229 -0
- package/src/identity/oidcPrincipal.ts +10 -0
- package/src/identity/operatorAdminAuth.ts +14 -0
- package/src/identity/operatorAuthHandlers.ts +52 -0
- package/src/identity/operatorIdentityVerifier.ts +19 -0
- package/src/identity/operatorInvitationLifecycle.ts +14 -0
- package/src/identity/operatorInvitationPorts.ts +11 -0
- package/src/identity/operatorPorts.ts +11 -0
- package/src/identity/operatorSession.ts +30 -0
- package/src/identity/operatorTokenAdminAuth.ts +79 -0
- package/src/identity/personalDataRegistry.ts +8 -0
- package/src/identity/platformAdminAuth.ts +7 -0
- package/src/identity/ports.ts +158 -0
- package/src/identity/postgresAccountLinkStore.ts +49 -0
- package/src/identity/postgresCustomerMe.ts +33 -0
- package/src/identity/postgresCustomerRecovery.ts +72 -0
- package/src/identity/postgresOperatorIdentity.ts +123 -0
- package/src/identity/postgresPrivacy.ts +117 -0
- package/src/identity/privacyHandlers.ts +33 -0
- package/src/identity/privacyPorts.ts +52 -0
- package/src/identity/privacyUseCases.ts +48 -0
- package/src/identity/reconcileAccountForPrincipal.ts +99 -0
- package/src/identity/reconcileAccountHandler.ts +36 -0
- package/src/identity/removeAdminUserUseCase.ts +85 -0
- package/src/identity/response.ts +3 -0
- package/src/identity/sql.ts +10 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createIdentityResponder } from "./response.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
z.config({ jitless: true });
|
|
4
|
+
const customerOtpVerifyRequestSchema = z.object({
|
|
5
|
+
email: z.string().trim().toLowerCase().email().max(160),
|
|
6
|
+
token: z.string().trim().min(16).max(256),
|
|
7
|
+
}).strict();
|
|
8
|
+
const customerOtpVerifyResponseSchema = z.object({
|
|
9
|
+
accessToken: z.string().min(1),
|
|
10
|
+
expiresAt: z.string().datetime({ offset: true }),
|
|
11
|
+
user: z.object({ id: z.guid(), email: z.string().email() }).strict(),
|
|
12
|
+
}).strict();
|
|
13
|
+
export function createCustomerVerifyOtpHandler(options) {
|
|
14
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = createIdentityResponder(options);
|
|
15
|
+
return async (req, res) => {
|
|
16
|
+
if (!options.enabled()) {
|
|
17
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer accounts are disabled");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (req.method !== "POST")
|
|
21
|
+
return sendMethodNotAllowed(res, ["POST"]);
|
|
22
|
+
const request = customerOtpVerifyRequestSchema.safeParse(req.body);
|
|
23
|
+
if (!request.success) {
|
|
24
|
+
sendBffError(res, "BAD_REQUEST", "Invalid one-time code request", { details: request.error.flatten() });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const resolved = options.resolveSession(options.env);
|
|
28
|
+
if (!resolved.binding) {
|
|
29
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer auth is not configured", {
|
|
30
|
+
details: { feature: "customer_auth", reason: resolved.error },
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const session = await resolved.binding.verifyChallenge(request.data.token);
|
|
35
|
+
if (!session) {
|
|
36
|
+
sendBffError(res, "UNAUTHORIZED", "One-time code is invalid or expired");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
sendBffSuccess(res, customerOtpVerifyResponseSchema.parse(session));
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type PgQueryExecutor, type PostgresDataGatewayEnv, type PostgresDataGatewayOptions } from "../data/index.js";
|
|
2
|
+
import type { OperatorInvitationStore } from "./operatorInvitationPorts.js";
|
|
3
|
+
/** Owner-authority lane, never the actor gateway. One transaction per command. */
|
|
4
|
+
export declare function createDirectOperatorInvitationStore(env: PostgresDataGatewayEnv, options?: PostgresDataGatewayOptions): OperatorInvitationStore & {
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
/** Principal creation commits independently; an invitation failure leaves it reusable. */
|
|
8
|
+
export declare function createDirectOperatorIdentityDirectory(env: PostgresDataGatewayEnv, options?: PostgresDataGatewayOptions): {
|
|
9
|
+
ensurePrincipal(input: {
|
|
10
|
+
email: string;
|
|
11
|
+
redirectTo: string;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
principalId: string;
|
|
14
|
+
created: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
close: () => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
/** Trusted authentication only; caller owns the transaction, including challenge consumption. */
|
|
19
|
+
export declare function acceptDirectOperatorInvitationInTransaction(db: PgQueryExecutor, id: string): Promise<import("./contracts/operatorInvitations.js").OperatorInvitationAcceptance>;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { createPostgresRoleFreeTransactionLane } from "../data/index.js";
|
|
2
|
+
import { assertOperatorInvitationTtl, invitationFailure } from "./operatorInvitationLifecycle.js";
|
|
3
|
+
const iso = (value) => value instanceof Date ? value.toISOString() : new Date(String(value)).toISOString();
|
|
4
|
+
const role = (value) => value === "distributor" ? "distributor" : "admin";
|
|
5
|
+
const snapshot = (row) => ({ principalId: String(row.principal_id), role: role(row.role) });
|
|
6
|
+
function result(row, code) {
|
|
7
|
+
return { ...snapshot(row), code, membershipState: "invited", invitationId: String(row.invitation_id), invitedAt: iso(row.invited_at), expiresAt: iso(row.expires_at) };
|
|
8
|
+
}
|
|
9
|
+
async function event(db, action, before, after, actor) {
|
|
10
|
+
await db.query(`INSERT INTO public.identity_operator_invitation_events
|
|
11
|
+
(event_id, invitation_id, principal_id, action, actor_kind, actor_principal_id, old_value, new_value, occurred_at)
|
|
12
|
+
VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6::jsonb, $7::jsonb, clock_timestamp())`, [after.invitation_id, after.principal_id, action, actor ? "human" : "system", actor, JSON.stringify(before ?? {}), JSON.stringify(after)]);
|
|
13
|
+
}
|
|
14
|
+
async function lock(db, id) {
|
|
15
|
+
const principal = (await db.query("SELECT principal_id, email FROM public.platform_human_principals WHERE principal_id = $1", [id])).rows[0];
|
|
16
|
+
if (!principal)
|
|
17
|
+
return null;
|
|
18
|
+
await db.query("SELECT pg_advisory_xact_lock(hashtext($1)::bigint)", [`operator_invitation:${String(principal.email).trim().toLowerCase()}`]);
|
|
19
|
+
return principal;
|
|
20
|
+
}
|
|
21
|
+
async function membership(db, id) {
|
|
22
|
+
return (await db.query("SELECT * FROM public.platform_human_memberships WHERE principal_id = $1 FOR UPDATE", [id])).rows[0] ?? null;
|
|
23
|
+
}
|
|
24
|
+
async function machine(db, id) {
|
|
25
|
+
return (await db.query("SELECT EXISTS (SELECT 1 FROM public.platform_control_operators WHERE principal_id = $1) AS present", [id])).rows[0]?.present === true;
|
|
26
|
+
}
|
|
27
|
+
async function lockCommandMemberships(db, actor, target) {
|
|
28
|
+
// Lock both rows in the same order before taking either actor or target locks.
|
|
29
|
+
await db.query("SELECT principal_id FROM public.platform_human_memberships WHERE principal_id = ANY($1::uuid[]) ORDER BY principal_id FOR UPDATE", [[actor, target]]);
|
|
30
|
+
}
|
|
31
|
+
async function administrator(db, id) {
|
|
32
|
+
const row = (await db.query("SELECT role, membership_state FROM public.platform_human_memberships WHERE principal_id = $1 FOR SHARE", [id])).rows[0];
|
|
33
|
+
if (row?.role !== "admin" || row.membership_state !== "active" || await machine(db, id))
|
|
34
|
+
throw invitationFailure("HUMAN_ADMIN_REQUIRED", "FORBIDDEN");
|
|
35
|
+
}
|
|
36
|
+
async function invitation(db, id) {
|
|
37
|
+
const row = (await db.query("SELECT * FROM public.identity_operator_invitations WHERE principal_id = $1 FOR UPDATE", [id])).rows[0];
|
|
38
|
+
if (!row)
|
|
39
|
+
return null;
|
|
40
|
+
// Capture the database clock after serialization; equality is already expired.
|
|
41
|
+
const expiredNow = (await db.query("SELECT clock_timestamp() >= $1::timestamptz AS expired", [row.expires_at])).rows[0].expired;
|
|
42
|
+
if (row.phase === "invited" && expiredNow === true) {
|
|
43
|
+
const expired = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'expired', closed_at = expires_at WHERE principal_id = $1 RETURNING *", [id])).rows[0];
|
|
44
|
+
await event(db, "expire", row, expired, null);
|
|
45
|
+
return expired;
|
|
46
|
+
}
|
|
47
|
+
return row;
|
|
48
|
+
}
|
|
49
|
+
/** Owner-authority lane, never the actor gateway. One transaction per command. */
|
|
50
|
+
export function createDirectOperatorInvitationStore(env, options = {}) {
|
|
51
|
+
const lane = createPostgresRoleFreeTransactionLane(env, options);
|
|
52
|
+
const run = (work) => lane.run((db) => work(db));
|
|
53
|
+
return {
|
|
54
|
+
issue(input) {
|
|
55
|
+
assertOperatorInvitationTtl(input.ttlSeconds);
|
|
56
|
+
return run(async (db) => {
|
|
57
|
+
// Serialize same-target commands, then lock both memberships in order.
|
|
58
|
+
const principal = await lock(db, input.targetPrincipalId);
|
|
59
|
+
await lockCommandMemberships(db, input.actorPrincipalId, input.targetPrincipalId);
|
|
60
|
+
await administrator(db, input.actorPrincipalId);
|
|
61
|
+
if ((await db.query("SELECT 1 FROM public.identity_privacy_erasures WHERE principal_id = $1", [input.targetPrincipalId])).rows.length)
|
|
62
|
+
return { code: "IDENTITY_CONFLICT" };
|
|
63
|
+
const email = input.email.trim().toLowerCase();
|
|
64
|
+
if (!principal || String(principal.email).trim().toLowerCase() !== email || await machine(db, input.targetPrincipalId))
|
|
65
|
+
return { code: "IDENTITY_CONFLICT" };
|
|
66
|
+
const member = await membership(db, input.targetPrincipalId);
|
|
67
|
+
if (member?.membership_state === "active")
|
|
68
|
+
return { code: "MEMBERSHIP_ALREADY_ACTIVE" };
|
|
69
|
+
const previous = await invitation(db, input.targetPrincipalId);
|
|
70
|
+
if (previous?.phase === "invited")
|
|
71
|
+
return previous.role === input.role ? result(previous, "ALREADY_INVITED") : { code: "INVITATION_ROLE_CONFLICT" };
|
|
72
|
+
const collision = (await db.query("SELECT principal_id FROM public.identity_operator_invitations WHERE email_key = $1", [email])).rows[0];
|
|
73
|
+
if (collision && collision.principal_id !== input.targetPrincipalId)
|
|
74
|
+
return { code: "IDENTITY_CONFLICT" };
|
|
75
|
+
const row = (await db.query(`WITH stamp AS (SELECT clock_timestamp() AS at)
|
|
76
|
+
INSERT INTO public.identity_operator_invitations
|
|
77
|
+
(principal_id, invitation_id, email_key, role, phase, invited_by, invited_at, expires_at)
|
|
78
|
+
SELECT $1, gen_random_uuid(), $2, $3, 'invited', $4, stamp.at, stamp.at + $5 * interval '1 second' FROM stamp WHERE true
|
|
79
|
+
ON CONFLICT (principal_id) DO UPDATE SET invitation_id = EXCLUDED.invitation_id, email_key = EXCLUDED.email_key,
|
|
80
|
+
role = EXCLUDED.role, phase = 'invited', invited_by = EXCLUDED.invited_by, invited_at = EXCLUDED.invited_at,
|
|
81
|
+
expires_at = EXCLUDED.expires_at, accepted_at = NULL, closed_at = NULL RETURNING *`, [input.targetPrincipalId, email, input.role, input.actorPrincipalId, input.ttlSeconds])).rows[0];
|
|
82
|
+
await event(db, previous || member ? "reissue" : "issue", previous, row, input.actorPrincipalId);
|
|
83
|
+
return result(row, previous || member ? "INVITATION_REISSUED" : "INVITATION_CREATED");
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
read(id) {
|
|
87
|
+
return run(async (db) => {
|
|
88
|
+
if (!await lock(db, id))
|
|
89
|
+
return null;
|
|
90
|
+
const member = await membership(db, id);
|
|
91
|
+
if (member?.membership_state === "active")
|
|
92
|
+
return { ...snapshot(member), membershipState: "active" };
|
|
93
|
+
const pending = await invitation(db, id);
|
|
94
|
+
if (pending && pending.phase !== "accepted")
|
|
95
|
+
return { ...snapshot(pending), membershipState: pending.phase === "invited" ? "invited" : pending.phase === "expired" ? "expired" : "revoked" };
|
|
96
|
+
return member ? { ...snapshot(member), membershipState: "revoked" } : null;
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
acceptOperatorInvitation(id) {
|
|
100
|
+
return run((db) => acceptDirectOperatorInvitationInTransaction(db, id));
|
|
101
|
+
},
|
|
102
|
+
remove(input) {
|
|
103
|
+
return run(async (db) => {
|
|
104
|
+
const principal = await lock(db, input.targetPrincipalId);
|
|
105
|
+
await lockCommandMemberships(db, input.actorPrincipalId, input.targetPrincipalId);
|
|
106
|
+
await administrator(db, input.actorPrincipalId);
|
|
107
|
+
if (input.actorPrincipalId === input.targetPrincipalId)
|
|
108
|
+
throw invitationFailure("SELF_REVOKE_FORBIDDEN", "FORBIDDEN");
|
|
109
|
+
if (!principal || await machine(db, input.targetPrincipalId))
|
|
110
|
+
throw invitationFailure("IDENTITY_CONFLICT");
|
|
111
|
+
const member = await membership(db, input.targetPrincipalId);
|
|
112
|
+
const pending = await invitation(db, input.targetPrincipalId);
|
|
113
|
+
if (pending?.phase === "invited") {
|
|
114
|
+
const cancelled = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'cancelled', closed_at = clock_timestamp() WHERE principal_id = $1 RETURNING *", [input.targetPrincipalId])).rows[0];
|
|
115
|
+
await event(db, "cancel", pending, cancelled, input.actorPrincipalId);
|
|
116
|
+
}
|
|
117
|
+
if (member?.membership_state === "active")
|
|
118
|
+
await db.query("SELECT public.platform_revoke_human_membership($1, $2, $3)", [input.actorPrincipalId, input.targetPrincipalId, input.reason ?? null]);
|
|
119
|
+
if (!member && !pending)
|
|
120
|
+
throw invitationFailure("ADMIN_NOT_FOUND", "BAD_REQUEST");
|
|
121
|
+
return { revoked: true };
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
close: lane.close,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Principal creation commits independently; an invitation failure leaves it reusable. */
|
|
128
|
+
export function createDirectOperatorIdentityDirectory(env, options = {}) {
|
|
129
|
+
const lane = createPostgresRoleFreeTransactionLane(env, options);
|
|
130
|
+
return {
|
|
131
|
+
ensurePrincipal(input) {
|
|
132
|
+
const email = input.email.trim().toLowerCase();
|
|
133
|
+
return lane.run(async (executor) => {
|
|
134
|
+
const db = executor;
|
|
135
|
+
await db.query("SELECT pg_advisory_xact_lock(hashtext($1)::bigint)", [`operator_invitation:${email}`]);
|
|
136
|
+
const matches = (await db.query("SELECT principal_id FROM public.platform_human_principals WHERE lower(btrim(email)) = $1", [email])).rows;
|
|
137
|
+
if (matches.length > 1)
|
|
138
|
+
throw invitationFailure("IDENTITY_CONFLICT");
|
|
139
|
+
if (matches[0]) {
|
|
140
|
+
const principalId = String(matches[0].principal_id);
|
|
141
|
+
if (await machine(db, principalId))
|
|
142
|
+
throw invitationFailure("IDENTITY_CONFLICT");
|
|
143
|
+
return { principalId, created: false };
|
|
144
|
+
}
|
|
145
|
+
const row = (await db.query("INSERT INTO public.platform_human_principals (principal_id, email) VALUES (gen_random_uuid(), $1) RETURNING principal_id", [email])).rows[0];
|
|
146
|
+
return { principalId: String(row.principal_id), created: true };
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
close: lane.close,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** Trusted authentication only; caller owns the transaction, including challenge consumption. */
|
|
153
|
+
export async function acceptDirectOperatorInvitationInTransaction(db, id) {
|
|
154
|
+
if (!await lock(db, id) || await machine(db, id))
|
|
155
|
+
return { code: "NO_INVITATION" };
|
|
156
|
+
if ((await db.query("SELECT 1 FROM public.identity_privacy_erasures WHERE principal_id = $1", [id])).rows.length)
|
|
157
|
+
return { code: "INVITATION_REVOKED" };
|
|
158
|
+
const member = await membership(db, id);
|
|
159
|
+
if (member?.membership_state === "active") {
|
|
160
|
+
if (member.role !== "admin" && member.role !== "distributor")
|
|
161
|
+
return { code: "NO_INVITATION" };
|
|
162
|
+
return { code: "ALREADY_ACTIVE", role: member.role };
|
|
163
|
+
}
|
|
164
|
+
const pending = await invitation(db, id);
|
|
165
|
+
if (!pending)
|
|
166
|
+
return { code: "NO_INVITATION" };
|
|
167
|
+
if (pending.phase === "expired")
|
|
168
|
+
return { code: "INVITATION_EXPIRED" };
|
|
169
|
+
if (pending.phase !== "invited" || (pending.role !== "admin" && pending.role !== "distributor"))
|
|
170
|
+
return { code: "INVITATION_REVOKED" };
|
|
171
|
+
const clock = (await db.query("WITH stamp AS (SELECT clock_timestamp() AS at) SELECT at, at >= $1::timestamptz AS expired FROM stamp", [pending.expires_at])).rows[0];
|
|
172
|
+
const at = clock.at;
|
|
173
|
+
if (clock.expired === true) {
|
|
174
|
+
const expired = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'expired', closed_at = expires_at WHERE principal_id = $1 RETURNING *", [id])).rows[0];
|
|
175
|
+
await event(db, "expire", pending, expired, null);
|
|
176
|
+
return { code: "INVITATION_EXPIRED" };
|
|
177
|
+
}
|
|
178
|
+
if (member) {
|
|
179
|
+
const setting = (await db.query("SELECT current_setting('app.platform_human_membership_revoke', true) AS previous")).rows[0]?.previous;
|
|
180
|
+
await db.query("SELECT set_config('app.platform_human_membership_revoke', 'true', true)");
|
|
181
|
+
await db.query(`UPDATE public.platform_human_memberships SET membership_state = 'active', membership_provenance = 'invitation',
|
|
182
|
+
membership_accepted_at = $2, membership_revoked_at = NULL, membership_revoked_by = NULL, membership_revocation_reason = NULL WHERE principal_id = $1`, [id, at]);
|
|
183
|
+
await db.query("UPDATE public.platform_human_memberships SET role = $2 WHERE principal_id = $1", [id, pending.role]);
|
|
184
|
+
await db.query("SELECT set_config('app.platform_human_membership_revoke', $1, true)", [setting ?? ""]);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
await db.query(`INSERT INTO public.platform_human_memberships (principal_id, role, membership_state, membership_provenance, membership_accepted_at)
|
|
188
|
+
VALUES ($1, $2, 'active', 'invitation', $3)`, [id, pending.role, at]);
|
|
189
|
+
}
|
|
190
|
+
const accepted = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'accepted', accepted_at = $2, closed_at = $2 WHERE principal_id = $1 RETURNING *", [id, at])).rows[0];
|
|
191
|
+
await event(db, "accept", pending, accepted, id);
|
|
192
|
+
return { code: "ACTIVATED", role: role(pending.role) };
|
|
193
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EmailTrustFailureCode } from "./contracts/emailTrust.js";
|
|
2
|
+
export type EmailTrustAssessment = {
|
|
3
|
+
trusted: true;
|
|
4
|
+
} | {
|
|
5
|
+
trusted: false;
|
|
6
|
+
code: EmailTrustFailureCode;
|
|
7
|
+
};
|
|
8
|
+
export interface EmailTrustPolicy {
|
|
9
|
+
assess(): Promise<EmailTrustAssessment>;
|
|
10
|
+
}
|
|
11
|
+
/** Explicitly retain verifier-only admission. This does not bypass mailbox verification. */
|
|
12
|
+
export declare function createVerifierResultOnlyEmailTrustPolicy(): EmailTrustPolicy;
|
|
13
|
+
/** Invalid, absent or throwing JavaScript bindings fail closed, including invalid results. */
|
|
14
|
+
export declare function assessEmailTrust(policy: EmailTrustPolicy): Promise<EmailTrustAssessment>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Explicitly retain verifier-only admission. This does not bypass mailbox verification. */
|
|
2
|
+
export function createVerifierResultOnlyEmailTrustPolicy() {
|
|
3
|
+
return { async assess() { return { trusted: true }; } };
|
|
4
|
+
}
|
|
5
|
+
/** Invalid, absent or throwing JavaScript bindings fail closed, including invalid results. */
|
|
6
|
+
export async function assessEmailTrust(policy) {
|
|
7
|
+
try {
|
|
8
|
+
const result = await policy.assess();
|
|
9
|
+
if (result?.trusted === true)
|
|
10
|
+
return { trusted: true };
|
|
11
|
+
if (result?.trusted === false && result.code === "EMAIL_TRUST_ENVELOPE_REFUSED")
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
catch { /* Do not expose policy or transport details. */ }
|
|
15
|
+
return { trusted: false, code: "EMAIL_TRUST_SETTINGS_UNAVAILABLE" };
|
|
16
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PersonalDataObligation } from "./contracts/privacy.js";
|
|
2
|
+
import { type PersonalDataContributor, type PrivacyStore } from "./privacyPorts.js";
|
|
3
|
+
export interface IdentityPrivacyPolicy {
|
|
4
|
+
/** Explicit dispositions for populated opaque contact fields; absence refuses erasure. */
|
|
5
|
+
retainedContactFields: Readonly<Record<string, PersonalDataObligation>>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createIdentityPersonalDataContributor<C>(store: PrivacyStore<C>, policy: IdentityPrivacyPolicy, randomId: () => string): PersonalDataContributor<C>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { PrivacyRefusal } from "./privacyPorts.js";
|
|
2
|
+
const owned = new Set(["id", "email", "first_name", "last_name", "phone", "country", "principal_id", "created_at", "updated_at"]);
|
|
3
|
+
const populated = (value) => value !== null && value !== "" && !(typeof value === "object" && Object.keys(value).length === 0);
|
|
4
|
+
export function createIdentityPersonalDataContributor(store, policy, randomId) {
|
|
5
|
+
function obligations(identity) {
|
|
6
|
+
const contact = identity.contact;
|
|
7
|
+
const result = [{ owner: "identity", resource: "identity.audit-and-receipt", disposition: "retain", reasonCode: "PSEUDONYMOUS_AUDIT", remote: false }];
|
|
8
|
+
if (contact && typeof contact === "object" && !Array.isArray(contact)) {
|
|
9
|
+
for (const [key, value] of Object.entries(contact)) {
|
|
10
|
+
if (owned.has(key) || !populated(value))
|
|
11
|
+
continue;
|
|
12
|
+
const declaration = policy.retainedContactFields[key];
|
|
13
|
+
result.push(declaration?.disposition === "retain" && declaration.resource === `clients.${key}` ? declaration : { owner: "unassigned", resource: `clients.${key}`, disposition: "unresolved", reasonCode: "PRIVACY_OWNERSHIP_UNDECLARED", remote: false });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
id: "identity", version: "identity.personal-data.v1",
|
|
20
|
+
ownedResources: ["identity.contact-profile", "identity.principal", "identity.membership", "identity.invitations", "identity.audit-and-receipt"],
|
|
21
|
+
async prepareErasure() { },
|
|
22
|
+
async export(_context, subject) {
|
|
23
|
+
const unavailable = Array.isArray(subject.identity.unavailable) ? subject.identity.unavailable.map(String) : [];
|
|
24
|
+
const data = { ...subject.identity };
|
|
25
|
+
const contact = data.contact;
|
|
26
|
+
if (contact && typeof contact === "object" && !Array.isArray(contact)) {
|
|
27
|
+
data.contact = Object.fromEntries(Object.entries(contact).filter(([key]) => owned.has(key) || key === "auth_user_id"));
|
|
28
|
+
for (const key of Object.keys(contact))
|
|
29
|
+
if (!owned.has(key) && key !== "auth_user_id")
|
|
30
|
+
unavailable.push(`contact.${key}`);
|
|
31
|
+
}
|
|
32
|
+
return { contributorId: "identity", version: "identity.personal-data.v1", data,
|
|
33
|
+
coverage: { complete: unavailable.length === 0, included: Object.keys(subject.identity).filter(k => k !== "unavailable"), unavailable }, obligations: obligations(subject.identity) };
|
|
34
|
+
},
|
|
35
|
+
async assessErasure(_context, subject) {
|
|
36
|
+
const declared = obligations(subject.identity);
|
|
37
|
+
if (declared.some(o => o.disposition === "unresolved"))
|
|
38
|
+
throw new PrivacyRefusal("PRIVACY_OWNERSHIP_UNDECLARED");
|
|
39
|
+
return { obligations: declared };
|
|
40
|
+
},
|
|
41
|
+
async eraseOrRetain(context, subject, assessment, actor) {
|
|
42
|
+
await store.eraseIdentity(context, subject, actor, `erased+${randomId()}@identity.invalid`);
|
|
43
|
+
return { contributorId: "identity", version: "identity.personal-data.v1", obligations: assessment.obligations };
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReadinessIssue } from "@openlup/core/readiness";
|
|
2
|
+
import { type EmailTrustPolicy } from "./emailTrustPolicy.js";
|
|
3
|
+
/** The module field stays structural so older core readiness types still compile; no new core code is needed. */
|
|
4
|
+
export declare function identityEmailTrustReadiness(policy: EmailTrustPolicy): Promise<readonly (ReadinessIssue & {
|
|
5
|
+
module: "identity";
|
|
6
|
+
})[]>;
|
|
7
|
+
/** Discovery and fresh key availability are independent readiness observations. */
|
|
8
|
+
export declare function identityOidcReadiness(verifier: import("./oidcIdentityVerifier.js").OidcIdentityVerifier): Promise<readonly (ReadinessIssue & {
|
|
9
|
+
module: "identity";
|
|
10
|
+
})[]>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { assessEmailTrust } from "./emailTrustPolicy.js";
|
|
2
|
+
/** The module field stays structural so older core readiness types still compile; no new core code is needed. */
|
|
3
|
+
export async function identityEmailTrustReadiness(policy) {
|
|
4
|
+
const result = await assessEmailTrust(policy);
|
|
5
|
+
if (result.trusted)
|
|
6
|
+
return [];
|
|
7
|
+
return [{
|
|
8
|
+
code: "OPENLUP_E_PORT_MISSING", package: "@openlup/platform", module: "identity",
|
|
9
|
+
subject: "emailTrustPolicy", observation: result.code,
|
|
10
|
+
fix: result.code === "EMAIL_TRUST_ENVELOPE_REFUSED"
|
|
11
|
+
? "Disable automatic confirmation and declare only providers that verify email."
|
|
12
|
+
: "Restore access to valid public identity settings or bind an explicit email-trust policy.",
|
|
13
|
+
}];
|
|
14
|
+
}
|
|
15
|
+
/** Discovery and fresh key availability are independent readiness observations. */
|
|
16
|
+
export async function identityOidcReadiness(verifier) {
|
|
17
|
+
const report = await verifier.readiness();
|
|
18
|
+
if (report.state === "ready")
|
|
19
|
+
return [];
|
|
20
|
+
return [
|
|
21
|
+
...(!report.discovery.valid ? ["oidcDiscovery"] : []),
|
|
22
|
+
...(!report.jwks.loaded ? ["oidcJwks"] : []),
|
|
23
|
+
].map(subject => ({ code: "OPENLUP_E_PORT_MISSING", package: "@openlup/platform", module: "identity", subject,
|
|
24
|
+
observation: report.code ?? "OIDC_JWKS_INVALID", fix: "Restore valid issuer discovery and fresh same-origin signing keys." }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export * from "./adminMagicLinkHandler.js";
|
|
2
|
+
export * from "./reconcileAccountForPrincipal.js";
|
|
3
|
+
export * from "./customerVerifyOtpHandler.js";
|
|
4
|
+
export * from "./authVerifyUrl.js";
|
|
5
|
+
export * from "./customerIdentityVerifier.js";
|
|
6
|
+
export * from "./operatorTokenAdminAuth.js";
|
|
7
|
+
export * from "./managedIdentityVerifier.js";
|
|
8
|
+
export * from "./managedAdminAuthVerifier.js";
|
|
9
|
+
export * from "./managedCustomerMe.js";
|
|
10
|
+
export * from "./sql.js";
|
|
11
|
+
export * from "./customerMeHandler.js";
|
|
12
|
+
export * from "./messages.js";
|
|
13
|
+
export * from "./managedAccountLinkStore.js";
|
|
14
|
+
export * from "./customerIdentityBinding.js";
|
|
15
|
+
export * from "./adminUserActionPorts.js";
|
|
16
|
+
export * from "./contribution.js";
|
|
17
|
+
export * from "./customerContracts.js";
|
|
18
|
+
export * from "./machineActorWriteFence.js";
|
|
19
|
+
export * from "./managedClient.js";
|
|
20
|
+
export * from "./customerAuthorization.js";
|
|
21
|
+
export * from "./customerSessionIssuer.js";
|
|
22
|
+
export * from "./managedClientAuthLink.js";
|
|
23
|
+
export * from "./customerAuth.js";
|
|
24
|
+
export * from "./reconcileAccountHandler.js";
|
|
25
|
+
export * from "./postgresCustomerMe.js";
|
|
26
|
+
export * from "./ports.js";
|
|
27
|
+
export * from "./postgresCustomerRecovery.js";
|
|
28
|
+
export * from "./customerMagicLinkHandler.js";
|
|
29
|
+
export * from "./customerSessionBinding.js";
|
|
30
|
+
export * from "./adminUserHandlers.js";
|
|
31
|
+
export * from "./adminPreflight.js";
|
|
32
|
+
export * from "./adminAuthBinding.js";
|
|
33
|
+
export * from "./customerPorts.js";
|
|
34
|
+
export * from "./magicLinkPorts.js";
|
|
35
|
+
export * from "./inviteAdminUserUseCase.js";
|
|
36
|
+
export * from "./adminAuthorization.js";
|
|
37
|
+
export * from "./removeAdminUserUseCase.js";
|
|
38
|
+
export * from "./adminMeHandler.js";
|
|
39
|
+
export * from "./managedAdminUserGateways.js";
|
|
40
|
+
export * from "./jwtLibrary.js";
|
|
41
|
+
export * from "./platformAdminAuth.js";
|
|
42
|
+
export * from "./adminContracts.js";
|
|
43
|
+
export * from "./contracts/emailTrust.js";
|
|
44
|
+
export { createVerifierResultOnlyEmailTrustPolicy } from "./emailTrustPolicy.js";
|
|
45
|
+
export type { EmailTrustAssessment, EmailTrustPolicy } from "./emailTrustPolicy.js";
|
|
46
|
+
export * from "./managedEmailTrustPolicy.js";
|
|
47
|
+
export * from "./identityReadiness.js";
|
|
48
|
+
export * from "./operatorInvitationPorts.js";
|
|
49
|
+
export * from "./operatorInvitationLifecycle.js";
|
|
50
|
+
export * from "./directOperatorInvitationStore.js";
|
|
51
|
+
export * from "./operatorPorts.js";
|
|
52
|
+
export * from "./operatorSession.js";
|
|
53
|
+
export * from "./operatorIdentityVerifier.js";
|
|
54
|
+
export * from "./operatorAdminAuth.js";
|
|
55
|
+
export * from "./operatorAuthHandlers.js";
|
|
56
|
+
export * from "./postgresOperatorIdentity.js";
|
|
57
|
+
export * from "./privacyPorts.js";
|
|
58
|
+
export * from "./personalDataRegistry.js";
|
|
59
|
+
export * from "./privacyUseCases.js";
|
|
60
|
+
export * from "./identityPersonalDataContributor.js";
|
|
61
|
+
export * from "./postgresPrivacy.js";
|
|
62
|
+
export * from "./managedPrivacy.js";
|
|
63
|
+
export * from "./privacyHandlers.js";
|
|
64
|
+
export * from "./oidcIdentityVerifier.js";
|
|
65
|
+
export * from "./oidcPrincipal.js";
|
|
66
|
+
export * from "./postgresAccountLinkStore.js";
|
|
67
|
+
export * from "./contracts/oidc.js";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export * from "./adminMagicLinkHandler.js";
|
|
2
|
+
export * from "./reconcileAccountForPrincipal.js";
|
|
3
|
+
export * from "./customerVerifyOtpHandler.js";
|
|
4
|
+
export * from "./authVerifyUrl.js";
|
|
5
|
+
export * from "./customerIdentityVerifier.js";
|
|
6
|
+
export * from "./operatorTokenAdminAuth.js";
|
|
7
|
+
export * from "./managedIdentityVerifier.js";
|
|
8
|
+
export * from "./managedAdminAuthVerifier.js";
|
|
9
|
+
export * from "./managedCustomerMe.js";
|
|
10
|
+
export * from "./sql.js";
|
|
11
|
+
export * from "./customerMeHandler.js";
|
|
12
|
+
export * from "./messages.js";
|
|
13
|
+
export * from "./managedAccountLinkStore.js";
|
|
14
|
+
export * from "./customerIdentityBinding.js";
|
|
15
|
+
export * from "./adminUserActionPorts.js";
|
|
16
|
+
export * from "./contribution.js";
|
|
17
|
+
export * from "./customerContracts.js";
|
|
18
|
+
export * from "./machineActorWriteFence.js";
|
|
19
|
+
export * from "./managedClient.js";
|
|
20
|
+
export * from "./customerAuthorization.js";
|
|
21
|
+
export * from "./customerSessionIssuer.js";
|
|
22
|
+
export * from "./managedClientAuthLink.js";
|
|
23
|
+
export * from "./customerAuth.js";
|
|
24
|
+
export * from "./reconcileAccountHandler.js";
|
|
25
|
+
export * from "./postgresCustomerMe.js";
|
|
26
|
+
export * from "./ports.js";
|
|
27
|
+
export * from "./postgresCustomerRecovery.js";
|
|
28
|
+
export * from "./customerMagicLinkHandler.js";
|
|
29
|
+
export * from "./customerSessionBinding.js";
|
|
30
|
+
export * from "./adminUserHandlers.js";
|
|
31
|
+
export * from "./adminPreflight.js";
|
|
32
|
+
export * from "./adminAuthBinding.js";
|
|
33
|
+
export * from "./customerPorts.js";
|
|
34
|
+
export * from "./magicLinkPorts.js";
|
|
35
|
+
export * from "./inviteAdminUserUseCase.js";
|
|
36
|
+
export * from "./adminAuthorization.js";
|
|
37
|
+
export * from "./removeAdminUserUseCase.js";
|
|
38
|
+
export * from "./adminMeHandler.js";
|
|
39
|
+
export * from "./managedAdminUserGateways.js";
|
|
40
|
+
export * from "./jwtLibrary.js";
|
|
41
|
+
export * from "./platformAdminAuth.js";
|
|
42
|
+
export * from "./adminContracts.js";
|
|
43
|
+
export * from "./contracts/emailTrust.js";
|
|
44
|
+
export { createVerifierResultOnlyEmailTrustPolicy } from "./emailTrustPolicy.js";
|
|
45
|
+
export * from "./managedEmailTrustPolicy.js";
|
|
46
|
+
export * from "./identityReadiness.js";
|
|
47
|
+
export * from "./operatorInvitationPorts.js";
|
|
48
|
+
export * from "./operatorInvitationLifecycle.js";
|
|
49
|
+
export * from "./directOperatorInvitationStore.js";
|
|
50
|
+
export * from "./operatorPorts.js";
|
|
51
|
+
export * from "./operatorSession.js";
|
|
52
|
+
export * from "./operatorIdentityVerifier.js";
|
|
53
|
+
export * from "./operatorAdminAuth.js";
|
|
54
|
+
export * from "./operatorAuthHandlers.js";
|
|
55
|
+
export * from "./postgresOperatorIdentity.js";
|
|
56
|
+
export * from "./privacyPorts.js";
|
|
57
|
+
export * from "./personalDataRegistry.js";
|
|
58
|
+
export * from "./privacyUseCases.js";
|
|
59
|
+
export * from "./identityPersonalDataContributor.js";
|
|
60
|
+
export * from "./postgresPrivacy.js";
|
|
61
|
+
export * from "./managedPrivacy.js";
|
|
62
|
+
export * from "./privacyHandlers.js";
|
|
63
|
+
export * from "./oidcIdentityVerifier.js";
|
|
64
|
+
export * from "./oidcPrincipal.js";
|
|
65
|
+
export * from "./postgresAccountLinkStore.js";
|
|
66
|
+
export * from "./contracts/oidc.js";
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ManagedOperatorMembershipResult, OperatorInvitationConflict } from "./contracts/operatorInvitations.js";
|
|
2
|
+
import type { OperatorInvitationStore } from "./operatorInvitationPorts.js";
|
|
3
|
+
import { type IdentityMessages } from "./messages.js";
|
|
4
|
+
export type AdminUserInviteRole = "admin" | "distributor";
|
|
5
|
+
export declare const ADMIN_ROLE_NOTIFICATION_LOCALE: "en";
|
|
6
|
+
export type AdminRoleNotificationSkipReason = "admin_disabled" | "egress_suppressed";
|
|
7
|
+
export type InviteAdminUserResponse = {
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
success?: boolean;
|
|
10
|
+
message?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
code?: string;
|
|
13
|
+
deliveryReference?: string | null;
|
|
14
|
+
deliveryReferencePresent?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export interface InviteAdminUserGateway {
|
|
17
|
+
getUser: (accessToken: string) => Promise<{
|
|
18
|
+
user: {
|
|
19
|
+
id: string;
|
|
20
|
+
} | null;
|
|
21
|
+
error: unknown;
|
|
22
|
+
}>;
|
|
23
|
+
findAdminUser: (userId: string) => Promise<{
|
|
24
|
+
id: string;
|
|
25
|
+
role: string;
|
|
26
|
+
membership_state: string;
|
|
27
|
+
is_machine_actor: boolean;
|
|
28
|
+
} | null>;
|
|
29
|
+
findAdminUserByEmail: (email: string) => Promise<{
|
|
30
|
+
id: string;
|
|
31
|
+
} | null>;
|
|
32
|
+
findAuthUserByEmail: (email: string) => Promise<{
|
|
33
|
+
id: string;
|
|
34
|
+
email?: string | null;
|
|
35
|
+
} | null>;
|
|
36
|
+
inviteAuthUser: (input: {
|
|
37
|
+
email: string;
|
|
38
|
+
redirectTo: string;
|
|
39
|
+
role: AdminUserInviteRole;
|
|
40
|
+
}) => Promise<{
|
|
41
|
+
user: {
|
|
42
|
+
id: string;
|
|
43
|
+
} | null;
|
|
44
|
+
error: {
|
|
45
|
+
message: string;
|
|
46
|
+
} | null;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export interface AdminRoleNotificationPort {
|
|
50
|
+
sendRoleGranted: (input: {
|
|
51
|
+
email: string;
|
|
52
|
+
authUserId: string;
|
|
53
|
+
role: AdminUserInviteRole;
|
|
54
|
+
locale: "pl" | "en";
|
|
55
|
+
invitationId?: string;
|
|
56
|
+
expiresAt?: string;
|
|
57
|
+
}) => Promise<{
|
|
58
|
+
ok: boolean;
|
|
59
|
+
providerMessageId: string | null;
|
|
60
|
+
providerError: string | null;
|
|
61
|
+
skipped: boolean;
|
|
62
|
+
skipReason: AdminRoleNotificationSkipReason | null;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
export declare function runInviteAdminUserUseCase(request: {
|
|
66
|
+
accessToken: string | null;
|
|
67
|
+
email: string;
|
|
68
|
+
role: string;
|
|
69
|
+
}, deps: {
|
|
70
|
+
gateway: InviteAdminUserGateway;
|
|
71
|
+
roleNotificationPort: AdminRoleNotificationPort;
|
|
72
|
+
siteUrl: string;
|
|
73
|
+
messages?: IdentityMessages;
|
|
74
|
+
notificationLocale?: "pl" | "en";
|
|
75
|
+
} & ({
|
|
76
|
+
invitationStore: OperatorInvitationStore;
|
|
77
|
+
ttlSeconds: number;
|
|
78
|
+
} | {
|
|
79
|
+
activateMembership: (input: {
|
|
80
|
+
principalId: string;
|
|
81
|
+
email: string;
|
|
82
|
+
role: AdminUserInviteRole;
|
|
83
|
+
}) => Promise<ManagedOperatorMembershipResult | {
|
|
84
|
+
code: OperatorInvitationConflict;
|
|
85
|
+
}>;
|
|
86
|
+
})): Promise<{
|
|
87
|
+
status: number;
|
|
88
|
+
body: InviteAdminUserResponse;
|
|
89
|
+
}>;
|