@stigmer/server 3.14.0 → 3.15.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/boot/compose.d.ts +8 -1
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +98 -12
- package/dist/boot/compose.js.map +1 -1
- package/dist/domain/agentexecution/constants.d.ts +16 -0
- package/dist/domain/agentexecution/constants.d.ts.map +1 -1
- package/dist/domain/agentexecution/constants.js +22 -0
- package/dist/domain/agentexecution/constants.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +10 -3
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentexecution/run-target.d.ts +26 -0
- package/dist/domain/agentexecution/run-target.d.ts.map +1 -0
- package/dist/domain/agentexecution/run-target.js +31 -0
- package/dist/domain/agentexecution/run-target.js.map +1 -0
- package/dist/domain/apikey/verifier.d.ts +8 -1
- package/dist/domain/apikey/verifier.d.ts.map +1 -1
- package/dist/domain/apikey/verifier.js +14 -2
- package/dist/domain/apikey/verifier.js.map +1 -1
- package/dist/domain/identityaccount/constants.d.ts +66 -0
- package/dist/domain/identityaccount/constants.d.ts.map +1 -0
- package/dist/domain/identityaccount/constants.js +153 -0
- package/dist/domain/identityaccount/constants.js.map +1 -0
- package/dist/domain/identityaccount/controller.d.ts +34 -0
- package/dist/domain/identityaccount/controller.d.ts.map +1 -0
- package/dist/domain/identityaccount/controller.js +342 -0
- package/dist/domain/identityaccount/controller.js.map +1 -0
- package/dist/domain/identityaccount/operator.d.ts +14 -0
- package/dist/domain/identityaccount/operator.d.ts.map +1 -0
- package/dist/domain/identityaccount/operator.js +67 -0
- package/dist/domain/identityaccount/operator.js.map +1 -0
- package/dist/domain/identityaccount/provisioning.d.ts +52 -0
- package/dist/domain/identityaccount/provisioning.d.ts.map +1 -0
- package/dist/domain/identityaccount/provisioning.js +114 -0
- package/dist/domain/identityaccount/provisioning.js.map +1 -0
- package/dist/domain/identityaccount/resolve.d.ts +36 -0
- package/dist/domain/identityaccount/resolve.d.ts.map +1 -0
- package/dist/domain/identityaccount/resolve.js +12 -0
- package/dist/domain/identityaccount/resolve.js.map +1 -0
- package/dist/domain/identityaccount/resource-store.d.ts +4 -0
- package/dist/domain/identityaccount/resource-store.d.ts.map +1 -0
- package/dist/domain/identityaccount/resource-store.js +131 -0
- package/dist/domain/identityaccount/resource-store.js.map +1 -0
- package/dist/domain/identityaccount/steps.d.ts +79 -0
- package/dist/domain/identityaccount/steps.d.ts.map +1 -0
- package/dist/domain/identityaccount/steps.js +291 -0
- package/dist/domain/identityaccount/steps.js.map +1 -0
- package/dist/domain/identityaccount/store-contract.d.ts +22 -0
- package/dist/domain/identityaccount/store-contract.d.ts.map +1 -0
- package/dist/domain/identityaccount/store-contract.js +307 -0
- package/dist/domain/identityaccount/store-contract.js.map +1 -0
- package/dist/domain/identityaccount/store.d.ts +66 -0
- package/dist/domain/identityaccount/store.d.ts.map +1 -0
- package/dist/domain/identityaccount/store.js +13 -0
- package/dist/domain/identityaccount/store.js.map +1 -0
- package/dist/domain/session/constants.d.ts +15 -0
- package/dist/domain/session/constants.d.ts.map +1 -0
- package/dist/domain/session/constants.js +17 -0
- package/dist/domain/session/constants.js.map +1 -0
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +10 -0
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/session/run-target.d.ts +15 -0
- package/dist/domain/session/run-target.d.ts.map +1 -0
- package/dist/domain/session/run-target.js +14 -0
- package/dist/domain/session/run-target.js.map +1 -0
- package/dist/domain/workflowexecution/constants.d.ts +9 -0
- package/dist/domain/workflowexecution/constants.d.ts.map +1 -1
- package/dist/domain/workflowexecution/constants.js +13 -0
- package/dist/domain/workflowexecution/constants.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +8 -0
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowexecution/run-target.d.ts +18 -0
- package/dist/domain/workflowexecution/run-target.d.ts.map +1 -0
- package/dist/domain/workflowexecution/run-target.js +23 -0
- package/dist/domain/workflowexecution/run-target.js.map +1 -0
- package/dist/extensions/drivers.d.ts +26 -0
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/gate-slots.d.ts +16 -1
- package/dist/extensions/gate-slots.d.ts.map +1 -1
- package/dist/extensions/gate-slots.js +1 -0
- package/dist/extensions/gate-slots.js.map +1 -1
- package/dist/extensions/identity-federation.d.ts +36 -0
- package/dist/extensions/identity-federation.d.ts.map +1 -0
- package/dist/extensions/identity-federation.js +2 -0
- package/dist/extensions/identity-federation.js.map +1 -0
- package/dist/extensions/registry.d.ts +18 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +20 -0
- package/dist/extensions/registry.js.map +1 -1
- package/dist/identity/oidc-discovery.d.ts +36 -0
- package/dist/identity/oidc-discovery.d.ts.map +1 -0
- package/dist/identity/oidc-discovery.js +60 -0
- package/dist/identity/oidc-discovery.js.map +1 -0
- package/dist/identity/oidc-userinfo.d.ts +30 -0
- package/dist/identity/oidc-userinfo.d.ts.map +1 -0
- package/dist/identity/oidc-userinfo.js +28 -0
- package/dist/identity/oidc-userinfo.js.map +1 -0
- package/dist/identity/oidc-verifier.d.ts +6 -0
- package/dist/identity/oidc-verifier.d.ts.map +1 -1
- package/dist/identity/oidc-verifier.js +35 -34
- package/dist/identity/oidc-verifier.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/interceptors/auth.d.ts +10 -0
- package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
- package/dist/pipeline/interceptors/auth.js +9 -1
- package/dist/pipeline/interceptors/auth.js.map +1 -1
- package/dist/pipeline/steps/authorize-run-target.d.ts +105 -0
- package/dist/pipeline/steps/authorize-run-target.d.ts.map +1 -0
- package/dist/pipeline/steps/authorize-run-target.js +61 -0
- package/dist/pipeline/steps/authorize-run-target.js.map +1 -0
- package/dist/query/search/criteria.d.ts +10 -3
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +11 -4
- package/dist/query/search/criteria.js.map +1 -1
- package/package.json +4 -4
- package/src/boot/__tests__/auth-enabled-composition.test.ts +226 -2
- package/src/boot/compose.ts +118 -13
- package/src/domain/agentexecution/__tests__/run-gate.test.ts +245 -0
- package/src/domain/agentexecution/__tests__/run-target.test.ts +117 -0
- package/src/domain/agentexecution/constants.ts +25 -0
- package/src/domain/agentexecution/controller.ts +10 -3
- package/src/domain/agentexecution/run-target.ts +64 -0
- package/src/domain/apikey/__tests__/verifier.test.ts +146 -22
- package/src/domain/apikey/verifier.ts +24 -2
- package/src/domain/identityaccount/__tests__/constants.test.ts +155 -0
- package/src/domain/identityaccount/__tests__/identityaccount.test.ts +484 -0
- package/src/domain/identityaccount/__tests__/operator.test.ts +180 -0
- package/src/domain/identityaccount/__tests__/provisioning.test.ts +281 -0
- package/src/domain/identityaccount/__tests__/resolve.test.ts +114 -0
- package/src/domain/identityaccount/__tests__/resource-store.test.ts +216 -0
- package/src/domain/identityaccount/__tests__/support.ts +58 -0
- package/src/domain/identityaccount/constants.ts +178 -0
- package/src/domain/identityaccount/controller.ts +667 -0
- package/src/domain/identityaccount/operator.ts +94 -0
- package/src/domain/identityaccount/provisioning.ts +182 -0
- package/src/domain/identityaccount/resolve.ts +52 -0
- package/src/domain/identityaccount/resource-store.ts +157 -0
- package/src/domain/identityaccount/steps.ts +377 -0
- package/src/domain/identityaccount/store-contract.ts +471 -0
- package/src/domain/identityaccount/store.ts +70 -0
- package/src/domain/session/__tests__/run-gate.test.ts +219 -0
- package/src/domain/session/__tests__/run-target.test.ts +43 -0
- package/src/domain/session/constants.ts +17 -0
- package/src/domain/session/controller.ts +10 -0
- package/src/domain/session/run-target.ts +30 -0
- package/src/domain/workflowexecution/__tests__/run-gate.test.ts +200 -0
- package/src/domain/workflowexecution/__tests__/run-target.test.ts +80 -0
- package/src/domain/workflowexecution/constants.ts +17 -0
- package/src/domain/workflowexecution/controller.ts +10 -0
- package/src/domain/workflowexecution/run-target.ts +47 -0
- package/src/extensions/__tests__/extension-composition.test.ts +49 -0
- package/src/extensions/__tests__/identity-account-composed.test.ts +803 -0
- package/src/extensions/__tests__/identity-account-points.test.ts +129 -0
- package/src/extensions/__tests__/registry.test.ts +4 -0
- package/src/extensions/__tests__/tier-truthfulness.test.ts +299 -0
- package/src/extensions/drivers.ts +26 -0
- package/src/extensions/gate-slots.ts +16 -0
- package/src/extensions/identity-federation.ts +58 -0
- package/src/extensions/registry.ts +44 -1
- package/src/identity/__tests__/oidc-discovery.test.ts +122 -0
- package/src/identity/__tests__/oidc-userinfo.test.ts +103 -0
- package/src/identity/__tests__/oidc-verifier.test.ts +173 -3
- package/src/identity/oidc-discovery.ts +84 -0
- package/src/identity/oidc-userinfo.ts +64 -0
- package/src/identity/oidc-verifier.ts +42 -45
- package/src/index.ts +26 -0
- package/src/pipeline/__tests__/support.ts +34 -0
- package/src/pipeline/interceptors/auth.ts +13 -1
- package/src/pipeline/steps/__tests__/authorize-annotation-completeness.test.ts +184 -0
- package/src/pipeline/steps/__tests__/authorize-run-target.test.ts +261 -0
- package/src/pipeline/steps/authorize-run-target.ts +148 -0
- package/src/query/search/__tests__/criteria.test.ts +18 -1
- package/src/query/search/criteria.ts +11 -4
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subject → identity resolution (20260911.11; T01_0_plan.md Q-IA-2,
|
|
3
|
+
* T01_1_review.md A1 and A6): the ONE statement of "what identityId does
|
|
4
|
+
* this subject get stamped with", called by every verifier after its own
|
|
5
|
+
* credential checks pass — the OIDC lane with the token's `sub`, the
|
|
6
|
+
* API-key lane with the key's creator stamp.
|
|
7
|
+
*
|
|
8
|
+
* The rule is the cloud's direct-login posture (iam/direct/verifier.ts):
|
|
9
|
+
* one primary-key read of the DIRECT account for the subject; a hit
|
|
10
|
+
* stamps the account id, so the Authorizer, the audit actor and the
|
|
11
|
+
* tuple lifecycle see one principal shape for every provisioned user; a
|
|
12
|
+
* miss returns the subject unchanged, so an unprovisioned caller is
|
|
13
|
+
* admitted idp-shaped and exactly two RPCs mean anything to them —
|
|
14
|
+
* whoAmI (NOT_FOUND) and provisionMyAccount. No cache between the two
|
|
15
|
+
* (Q5's liveness posture): a row that appears is seen by the very next
|
|
16
|
+
* request, a row that is deleted stops resolving on the next.
|
|
17
|
+
*
|
|
18
|
+
* It lives in the domain, not in either verifier, because it is the
|
|
19
|
+
* domain's knowledge that a subject and an account are two names for one
|
|
20
|
+
* principal; a verifier only knows which string it was handed. Stated
|
|
21
|
+
* once, both lanes cannot drift, and a stamp that is already an account
|
|
22
|
+
* id needs no special case: no account carries an `ida_` as its subject,
|
|
23
|
+
* so the read is a miss and the stamp is kept.
|
|
24
|
+
*
|
|
25
|
+
* Faults: a store failure propagates as the error it is — the chassis
|
|
26
|
+
* maps a non-ConnectError to INTERNAL (pipeline/interceptors/auth.ts),
|
|
27
|
+
* so an outage never reads as a bad credential. A hit whose row carries
|
|
28
|
+
* no id is the one deliberate divergence from the cloud's inline
|
|
29
|
+
* `metadata?.id ?? ""`: a `""` principal is the silent-junk failure the
|
|
30
|
+
* oss#405 doctrine forbids, so it is a loud fault naming the subject.
|
|
31
|
+
*/
|
|
32
|
+
import type { IdentityAccountStore } from "./store.js";
|
|
33
|
+
/** The one read a verifier needs from the domain: the direct account for a subject. */
|
|
34
|
+
export type AccountsBySubject = Pick<IdentityAccountStore, "findDirectByIdpId">;
|
|
35
|
+
export declare function identityIdForSubject(accounts: AccountsBySubject, subject: string): Promise<string>;
|
|
36
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/domain/identityaccount/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,uFAAuF;AACvF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,CAAC;AAEhF,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAYjB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export async function identityIdForSubject(accounts, subject) {
|
|
2
|
+
const account = await accounts.findDirectByIdpId(subject);
|
|
3
|
+
if (account === undefined) {
|
|
4
|
+
return subject;
|
|
5
|
+
}
|
|
6
|
+
const id = account.metadata?.id ?? "";
|
|
7
|
+
if (id === "") {
|
|
8
|
+
throw new Error(`identity account for subject '${subject}' carries no id — refusing to stamp an empty principal`);
|
|
9
|
+
}
|
|
10
|
+
return id;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/domain/identityaccount/resolve.ts"],"names":[],"mappings":"AAoCA,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAA2B,EAC3B,OAAe;IAEf,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC;IACtC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,iCAAiC,OAAO,wDAAwD,CACjG,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-store.d.ts","sourceRoot":"","sources":["../../../src/domain/identityaccount/resource-store.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,KAAK,GACX,oBAAoB,CAyFtB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The open-source IdentityAccountStore: the port (store.ts) over the
|
|
3
|
+
* generic Store, rows of the `resources` table by kind (20260911.11,
|
|
4
|
+
* T01_1_review.md A1). The composition root installs it when no
|
|
5
|
+
* extension registers `drivers.identityAccountStore`.
|
|
6
|
+
*
|
|
7
|
+
* Every subject lookup is a PRIMARY-KEY read of the derived id
|
|
8
|
+
* (constants.ts accountIdFor): open source has no legacy random ids, so
|
|
9
|
+
* "the account with subject S" IS "the row with id accountIdFor(S)". That
|
|
10
|
+
* is what makes the OIDC lane's per-request resolve, whoAmI and the
|
|
11
|
+
* provisioning idempotency check O(1) reads with no secondary index and
|
|
12
|
+
* no scan, and it is why `save` refuses a direct account whose id is not
|
|
13
|
+
* its derived id — a stray row would be unreachable by subject forever.
|
|
14
|
+
* `findDirectByEmail` is the one lookup that scans (Store.findByField, a
|
|
15
|
+
* decode-and-scan of the kind's rows, T01_1_review.md finding 2): an
|
|
16
|
+
* administrative RPC, never a per-request path.
|
|
17
|
+
*
|
|
18
|
+
* `save` and `update` are both read-then-write because the generic
|
|
19
|
+
* Store's saveResource is an upsert with neither a create-only nor an
|
|
20
|
+
* update-only form. `save` refuses when the read finds a row (the port:
|
|
21
|
+
* a held id is DuplicateAccountError, never a silent overwrite); `update`
|
|
22
|
+
* returns when the read finds none (the port, A12: replace, never
|
|
23
|
+
* create — a driver whose UPDATE matches no row writes nothing, and this
|
|
24
|
+
* adapter must not differ). The residual window between the read and
|
|
25
|
+
* the write admits a same-content overwrite of the first writer's audit
|
|
26
|
+
* stamp, or a row recreated under a concurrent delete, by milliseconds —
|
|
27
|
+
* the posture every chain on the platform has today (CheckDuplicate then
|
|
28
|
+
* upsert); conditional store writes are a platform-wide follow-up, not
|
|
29
|
+
* this adapter's to invent. The primary key still guarantees ONE row per
|
|
30
|
+
* subject, which is the invariant that matters.
|
|
31
|
+
*
|
|
32
|
+
* Store faults follow the ratified mapping (domain/apikey/lookup.ts): a
|
|
33
|
+
* typed ResourceNotFoundError reads as `undefined`; anything else
|
|
34
|
+
* propagates — an outage must never read as "no account".
|
|
35
|
+
*
|
|
36
|
+
* The port's contract is proven by store-contract.ts, run over this
|
|
37
|
+
* adapter on both drivers in __tests__/resource-store.test.ts, which
|
|
38
|
+
* also pins the two invariants above that are this adapter's own.
|
|
39
|
+
*/
|
|
40
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
41
|
+
import { IdentityAccountSchema } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/api_pb";
|
|
42
|
+
import { IdentityAccountProvisioningMode } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/enum_pb";
|
|
43
|
+
import { ResourceNotFoundError } from "../../store/interface.js";
|
|
44
|
+
import { accountIdFor } from "./constants.js";
|
|
45
|
+
import { DuplicateAccountError } from "./store.js";
|
|
46
|
+
const KIND = ApiResourceKind.identity_account;
|
|
47
|
+
export function newResourceIdentityAccountStore(store) {
|
|
48
|
+
async function readById(id) {
|
|
49
|
+
try {
|
|
50
|
+
return await store.getResource(KIND, id, IdentityAccountSchema);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof ResourceNotFoundError) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** The row for a subject — undefined for the empty subject, which no account carries. */
|
|
60
|
+
async function readBySubject(idpId) {
|
|
61
|
+
if (idpId === "") {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return readById(accountIdFor(idpId));
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
async save(account) {
|
|
68
|
+
const id = account.metadata?.id ?? "";
|
|
69
|
+
const idpId = account.spec?.idpId ?? "";
|
|
70
|
+
if (isDirect(account) && id !== accountIdFor(idpId)) {
|
|
71
|
+
throw new Error("direct account id must be derived from its idp_id");
|
|
72
|
+
}
|
|
73
|
+
if ((await readById(id)) !== undefined) {
|
|
74
|
+
throw new DuplicateAccountError(`identity account '${id}' already exists`);
|
|
75
|
+
}
|
|
76
|
+
await store.saveResource(KIND, id, IdentityAccountSchema, account);
|
|
77
|
+
},
|
|
78
|
+
async update(account) {
|
|
79
|
+
const id = account.metadata?.id ?? "";
|
|
80
|
+
if ((await readById(id)) === undefined) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
await store.saveResource(KIND, id, IdentityAccountSchema, account);
|
|
84
|
+
},
|
|
85
|
+
async deleteById(id) {
|
|
86
|
+
await store.deleteResource(KIND, id);
|
|
87
|
+
},
|
|
88
|
+
findById: readById,
|
|
89
|
+
findByIdpId: readBySubject,
|
|
90
|
+
async findDirectByIdpId(idpId) {
|
|
91
|
+
const account = await readBySubject(idpId);
|
|
92
|
+
return account !== undefined && isDirect(account) ? account : undefined;
|
|
93
|
+
},
|
|
94
|
+
async findDirectByEmail(email) {
|
|
95
|
+
let account;
|
|
96
|
+
try {
|
|
97
|
+
account = await store.findByField(KIND, "spec.email", email, IdentityAccountSchema);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error instanceof ResourceNotFoundError) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
return isDirect(account) ? account : undefined;
|
|
106
|
+
},
|
|
107
|
+
async findByIds(ids) {
|
|
108
|
+
const found = [];
|
|
109
|
+
// One row per distinct id, at its first position (the port, A12):
|
|
110
|
+
// a Set iterates in insertion order, so the dedupe keeps the order.
|
|
111
|
+
for (const id of new Set(ids)) {
|
|
112
|
+
const account = await readById(id);
|
|
113
|
+
if (account !== undefined) {
|
|
114
|
+
found.push(account);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return found;
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Non-federated: the platform's own subject. A federated account carries
|
|
123
|
+
* its identity_provider_ref; open source never writes one (its create
|
|
124
|
+
* path assigns `direct`), so this is the port's contract stated, not a
|
|
125
|
+
* branch the OSS store expects to take.
|
|
126
|
+
*/
|
|
127
|
+
function isDirect(account) {
|
|
128
|
+
return (account.spec?.identityProviderRef === undefined &&
|
|
129
|
+
account.spec?.provisioningMode !== IdentityAccountProvisioningMode.federated);
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=resource-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-store.js","sourceRoot":"","sources":["../../../src/domain/identityaccount/resource-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity-account domain steps (20260911.11; T01_1_review.md A1, A9).
|
|
3
|
+
*
|
|
4
|
+
* This is the first OSS domain whose persistence is a PORT
|
|
5
|
+
* (IdentityAccountStore) rather than the generic Store, so the cloud can
|
|
6
|
+
* serve the same controller over its own table. The shared steps that
|
|
7
|
+
* touch storage (CheckDuplicate, LoadExisting, LoadTarget, Persist, the
|
|
8
|
+
* delete pair) are typed over `Store`; this module brings the domain's
|
|
9
|
+
* own under the SAME names, so a chain reads onto the inventory unchanged
|
|
10
|
+
* (the organization domain's "own step, same name" precedent), and reuses
|
|
11
|
+
* every storage-free shared step as it is. The shared context keys are
|
|
12
|
+
* reused too, so BuildUpdateState finds EXISTING_RESOURCE_KEY exactly
|
|
13
|
+
* where it expects it.
|
|
14
|
+
*
|
|
15
|
+
* The domain's own rules:
|
|
16
|
+
* - DefaultAccountName: `metadata.name` falls back to the email, then
|
|
17
|
+
* the subject. ResolveSlug refuses an empty name and the field is not
|
|
18
|
+
* proto-required, and a machine subject whose IdP releases no email
|
|
19
|
+
* must still provision.
|
|
20
|
+
* - DeriveAccountId (A1): `metadata.id = accountIdFor(spec.idp_id)`,
|
|
21
|
+
* replacing whatever the caller sent (the organization's CopySlugToId
|
|
22
|
+
* precedent, `metadata.proto`'s documented exception). Runs BEFORE
|
|
23
|
+
* BuildNewState, which mints only when the id is empty.
|
|
24
|
+
* - CheckDuplicate: by SUBJECT, a primary-key read of the derived id —
|
|
25
|
+
* two accounts may share an email; the account's uniqueness is its
|
|
26
|
+
* subject, never its slug (the guideline's "domains with different
|
|
27
|
+
* uniqueness rules bring their own step under the same name").
|
|
28
|
+
* - AssignBackendFields: `is_machine_account` from the `@clients`
|
|
29
|
+
* suffix and `provisioning_mode direct` on create (the spec's
|
|
30
|
+
* "assigned by backend" fields); on update, every backend-assigned
|
|
31
|
+
* field and the email are preserved from the existing row (A9: the
|
|
32
|
+
* cloud's writable surface — first/last name, picture, preferences —
|
|
33
|
+
* and nothing else; the `preserveImmutableFields` shape).
|
|
34
|
+
* - GuardImmutableSubject (A1): a changed `spec.idp_id` is refused
|
|
35
|
+
* FAILED_PRECONDITION (the schedule domain's shape), never silently
|
|
36
|
+
* preserved — the subject IS the identity, and a client that sends a
|
|
37
|
+
* different one has a bug it should hear about.
|
|
38
|
+
*
|
|
39
|
+
* NOT_FOUND copy is the domain's (`Identity account not found: <id>`, the
|
|
40
|
+
* cloud's byte-pinned sentence); the shared `notFoundError` would render
|
|
41
|
+
* the kind_meta name `IdentityAccount`.
|
|
42
|
+
*/
|
|
43
|
+
import { ConnectError } from "@connectrpc/connect";
|
|
44
|
+
import type { DescMessage } from "@bufbuild/protobuf";
|
|
45
|
+
import { IdentityAccountSchema } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/api_pb";
|
|
46
|
+
import type { IdentityAccountSpec } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/spec_pb";
|
|
47
|
+
import type { PipelineStep } from "../../pipeline/pipeline.js";
|
|
48
|
+
import type { IdentityAccountStore } from "./store.js";
|
|
49
|
+
type AccountStep = PipelineStep<typeof IdentityAccountSchema>;
|
|
50
|
+
/** `Identity account not found: <handle>` as a NOT_FOUND ConnectError. */
|
|
51
|
+
export declare function accountNotFoundError(handle: string): ConnectError;
|
|
52
|
+
export declare function newDefaultAccountNameStep(): AccountStep;
|
|
53
|
+
export declare function newDeriveAccountIdStep(): AccountStep;
|
|
54
|
+
/** Rejects a create whose SUBJECT already has an account (a primary-key read). */
|
|
55
|
+
export declare function newCheckDuplicateStep(accounts: IdentityAccountStore): AccountStep;
|
|
56
|
+
/**
|
|
57
|
+
* The backend-assigned fields of a DIRECT account, applied in place: the
|
|
58
|
+
* machine flag from the subject's shape, `provisioning_mode direct`, no
|
|
59
|
+
* provider ref (a direct account is the platform's own subject). The ONE
|
|
60
|
+
* writer of these fields; the provisioner builds its spec through it too.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assignDirectBackendFields(spec: IdentityAccountSpec): IdentityAccountSpec;
|
|
63
|
+
export declare function newAssignBackendFieldsStep(): AccountStep;
|
|
64
|
+
/** Saves the new account through the port; a held subject is ALREADY_EXISTS. */
|
|
65
|
+
export declare function newPersistNewAccountStep(accounts: IdentityAccountStore): AccountStep;
|
|
66
|
+
/** Loads the existing account by id into EXISTING_RESOURCE_KEY; the domain's NOT_FOUND copy. */
|
|
67
|
+
export declare function newLoadExistingAccountStep(accounts: IdentityAccountStore): AccountStep;
|
|
68
|
+
export declare function newGuardImmutableSubjectStep(): AccountStep;
|
|
69
|
+
/** Preserves the backend-assigned fields and the email from the existing row (A9). */
|
|
70
|
+
export declare function newPreserveBackendFieldsStep(): AccountStep;
|
|
71
|
+
/** Replaces the row through the port. */
|
|
72
|
+
export declare function newPersistUpdatedAccountStep(accounts: IdentityAccountStore): AccountStep;
|
|
73
|
+
/** Loads the account named by an ID-wrapper input into TARGET_RESOURCE_KEY. */
|
|
74
|
+
export declare function newLoadTargetAccountStep<InputDesc extends DescMessage>(accounts: IdentityAccountStore): PipelineStep<InputDesc>;
|
|
75
|
+
/** Loads the doomed account (after ExtractResourceId) so delete can return it. */
|
|
76
|
+
export declare function newLoadExistingAccountForDeleteStep<InputDesc extends DescMessage>(accounts: IdentityAccountStore): PipelineStep<InputDesc>;
|
|
77
|
+
export declare function newDeleteAccountStep<InputDesc extends DescMessage>(accounts: IdentityAccountStore): PipelineStep<InputDesc>;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=steps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../../src/domain/identityaccount/steps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAAQ,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0DAA0D,CAAC;AAEjG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2DAA2D,CAAC;AASrG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAa/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,KAAK,WAAW,GAAG,YAAY,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAM9D,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAEjE;AAMD,wBAAgB,yBAAyB,IAAI,WAAW,CAWvD;AAED,wBAAgB,sBAAsB,IAAI,WAAW,CAiBpD;AAED,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,oBAAoB,GAC7B,WAAW,CAoBb;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,mBAAmB,GACxB,mBAAmB,CAKrB;AAED,wBAAgB,0BAA0B,IAAI,WAAW,CAOxD;AAED,gFAAgF;AAChF,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,oBAAoB,GAC7B,WAAW,CAiBb;AAMD,gGAAgG;AAChG,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,oBAAoB,GAC7B,WAAW,CAeb;AAED,wBAAgB,4BAA4B,IAAI,WAAW,CAW1D;AAED,sFAAsF;AACtF,wBAAgB,4BAA4B,IAAI,WAAW,CAe1D;AAED,yCAAyC;AACzC,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,oBAAoB,GAC7B,WAAW,CAWb;AAMD,+EAA+E;AAC/E,wBAAgB,wBAAwB,CAAC,SAAS,SAAS,WAAW,EACpE,QAAQ,EAAE,oBAAoB,GAC7B,YAAY,CAAC,SAAS,CAAC,CAWzB;AAED,kFAAkF;AAClF,wBAAgB,mCAAmC,CACjD,SAAS,SAAS,WAAW,EAC7B,QAAQ,EAAE,oBAAoB,GAAG,YAAY,CAAC,SAAS,CAAC,CAUzD;AAED,wBAAgB,oBAAoB,CAAC,SAAS,SAAS,WAAW,EAChE,QAAQ,EAAE,oBAAoB,GAC7B,YAAY,CAAC,SAAS,CAAC,CAWzB"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity-account domain steps (20260911.11; T01_1_review.md A1, A9).
|
|
3
|
+
*
|
|
4
|
+
* This is the first OSS domain whose persistence is a PORT
|
|
5
|
+
* (IdentityAccountStore) rather than the generic Store, so the cloud can
|
|
6
|
+
* serve the same controller over its own table. The shared steps that
|
|
7
|
+
* touch storage (CheckDuplicate, LoadExisting, LoadTarget, Persist, the
|
|
8
|
+
* delete pair) are typed over `Store`; this module brings the domain's
|
|
9
|
+
* own under the SAME names, so a chain reads onto the inventory unchanged
|
|
10
|
+
* (the organization domain's "own step, same name" precedent), and reuses
|
|
11
|
+
* every storage-free shared step as it is. The shared context keys are
|
|
12
|
+
* reused too, so BuildUpdateState finds EXISTING_RESOURCE_KEY exactly
|
|
13
|
+
* where it expects it.
|
|
14
|
+
*
|
|
15
|
+
* The domain's own rules:
|
|
16
|
+
* - DefaultAccountName: `metadata.name` falls back to the email, then
|
|
17
|
+
* the subject. ResolveSlug refuses an empty name and the field is not
|
|
18
|
+
* proto-required, and a machine subject whose IdP releases no email
|
|
19
|
+
* must still provision.
|
|
20
|
+
* - DeriveAccountId (A1): `metadata.id = accountIdFor(spec.idp_id)`,
|
|
21
|
+
* replacing whatever the caller sent (the organization's CopySlugToId
|
|
22
|
+
* precedent, `metadata.proto`'s documented exception). Runs BEFORE
|
|
23
|
+
* BuildNewState, which mints only when the id is empty.
|
|
24
|
+
* - CheckDuplicate: by SUBJECT, a primary-key read of the derived id —
|
|
25
|
+
* two accounts may share an email; the account's uniqueness is its
|
|
26
|
+
* subject, never its slug (the guideline's "domains with different
|
|
27
|
+
* uniqueness rules bring their own step under the same name").
|
|
28
|
+
* - AssignBackendFields: `is_machine_account` from the `@clients`
|
|
29
|
+
* suffix and `provisioning_mode direct` on create (the spec's
|
|
30
|
+
* "assigned by backend" fields); on update, every backend-assigned
|
|
31
|
+
* field and the email are preserved from the existing row (A9: the
|
|
32
|
+
* cloud's writable surface — first/last name, picture, preferences —
|
|
33
|
+
* and nothing else; the `preserveImmutableFields` shape).
|
|
34
|
+
* - GuardImmutableSubject (A1): a changed `spec.idp_id` is refused
|
|
35
|
+
* FAILED_PRECONDITION (the schedule domain's shape), never silently
|
|
36
|
+
* preserved — the subject IS the identity, and a client that sends a
|
|
37
|
+
* different one has a bug it should hear about.
|
|
38
|
+
*
|
|
39
|
+
* NOT_FOUND copy is the domain's (`Identity account not found: <id>`, the
|
|
40
|
+
* cloud's byte-pinned sentence); the shared `notFoundError` would render
|
|
41
|
+
* the kind_meta name `IdentityAccount`.
|
|
42
|
+
*/
|
|
43
|
+
import { Code, ConnectError } from "@connectrpc/connect";
|
|
44
|
+
import { create } from "@bufbuild/protobuf";
|
|
45
|
+
import { IdentityAccountProvisioningMode } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/enum_pb";
|
|
46
|
+
import { IdentityAccountSpecSchema } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/spec_pb";
|
|
47
|
+
import { alreadyExistsError, failedPreconditionError, internalError, invalidArgumentError, } from "../../pipeline/errors.js";
|
|
48
|
+
import { RESOURCE_ID_KEY } from "../../pipeline/steps/delete.js";
|
|
49
|
+
import { EXISTING_RESOURCE_KEY } from "../../pipeline/steps/load-existing.js";
|
|
50
|
+
import { TARGET_RESOURCE_KEY } from "../../pipeline/steps/load-target.js";
|
|
51
|
+
import { idValueOf, metadataOf } from "../../pipeline/steps/shapes.js";
|
|
52
|
+
import { accountIdFor, accountNotFoundMessage, idpIdImmutableMessage, isMachineSubject, } from "./constants.js";
|
|
53
|
+
import { DuplicateAccountError } from "./store.js";
|
|
54
|
+
/** The kind's name as the shared error constructors render it. */
|
|
55
|
+
const KIND_NAME = "IdentityAccount";
|
|
56
|
+
/** `Identity account not found: <handle>` as a NOT_FOUND ConnectError. */
|
|
57
|
+
export function accountNotFoundError(handle) {
|
|
58
|
+
return new ConnectError(accountNotFoundMessage(handle), Code.NotFound);
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Create
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
export function newDefaultAccountNameStep() {
|
|
64
|
+
return {
|
|
65
|
+
name: "DefaultAccountName",
|
|
66
|
+
execute(ctx) {
|
|
67
|
+
const metadata = requireMetadata(ctx, "default account name");
|
|
68
|
+
if (metadata.name === "") {
|
|
69
|
+
const spec = specOf(ctx);
|
|
70
|
+
metadata.name = spec.email !== "" ? spec.email : spec.idpId;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function newDeriveAccountIdStep() {
|
|
76
|
+
return {
|
|
77
|
+
name: "DeriveAccountId",
|
|
78
|
+
execute(ctx) {
|
|
79
|
+
const metadata = requireMetadata(ctx, "derive account id");
|
|
80
|
+
// ValidateProto ran upstream (idp_id is required), so an empty
|
|
81
|
+
// subject here is a pipeline-ordering bug, not bad client input.
|
|
82
|
+
const idpId = specOf(ctx).idpId;
|
|
83
|
+
if (idpId === "") {
|
|
84
|
+
throw internalError(new Error("identity account idp_id is empty"), "derive account id");
|
|
85
|
+
}
|
|
86
|
+
metadata.id = accountIdFor(idpId);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Rejects a create whose SUBJECT already has an account (a primary-key read). */
|
|
91
|
+
export function newCheckDuplicateStep(accounts) {
|
|
92
|
+
return {
|
|
93
|
+
name: "CheckDuplicate",
|
|
94
|
+
async execute(ctx) {
|
|
95
|
+
const metadata = requireMetadata(ctx, "duplicate check");
|
|
96
|
+
const idpId = specOf(ctx).idpId;
|
|
97
|
+
let existing;
|
|
98
|
+
try {
|
|
99
|
+
existing = await accounts.findById(metadata.id);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
throw internalError(error, "failed to check for duplicate identity account");
|
|
103
|
+
}
|
|
104
|
+
if (existing !== undefined) {
|
|
105
|
+
throw alreadyExistsError(KIND_NAME, `subject '${idpId}'`);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The backend-assigned fields of a DIRECT account, applied in place: the
|
|
112
|
+
* machine flag from the subject's shape, `provisioning_mode direct`, no
|
|
113
|
+
* provider ref (a direct account is the platform's own subject). The ONE
|
|
114
|
+
* writer of these fields; the provisioner builds its spec through it too.
|
|
115
|
+
*/
|
|
116
|
+
export function assignDirectBackendFields(spec) {
|
|
117
|
+
spec.isMachineAccount = isMachineSubject(spec.idpId);
|
|
118
|
+
spec.provisioningMode = IdentityAccountProvisioningMode.direct;
|
|
119
|
+
spec.identityProviderRef = undefined;
|
|
120
|
+
return spec;
|
|
121
|
+
}
|
|
122
|
+
export function newAssignBackendFieldsStep() {
|
|
123
|
+
return {
|
|
124
|
+
name: "AssignBackendFields",
|
|
125
|
+
execute(ctx) {
|
|
126
|
+
assignDirectBackendFields(specOf(ctx));
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Saves the new account through the port; a held subject is ALREADY_EXISTS. */
|
|
131
|
+
export function newPersistNewAccountStep(accounts) {
|
|
132
|
+
return {
|
|
133
|
+
name: "Persist",
|
|
134
|
+
async execute(ctx) {
|
|
135
|
+
try {
|
|
136
|
+
await accounts.save(ctx.newState);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error instanceof DuplicateAccountError) {
|
|
140
|
+
// The race the duplicate check could not see: another writer
|
|
141
|
+
// took the subject between the read and this save. The
|
|
142
|
+
// provisioner's race arm resolves the winner from this code.
|
|
143
|
+
throw alreadyExistsError(KIND_NAME, `subject '${specOf(ctx).idpId}'`);
|
|
144
|
+
}
|
|
145
|
+
throw internalError(error, "failed to save identity account");
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Update
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
/** Loads the existing account by id into EXISTING_RESOURCE_KEY; the domain's NOT_FOUND copy. */
|
|
154
|
+
export function newLoadExistingAccountStep(accounts) {
|
|
155
|
+
return {
|
|
156
|
+
name: "LoadExisting",
|
|
157
|
+
async execute(ctx) {
|
|
158
|
+
const metadata = requireMetadata(ctx, "load existing");
|
|
159
|
+
if (metadata.id === "") {
|
|
160
|
+
// By id only: an account's slug is derived from its display name
|
|
161
|
+
// and two accounts may share one, so a slug lookup could never be
|
|
162
|
+
// an identity.
|
|
163
|
+
throw invalidArgumentError("resource id is required for update");
|
|
164
|
+
}
|
|
165
|
+
const existing = await readAccount(accounts, metadata.id);
|
|
166
|
+
ctx.set(EXISTING_RESOURCE_KEY, existing);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
export function newGuardImmutableSubjectStep() {
|
|
171
|
+
return {
|
|
172
|
+
name: "GuardImmutableSubject",
|
|
173
|
+
execute(ctx) {
|
|
174
|
+
const existing = existingAccountOf(ctx);
|
|
175
|
+
const existingIdpId = existing.spec?.idpId ?? "";
|
|
176
|
+
if (specOf(ctx).idpId !== existingIdpId) {
|
|
177
|
+
throw failedPreconditionError(idpIdImmutableMessage(existingIdpId));
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** Preserves the backend-assigned fields and the email from the existing row (A9). */
|
|
183
|
+
export function newPreserveBackendFieldsStep() {
|
|
184
|
+
return {
|
|
185
|
+
name: "AssignBackendFields",
|
|
186
|
+
execute(ctx) {
|
|
187
|
+
const existingSpec = existingAccountOf(ctx).spec;
|
|
188
|
+
const spec = specOf(ctx);
|
|
189
|
+
spec.idpId = existingSpec?.idpId ?? spec.idpId;
|
|
190
|
+
spec.email = existingSpec?.email ?? "";
|
|
191
|
+
spec.isMachineAccount = existingSpec?.isMachineAccount ?? false;
|
|
192
|
+
spec.provisioningMode =
|
|
193
|
+
existingSpec?.provisioningMode ??
|
|
194
|
+
IdentityAccountProvisioningMode.identity_account_provisioning_mode_unspecified;
|
|
195
|
+
spec.identityProviderRef = existingSpec?.identityProviderRef;
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/** Replaces the row through the port. */
|
|
200
|
+
export function newPersistUpdatedAccountStep(accounts) {
|
|
201
|
+
return {
|
|
202
|
+
name: "Persist",
|
|
203
|
+
async execute(ctx) {
|
|
204
|
+
try {
|
|
205
|
+
await accounts.update(ctx.newState);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
throw internalError(error, "failed to save identity account");
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Get / Delete (ID-wrapper inputs)
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
/** Loads the account named by an ID-wrapper input into TARGET_RESOURCE_KEY. */
|
|
217
|
+
export function newLoadTargetAccountStep(accounts) {
|
|
218
|
+
return {
|
|
219
|
+
name: "LoadTarget",
|
|
220
|
+
async execute(ctx) {
|
|
221
|
+
const id = idValueOf(ctx.input);
|
|
222
|
+
if (id === "") {
|
|
223
|
+
throw invalidArgumentError("resource id is required");
|
|
224
|
+
}
|
|
225
|
+
ctx.set(TARGET_RESOURCE_KEY, await readAccount(accounts, id));
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/** Loads the doomed account (after ExtractResourceId) so delete can return it. */
|
|
230
|
+
export function newLoadExistingAccountForDeleteStep(accounts) {
|
|
231
|
+
return {
|
|
232
|
+
name: "LoadExistingForDelete",
|
|
233
|
+
async execute(ctx) {
|
|
234
|
+
ctx.set(EXISTING_RESOURCE_KEY, await readAccount(accounts, requireResourceId(ctx)));
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
export function newDeleteAccountStep(accounts) {
|
|
239
|
+
return {
|
|
240
|
+
name: "DeleteResource",
|
|
241
|
+
async execute(ctx) {
|
|
242
|
+
try {
|
|
243
|
+
await accounts.deleteById(requireResourceId(ctx));
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
throw internalError(error, "failed to delete identity account");
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
// Shared readers
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
/** The account by id, or the domain's NOT_FOUND; any other fault propagates. */
|
|
255
|
+
async function readAccount(accounts, id) {
|
|
256
|
+
const account = await accounts.findById(id);
|
|
257
|
+
if (account === undefined) {
|
|
258
|
+
throw accountNotFoundError(id);
|
|
259
|
+
}
|
|
260
|
+
return account;
|
|
261
|
+
}
|
|
262
|
+
function requireMetadata(ctx, operation) {
|
|
263
|
+
const metadata = metadataOf(ctx.newState);
|
|
264
|
+
if (metadata === undefined) {
|
|
265
|
+
throw internalError(new Error("identity account metadata is nil"), operation);
|
|
266
|
+
}
|
|
267
|
+
return metadata;
|
|
268
|
+
}
|
|
269
|
+
/** The spec being built, created in place when the caller omitted it. */
|
|
270
|
+
function specOf(ctx) {
|
|
271
|
+
const state = ctx.newState;
|
|
272
|
+
if (state.spec === undefined) {
|
|
273
|
+
state.spec = create(IdentityAccountSpecSchema);
|
|
274
|
+
}
|
|
275
|
+
return state.spec;
|
|
276
|
+
}
|
|
277
|
+
function existingAccountOf(ctx) {
|
|
278
|
+
const existing = ctx.get(EXISTING_RESOURCE_KEY);
|
|
279
|
+
if (existing === undefined) {
|
|
280
|
+
throw internalError(new Error("existing identity account not found in context - LoadExisting must run first"), "identity account update ordering");
|
|
281
|
+
}
|
|
282
|
+
return existing;
|
|
283
|
+
}
|
|
284
|
+
function requireResourceId(ctx) {
|
|
285
|
+
const id = ctx.get(RESOURCE_ID_KEY);
|
|
286
|
+
if (typeof id !== "string" || id === "") {
|
|
287
|
+
throw internalError(new Error("resource id not found in context (ExtractResourceId must run first)"), "identity account delete ordering");
|
|
288
|
+
}
|
|
289
|
+
return id;
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=steps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../src/domain/identityaccount/steps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IdentityAccountStore } from "./store.js";
|
|
2
|
+
/** One fresh, isolated store per case, plus the one escape hatch the port cannot express. */
|
|
3
|
+
export interface IdentityAccountStoreContractFixture {
|
|
4
|
+
readonly store: IdentityAccountStore;
|
|
5
|
+
/**
|
|
6
|
+
* Cuts the store from its database so every later call is an
|
|
7
|
+
* infrastructure fault. `cleanup` still runs afterwards and must tolerate
|
|
8
|
+
* a disconnected store (both OSS drivers' `close` is idempotent; a pool
|
|
9
|
+
* fixture ends a per-case pool here and skips it in cleanup).
|
|
10
|
+
*/
|
|
11
|
+
disconnect(): Promise<void>;
|
|
12
|
+
cleanup(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface IdentityAccountStoreContractCase {
|
|
15
|
+
/** The contract line, in the port's words; the framework prints it as the test name. */
|
|
16
|
+
readonly name: string;
|
|
17
|
+
/** Makes a FRESH fixture, runs the case, cleans up. A failing assertion wins over a failing cleanup. */
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/** The port's cases over `makeFixture`, one fresh fixture per case. */
|
|
21
|
+
export declare function identityAccountStoreContract(makeFixture: () => Promise<IdentityAccountStoreContractFixture>): ReadonlyArray<IdentityAccountStoreContractCase>;
|
|
22
|
+
//# sourceMappingURL=store-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-contract.d.ts","sourceRoot":"","sources":["../../../src/domain/identityaccount/store-contract.ts"],"names":[],"mappings":"AAwDA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,6FAA6F;AAC7F,MAAM,WAAW,mCAAmC;IAClD,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC;;;;;OAKG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,gCAAgC;IAC/C,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wGAAwG;IACxG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAkYD,uEAAuE;AACvE,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,MAAM,OAAO,CAAC,mCAAmC,CAAC,GAC9D,aAAa,CAAC,gCAAgC,CAAC,CAKjD"}
|