@stigmer/server 3.24.0 → 3.26.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/authorization/authorizer.d.ts +10 -4
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +11 -4
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/model/index.d.ts +4 -4
- package/dist/authorization/model/index.d.ts.map +1 -1
- package/dist/authorization/model/index.js +2 -0
- package/dist/authorization/model/index.js.map +1 -1
- package/dist/authorization/model/oauth_app.d.ts.map +1 -1
- package/dist/authorization/model/oauth_app.js +3 -2
- package/dist/authorization/model/oauth_app.js.map +1 -1
- package/dist/authorization/model/platform_client.d.ts +2 -0
- package/dist/authorization/model/platform_client.d.ts.map +1 -0
- package/dist/authorization/model/platform_client.js +31 -0
- package/dist/authorization/model/platform_client.js.map +1 -0
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +99 -13
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/list-indexes.d.ts +3 -0
- package/dist/boot/list-indexes.d.ts.map +1 -0
- package/dist/boot/list-indexes.js +24 -0
- package/dist/boot/list-indexes.js.map +1 -0
- package/dist/boot/route-shadowing.d.ts +25 -0
- package/dist/boot/route-shadowing.d.ts.map +1 -0
- package/dist/boot/route-shadowing.js +17 -0
- package/dist/boot/route-shadowing.js.map +1 -0
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +6 -8
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentexecution/list-index.d.ts +2 -0
- package/dist/domain/agentexecution/list-index.d.ts.map +1 -0
- package/dist/domain/agentexecution/list-index.js +23 -0
- package/dist/domain/agentexecution/list-index.js.map +1 -0
- package/dist/domain/agentexecution/steps.d.ts +17 -33
- package/dist/domain/agentexecution/steps.d.ts.map +1 -1
- package/dist/domain/agentexecution/steps.js +94 -95
- package/dist/domain/agentexecution/steps.js.map +1 -1
- package/dist/domain/agentexecution/usage.d.ts.map +1 -1
- package/dist/domain/agentexecution/usage.js +37 -14
- package/dist/domain/agentexecution/usage.js.map +1 -1
- package/dist/domain/artifact/controller.d.ts.map +1 -1
- package/dist/domain/artifact/controller.js +27 -15
- package/dist/domain/artifact/controller.js.map +1 -1
- package/dist/domain/artifact/list-index.d.ts +2 -0
- package/dist/domain/artifact/list-index.d.ts.map +1 -0
- package/dist/domain/artifact/list-index.js +21 -0
- package/dist/domain/artifact/list-index.js.map +1 -0
- package/dist/domain/iampolicy/membership.d.ts +6 -1
- package/dist/domain/iampolicy/membership.d.ts.map +1 -1
- package/dist/domain/iampolicy/membership.js +8 -2
- package/dist/domain/iampolicy/membership.js.map +1 -1
- package/dist/domain/identityaccount/constants.d.ts +13 -0
- package/dist/domain/identityaccount/constants.d.ts.map +1 -1
- package/dist/domain/identityaccount/constants.js +17 -0
- package/dist/domain/identityaccount/constants.js.map +1 -1
- package/dist/domain/identityaccount/controller.d.ts +3 -1
- package/dist/domain/identityaccount/controller.d.ts.map +1 -1
- package/dist/domain/identityaccount/controller.js +15 -6
- package/dist/domain/identityaccount/controller.js.map +1 -1
- package/dist/domain/identityaccount/operator.d.ts.map +1 -1
- package/dist/domain/identityaccount/operator.js +1 -0
- package/dist/domain/identityaccount/operator.js.map +1 -1
- package/dist/domain/identityaccount/provisioning.d.ts +18 -0
- package/dist/domain/identityaccount/provisioning.d.ts.map +1 -1
- package/dist/domain/identityaccount/provisioning.js +1 -0
- package/dist/domain/identityaccount/provisioning.js.map +1 -1
- package/dist/domain/identityaccount/resource-store.js +9 -5
- package/dist/domain/identityaccount/resource-store.js.map +1 -1
- package/dist/domain/identityaccount/steps.d.ts +11 -2
- package/dist/domain/identityaccount/steps.d.ts.map +1 -1
- package/dist/domain/identityaccount/steps.js +42 -4
- package/dist/domain/identityaccount/steps.js.map +1 -1
- package/dist/domain/identityaccount/store-contract.d.ts.map +1 -1
- package/dist/domain/identityaccount/store-contract.js +33 -0
- package/dist/domain/identityaccount/store-contract.js.map +1 -1
- package/dist/domain/identityaccount/store.d.ts +7 -5
- package/dist/domain/identityaccount/store.d.ts.map +1 -1
- package/dist/domain/identityaccount/store.js.map +1 -1
- package/dist/domain/platform/controller.d.ts +10 -0
- package/dist/domain/platform/controller.d.ts.map +1 -1
- package/dist/domain/platform/controller.js +2 -1
- package/dist/domain/platform/controller.js.map +1 -1
- package/dist/domain/platformclient/constants.d.ts +60 -0
- package/dist/domain/platformclient/constants.d.ts.map +1 -0
- package/dist/domain/platformclient/constants.js +78 -0
- package/dist/domain/platformclient/constants.js.map +1 -0
- package/dist/domain/platformclient/controller.d.ts +59 -0
- package/dist/domain/platformclient/controller.d.ts.map +1 -0
- package/dist/domain/platformclient/controller.js +191 -0
- package/dist/domain/platformclient/controller.js.map +1 -0
- package/dist/domain/platformclient/credentials.d.ts +13 -0
- package/dist/domain/platformclient/credentials.d.ts.map +1 -0
- package/dist/domain/platformclient/credentials.js +56 -0
- package/dist/domain/platformclient/credentials.js.map +1 -0
- package/dist/domain/platformclient/mint.d.ts +23 -0
- package/dist/domain/platformclient/mint.d.ts.map +1 -0
- package/dist/domain/platformclient/mint.js +254 -0
- package/dist/domain/platformclient/mint.js.map +1 -0
- package/dist/domain/platformclient/origin-guard.d.ts +9 -0
- package/dist/domain/platformclient/origin-guard.d.ts.map +1 -0
- package/dist/domain/platformclient/origin-guard.js +81 -0
- package/dist/domain/platformclient/origin-guard.js.map +1 -0
- package/dist/domain/platformclient/resource-store.d.ts +4 -0
- package/dist/domain/platformclient/resource-store.d.ts.map +1 -0
- package/dist/domain/platformclient/resource-store.js +117 -0
- package/dist/domain/platformclient/resource-store.js.map +1 -0
- package/dist/domain/platformclient/steps.d.ts +51 -0
- package/dist/domain/platformclient/steps.d.ts.map +1 -0
- package/dist/domain/platformclient/steps.js +339 -0
- package/dist/domain/platformclient/steps.js.map +1 -0
- package/dist/domain/platformclient/store-contract.d.ts +8 -0
- package/dist/domain/platformclient/store-contract.d.ts.map +1 -0
- package/dist/domain/platformclient/store-contract.js +208 -0
- package/dist/domain/platformclient/store-contract.js.map +1 -0
- package/dist/domain/platformclient/store.d.ts +58 -0
- package/dist/domain/platformclient/store.d.ts.map +1 -0
- package/dist/domain/platformclient/store.js +13 -0
- package/dist/domain/platformclient/store.js.map +1 -0
- package/dist/domain/platformclient/system-managed.d.ts +18 -0
- package/dist/domain/platformclient/system-managed.d.ts.map +1 -0
- package/dist/domain/platformclient/system-managed.js +80 -0
- package/dist/domain/platformclient/system-managed.js.map +1 -0
- package/dist/domain/platformclient/token-controller.d.ts +11 -0
- package/dist/domain/platformclient/token-controller.d.ts.map +1 -0
- package/dist/domain/platformclient/token-controller.js +32 -0
- package/dist/domain/platformclient/token-controller.js.map +1 -0
- package/dist/domain/platformclient/verifier.d.ts +10 -0
- package/dist/domain/platformclient/verifier.d.ts.map +1 -0
- package/dist/domain/platformclient/verifier.js +76 -0
- package/dist/domain/platformclient/verifier.js.map +1 -0
- package/dist/domain/session/list-index.d.ts +4 -0
- package/dist/domain/session/list-index.d.ts.map +1 -0
- package/dist/domain/session/list-index.js +26 -0
- package/dist/domain/session/list-index.js.map +1 -0
- package/dist/domain/session/steps.d.ts +7 -8
- package/dist/domain/session/steps.d.ts.map +1 -1
- package/dist/domain/session/steps.js +76 -78
- package/dist/domain/session/steps.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +53 -55
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowexecution/execution-filter.d.ts +4 -0
- package/dist/domain/workflowexecution/execution-filter.d.ts.map +1 -1
- package/dist/domain/workflowexecution/execution-filter.js +11 -5
- package/dist/domain/workflowexecution/execution-filter.js.map +1 -1
- package/dist/domain/workflowexecution/get-execution-summary.d.ts.map +1 -1
- package/dist/domain/workflowexecution/get-execution-summary.js +23 -17
- package/dist/domain/workflowexecution/get-execution-summary.js.map +1 -1
- package/dist/domain/workflowexecution/list-index.d.ts +2 -0
- package/dist/domain/workflowexecution/list-index.d.ts.map +1 -0
- package/dist/domain/workflowexecution/list-index.js +21 -0
- package/dist/domain/workflowexecution/list-index.js.map +1 -0
- package/dist/domain/workflowexecution/list-pending-approvals.d.ts.map +1 -1
- package/dist/domain/workflowexecution/list-pending-approvals.js +69 -32
- package/dist/domain/workflowexecution/list-pending-approvals.js.map +1 -1
- package/dist/domain/workflowexecution/queries.d.ts +30 -4
- package/dist/domain/workflowexecution/queries.d.ts.map +1 -1
- package/dist/domain/workflowexecution/queries.js +76 -22
- package/dist/domain/workflowexecution/queries.js.map +1 -1
- package/dist/encryption/key-manager.d.ts +20 -3
- package/dist/encryption/key-manager.d.ts.map +1 -1
- package/dist/encryption/key-manager.js +64 -42
- package/dist/encryption/key-manager.js.map +1 -1
- package/dist/extensions/caller-guards.d.ts +8 -3
- package/dist/extensions/caller-guards.d.ts.map +1 -1
- package/dist/extensions/drivers.d.ts +37 -0
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/guest-token-minting.d.ts +26 -0
- package/dist/extensions/guest-token-minting.d.ts.map +1 -0
- package/dist/extensions/guest-token-minting.js +2 -0
- package/dist/extensions/guest-token-minting.js.map +1 -0
- package/dist/extensions/identity.d.ts +6 -4
- package/dist/extensions/identity.d.ts.map +1 -1
- package/dist/extensions/identity.js.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +9 -12
- package/dist/extensions/list-read-scope.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.js.map +1 -1
- package/dist/extensions/registry.d.ts +30 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +34 -1
- package/dist/extensions/registry.js.map +1 -1
- package/dist/index.d.ts +15 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/interceptors/auth.d.ts +14 -0
- package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
- package/dist/pipeline/interceptors/auth.js +24 -0
- package/dist/pipeline/interceptors/auth.js.map +1 -1
- package/dist/pipeline/steps/build-update-state.d.ts +11 -1
- package/dist/pipeline/steps/build-update-state.d.ts.map +1 -1
- package/dist/pipeline/steps/build-update-state.js +5 -2
- package/dist/pipeline/steps/build-update-state.js.map +1 -1
- package/dist/pipeline/steps/list-page.d.ts +63 -0
- package/dist/pipeline/steps/list-page.d.ts.map +1 -0
- package/dist/pipeline/steps/list-page.js +187 -0
- package/dist/pipeline/steps/list-page.js.map +1 -0
- package/dist/platformtoken/envelope.d.ts +100 -0
- package/dist/platformtoken/envelope.d.ts.map +1 -0
- package/dist/platformtoken/envelope.js +184 -0
- package/dist/platformtoken/envelope.js.map +1 -0
- package/dist/platformtoken/key-ring.d.ts +85 -0
- package/dist/platformtoken/key-ring.d.ts.map +1 -0
- package/dist/platformtoken/key-ring.js +164 -0
- package/dist/platformtoken/key-ring.js.map +1 -0
- package/dist/query/activity/handler.d.ts +6 -7
- package/dist/query/activity/handler.d.ts.map +1 -1
- package/dist/query/activity/handler.js +42 -59
- package/dist/query/activity/handler.js.map +1 -1
- package/dist/store/interface.d.ts +29 -0
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/list-index.d.ts +123 -0
- package/dist/store/list-index.d.ts.map +1 -0
- package/dist/store/list-index.js +277 -0
- package/dist/store/list-index.js.map +1 -0
- package/dist/store/logger.d.ts +3 -0
- package/dist/store/logger.d.ts.map +1 -1
- package/dist/store/logger.js +5 -1
- package/dist/store/logger.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +10 -6
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +64 -6
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts +26 -5
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +377 -22
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/sqlite/migrations.d.ts +11 -5
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +50 -4
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts +18 -3
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +321 -15
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/README.md +1 -1
- package/src/authorization/__tests__/authorizer.test.ts +6 -2
- package/src/authorization/__tests__/drivers.ts +6 -1
- package/src/authorization/__tests__/wire-permissions.test.ts +1 -0
- package/src/authorization/authorizer.ts +11 -4
- package/src/authorization/model/__tests__/registry.test.ts +8 -1
- package/src/authorization/model/index.ts +6 -4
- package/src/authorization/model/oauth_app.ts +3 -2
- package/src/authorization/model/platform_client.ts +43 -0
- package/src/boot/__tests__/auth-enabled-composition.test.ts +2 -1
- package/src/boot/__tests__/list-indexes.test.ts +65 -0
- package/src/boot/__tests__/route-shadowing.test.ts +67 -0
- package/src/boot/compose.ts +113 -18
- package/src/boot/list-indexes.ts +25 -0
- package/src/boot/route-shadowing.ts +47 -0
- package/src/domain/agentexecution/__tests__/agentexecution.test.ts +85 -3
- package/src/domain/agentexecution/controller.ts +8 -10
- package/src/domain/agentexecution/list-index.ts +24 -0
- package/src/domain/agentexecution/steps.ts +118 -115
- package/src/domain/agentexecution/usage.ts +38 -17
- package/src/domain/artifact/controller.ts +28 -18
- package/src/domain/artifact/list-index.ts +22 -0
- package/src/domain/iampolicy/__tests__/membership.test.ts +7 -0
- package/src/domain/iampolicy/membership.ts +8 -2
- package/src/domain/identityaccount/__tests__/platform-client-accounts.test.ts +124 -0
- package/src/domain/identityaccount/__tests__/resource-store.test.ts +1 -0
- package/src/domain/identityaccount/constants.ts +20 -0
- package/src/domain/identityaccount/controller.ts +16 -4
- package/src/domain/identityaccount/operator.ts +1 -0
- package/src/domain/identityaccount/provisioning.ts +17 -0
- package/src/domain/identityaccount/resource-store.ts +9 -5
- package/src/domain/identityaccount/steps.ts +58 -3
- package/src/domain/identityaccount/store-contract.ts +51 -0
- package/src/domain/identityaccount/store.ts +7 -5
- package/src/domain/platform/__tests__/platform.test.ts +7 -1
- package/src/domain/platform/controller.ts +12 -1
- package/src/domain/platformclient/__tests__/controller.test.ts +397 -0
- package/src/domain/platformclient/__tests__/credentials.test.ts +46 -0
- package/src/domain/platformclient/__tests__/mint.test.ts +415 -0
- package/src/domain/platformclient/__tests__/resource-store.test.ts +140 -0
- package/src/domain/platformclient/__tests__/system-managed.test.ts +101 -0
- package/src/domain/platformclient/__tests__/verifier-and-guard.test.ts +207 -0
- package/src/domain/platformclient/constants.ts +120 -0
- package/src/domain/platformclient/controller.ts +449 -0
- package/src/domain/platformclient/credentials.ts +68 -0
- package/src/domain/platformclient/mint.ts +412 -0
- package/src/domain/platformclient/origin-guard.ts +117 -0
- package/src/domain/platformclient/resource-store.ts +150 -0
- package/src/domain/platformclient/steps.ts +472 -0
- package/src/domain/platformclient/store-contract.ts +318 -0
- package/src/domain/platformclient/store.ts +63 -0
- package/src/domain/platformclient/system-managed.ts +115 -0
- package/src/domain/platformclient/token-controller.ts +49 -0
- package/src/domain/platformclient/verifier.ts +106 -0
- package/src/domain/session/list-index.ts +28 -0
- package/src/domain/session/steps.ts +108 -123
- package/src/domain/workflowexecution/__tests__/list-read-scope-summaries.test.ts +155 -99
- package/src/domain/workflowexecution/__tests__/workflowexecution.test.ts +193 -31
- package/src/domain/workflowexecution/controller.ts +54 -82
- package/src/domain/workflowexecution/execution-filter.ts +20 -7
- package/src/domain/workflowexecution/get-execution-summary.ts +31 -24
- package/src/domain/workflowexecution/list-index.ts +22 -0
- package/src/domain/workflowexecution/list-pending-approvals.ts +82 -25
- package/src/domain/workflowexecution/queries.ts +134 -23
- package/src/encryption/key-manager.ts +87 -44
- package/src/extensions/__tests__/extension-composition.test.ts +3 -2
- package/src/extensions/__tests__/platform-client-composed.test.ts +288 -0
- package/src/extensions/__tests__/registry.test.ts +4 -1
- package/src/extensions/__tests__/tier-truthfulness.test.ts +8 -0
- package/src/extensions/caller-guards.ts +8 -3
- package/src/extensions/drivers.ts +37 -0
- package/src/extensions/guest-token-minting.ts +32 -0
- package/src/extensions/identity.ts +6 -4
- package/src/extensions/list-read-scope.ts +9 -12
- package/src/extensions/registry.ts +77 -3
- package/src/index.ts +65 -1
- package/src/pipeline/interceptors/auth.ts +27 -0
- package/src/pipeline/steps/__tests__/list-page.test.ts +186 -0
- package/src/pipeline/steps/__tests__/references.test.ts +2 -0
- package/src/pipeline/steps/build-update-state.ts +5 -2
- package/src/pipeline/steps/list-page.ts +284 -0
- package/src/platformtoken/__tests__/envelope.test.ts +277 -0
- package/src/platformtoken/__tests__/key-ring.test.ts +205 -0
- package/src/platformtoken/envelope.ts +276 -0
- package/src/platformtoken/key-ring.ts +266 -0
- package/src/query/activity/handler.ts +57 -78
- package/src/runnerauth/__tests__/runner-subject-composed.test.ts +9 -3
- package/src/store/README.md +51 -32
- package/src/store/__tests__/list-index.measure.test.ts +293 -0
- package/src/store/__tests__/list-index.test.ts +328 -0
- package/src/store/__tests__/store-contract.ts +346 -3
- package/src/store/interface.ts +39 -0
- package/src/store/list-index.ts +465 -0
- package/src/store/logger.ts +8 -1
- package/src/store/postgres/__tests__/list-index-repair.test.ts +133 -0
- package/src/store/postgres/__tests__/migrations.test.ts +27 -19
- package/src/store/postgres/__tests__/store-contract.test.ts +50 -3
- package/src/store/postgres/migrations.ts +69 -7
- package/src/store/postgres/store.ts +485 -29
- package/src/store/sqlite/__tests__/migrations.test.ts +51 -7
- package/src/store/sqlite/__tests__/store-contract.test.ts +70 -16
- package/src/store/sqlite/__tests__/support.ts +12 -3
- package/src/store/sqlite/migrations.ts +57 -6
- package/src/store/sqlite/store.ts +462 -21
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform-client domain steps.
|
|
3
|
+
*
|
|
4
|
+
* The domain's persistence is a PORT (store.ts), so the shared steps that
|
|
5
|
+
* touch storage (CheckDuplicate, LoadExisting, LoadTarget, LoadByReference,
|
|
6
|
+
* Persist, the delete pair) come here under the SAME names over the port —
|
|
7
|
+
* the identity-account domain's "own step, same name" precedent — and every
|
|
8
|
+
* storage-free shared step is reused as it is. The shared context keys are
|
|
9
|
+
* reused too, so BuildUpdateState finds EXISTING_RESOURCE_KEY and the
|
|
10
|
+
* reference read's AuthorizeResolvedTarget finds TARGET_RESOURCE_KEY where
|
|
11
|
+
* they expect them.
|
|
12
|
+
*
|
|
13
|
+
* The domain's own rules:
|
|
14
|
+
* - RefuseReservedSlug: `system-share-client` is the platform's (the
|
|
15
|
+
* cloud keeps each organization's system-managed share client under
|
|
16
|
+
* it, created through its driver, never through this chain), refused
|
|
17
|
+
* after ResolveSlug so a derived slug is refused too.
|
|
18
|
+
* - RefuseSystemManaged: a client carrying the reserved
|
|
19
|
+
* `stigmer.ai/system-managed` label is the platform's; update, delete
|
|
20
|
+
* and rotate refuse it FAILED_PRECONDITION with the cloud's copy.
|
|
21
|
+
* - GenerateClientCredentials, PreserveClientCredentials and
|
|
22
|
+
* RotateClientCredentials: the credential fields are the server's
|
|
23
|
+
* alone — generated on create (whatever the request carried), kept
|
|
24
|
+
* from the stored row on update, replaced (secret only; the client_id
|
|
25
|
+
* is permanent) on rotate. The plaintext secret is parked in the
|
|
26
|
+
* context for the one response that shows it, never persisted.
|
|
27
|
+
* - redactPlatformClient: the stored hash leaves on no response. It is
|
|
28
|
+
* cleared rather than marked (OAuthApp's `***REDACTED***` marker exists
|
|
29
|
+
* because its secret round-trips on update; this hash never does —
|
|
30
|
+
* PreserveClientCredentials restores it from the row).
|
|
31
|
+
*
|
|
32
|
+
* Not-found answers use the shared `notFoundError`, which renders exactly
|
|
33
|
+
* the cloud's `PlatformClient not found: <id>`; the reference read keeps
|
|
34
|
+
* the cloud's own sentence (constants.ts).
|
|
35
|
+
*/
|
|
36
|
+
import { Code, ConnectError } from "@connectrpc/connect";
|
|
37
|
+
import { create } from "@bufbuild/protobuf";
|
|
38
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
39
|
+
import { PlatformClientSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
40
|
+
import { PlatformClientSpecSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/spec_pb";
|
|
41
|
+
import { RESERVED_LABEL_TRUE, SYSTEM_MANAGED_LABEL, } from "../../pipeline/apiresource-labels.js";
|
|
42
|
+
import { alreadyExistsError, failedPreconditionError, internalError, invalidArgumentError, notFoundError, } from "../../pipeline/errors.js";
|
|
43
|
+
import { updateAuditFields } from "../../pipeline/steps/build-update-state.js";
|
|
44
|
+
import { RESOURCE_ID_KEY } from "../../pipeline/steps/delete.js";
|
|
45
|
+
import { requireOrgForReference } from "../../pipeline/steps/helpers.js";
|
|
46
|
+
import { EXISTING_RESOURCE_KEY } from "../../pipeline/steps/load-existing.js";
|
|
47
|
+
import { TARGET_RESOURCE_KEY } from "../../pipeline/steps/load-target.js";
|
|
48
|
+
import { idValueOf, metadataOf } from "../../pipeline/steps/shapes.js";
|
|
49
|
+
import { PLATFORM_CLIENT_KIND_NAME, RESERVED_PLATFORM_CLIENT_SLUGS, referenceNotFoundMessage, reservedSlugMessage, systemManagedMessage, } from "./constants.js";
|
|
50
|
+
import { generateClientId, generateClientSecret, hashClientSecret, secretFingerprint, } from "./credentials.js";
|
|
51
|
+
import { DuplicatePlatformClientError } from "./store.js";
|
|
52
|
+
/** Context key for the plaintext secret the create and rotate responses show once. */
|
|
53
|
+
export const CLIENT_SECRET_PLAINTEXT_KEY = "platformClientSecretPlaintext";
|
|
54
|
+
/** Clears the stored hash in place and answers the client — every response goes through it. */
|
|
55
|
+
export function redactPlatformClient(client) {
|
|
56
|
+
if (client.spec !== undefined) {
|
|
57
|
+
client.spec.clientSecretHash = "";
|
|
58
|
+
}
|
|
59
|
+
return client;
|
|
60
|
+
}
|
|
61
|
+
/** The plaintext secret a credential step parked, or an ordering fault. */
|
|
62
|
+
export function parkedClientSecret(ctx) {
|
|
63
|
+
const secret = ctx.get(CLIENT_SECRET_PLAINTEXT_KEY);
|
|
64
|
+
if (typeof secret !== "string" || secret === "") {
|
|
65
|
+
throw internalError(new Error("no client secret parked — a credential step must run first"), "platform client credentials");
|
|
66
|
+
}
|
|
67
|
+
return secret;
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Create
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
export function newRefuseReservedSlugStep() {
|
|
73
|
+
return {
|
|
74
|
+
name: "RefuseReservedSlug",
|
|
75
|
+
execute(ctx) {
|
|
76
|
+
const slug = metadataOf(ctx.newState)?.slug ?? "";
|
|
77
|
+
if (RESERVED_PLATFORM_CLIENT_SLUGS.has(slug)) {
|
|
78
|
+
throw invalidArgumentError(reservedSlugMessage(slug));
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Rejects a create whose slug the organization already holds. */
|
|
84
|
+
export function newCheckDuplicateStep(clients) {
|
|
85
|
+
return {
|
|
86
|
+
name: "CheckDuplicate",
|
|
87
|
+
async execute(ctx) {
|
|
88
|
+
const metadata = requireMetadata(ctx, "duplicate check");
|
|
89
|
+
const existing = await clients.findByOrgAndSlug(metadata.org, metadata.slug);
|
|
90
|
+
if (existing !== undefined) {
|
|
91
|
+
throw alreadyExistsError(PLATFORM_CLIENT_KIND_NAME, `slug '${metadata.slug}' in org '${metadata.org}' (id: ${existing.metadata?.id ?? ""})`);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** Generates the credential fields after BuildNewState, parking the plaintext for the response. */
|
|
97
|
+
export function newGenerateClientCredentialsStep() {
|
|
98
|
+
return {
|
|
99
|
+
name: "GenerateClientCredentials",
|
|
100
|
+
execute(ctx) {
|
|
101
|
+
const spec = specOf(ctx.newState);
|
|
102
|
+
const secret = generateClientSecret();
|
|
103
|
+
spec.clientId = generateClientId();
|
|
104
|
+
spec.clientSecretHash = hashClientSecret(secret);
|
|
105
|
+
spec.secretFingerprint = secretFingerprint(secret);
|
|
106
|
+
ctx.set(CLIENT_SECRET_PLAINTEXT_KEY, secret);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Saves the new client through the port; a held slug or client_id is ALREADY_EXISTS. */
|
|
111
|
+
export function newPersistNewClientStep(clients) {
|
|
112
|
+
return {
|
|
113
|
+
name: "Persist",
|
|
114
|
+
async execute(ctx) {
|
|
115
|
+
try {
|
|
116
|
+
await clients.save(ctx.newState);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error instanceof DuplicatePlatformClientError) {
|
|
120
|
+
// The race the duplicate check could not see.
|
|
121
|
+
const metadata = requireMetadata(ctx, "persist");
|
|
122
|
+
throw alreadyExistsError(PLATFORM_CLIENT_KIND_NAME, `slug '${metadata.slug}' in org '${metadata.org}'`);
|
|
123
|
+
}
|
|
124
|
+
throw internalError(error, "failed to save platform client");
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Update
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
/** Loads the client by id, else by org-scoped slug (the id written back), into EXISTING_RESOURCE_KEY. */
|
|
133
|
+
export function newLoadExistingClientStep(clients) {
|
|
134
|
+
return {
|
|
135
|
+
name: "LoadExisting",
|
|
136
|
+
async execute(ctx) {
|
|
137
|
+
const metadata = requireMetadata(ctx, "load existing");
|
|
138
|
+
let existing;
|
|
139
|
+
if (metadata.id !== "") {
|
|
140
|
+
existing = await clients.findById(metadata.id);
|
|
141
|
+
if (existing === undefined) {
|
|
142
|
+
throw notFoundError(PLATFORM_CLIENT_KIND_NAME, metadata.id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (metadata.slug !== "") {
|
|
146
|
+
existing = await clients.findByOrgAndSlug(metadata.org, metadata.slug);
|
|
147
|
+
if (existing === undefined) {
|
|
148
|
+
throw notFoundError(PLATFORM_CLIENT_KIND_NAME, metadata.slug);
|
|
149
|
+
}
|
|
150
|
+
metadata.id = existing.metadata?.id ?? "";
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
throw invalidArgumentError("resource id or slug is required for update");
|
|
154
|
+
}
|
|
155
|
+
ctx.set(EXISTING_RESOURCE_KEY, existing);
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Restores the credential fields from the stored row after BuildUpdateState. */
|
|
160
|
+
export function newPreserveClientCredentialsStep() {
|
|
161
|
+
return {
|
|
162
|
+
name: "PreserveClientCredentials",
|
|
163
|
+
execute(ctx) {
|
|
164
|
+
const existing = storedClientOf(ctx, EXISTING_RESOURCE_KEY);
|
|
165
|
+
const spec = specOf(ctx.newState);
|
|
166
|
+
spec.clientId = existing.spec?.clientId ?? "";
|
|
167
|
+
spec.clientSecretHash = existing.spec?.clientSecretHash ?? "";
|
|
168
|
+
spec.secretFingerprint = existing.spec?.secretFingerprint ?? "";
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/** Replaces the row through the port. */
|
|
173
|
+
export function newPersistUpdatedClientStep(clients) {
|
|
174
|
+
return {
|
|
175
|
+
name: "Persist",
|
|
176
|
+
async execute(ctx) {
|
|
177
|
+
try {
|
|
178
|
+
await clients.update(ctx.newState);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
throw internalError(error, "failed to save platform client");
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Update, delete and rotate: the platform's own clients
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
/** Refuses a mutation of a client carrying the system-managed label, read from `key`. */
|
|
190
|
+
export function newRefuseSystemManagedStep(verb, key) {
|
|
191
|
+
return {
|
|
192
|
+
name: "RefuseSystemManaged",
|
|
193
|
+
execute(ctx) {
|
|
194
|
+
const client = storedClientOf(ctx, key);
|
|
195
|
+
if (client.metadata?.labels[SYSTEM_MANAGED_LABEL] === RESERVED_LABEL_TRUE) {
|
|
196
|
+
throw failedPreconditionError(systemManagedMessage(verb));
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Get, getByReference, rotate
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
/** Loads the client named by an ID-wrapper input into TARGET_RESOURCE_KEY. */
|
|
205
|
+
export function newLoadTargetClientStep(clients) {
|
|
206
|
+
return {
|
|
207
|
+
name: "LoadTarget",
|
|
208
|
+
async execute(ctx) {
|
|
209
|
+
const id = idValueOf(ctx.input);
|
|
210
|
+
if (id === "") {
|
|
211
|
+
throw invalidArgumentError("resource id is required");
|
|
212
|
+
}
|
|
213
|
+
ctx.set(TARGET_RESOURCE_KEY, await readClient(clients, id));
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** Loads the client an org/slug reference names into TARGET_RESOURCE_KEY, with the cloud's copy. */
|
|
218
|
+
export function newLoadClientByReferenceStep(clients) {
|
|
219
|
+
return {
|
|
220
|
+
name: "LoadByReference",
|
|
221
|
+
async execute(ctx) {
|
|
222
|
+
const ref = ctx.input;
|
|
223
|
+
if (ref.slug === "") {
|
|
224
|
+
throw invalidArgumentError("slug is required in reference");
|
|
225
|
+
}
|
|
226
|
+
if (ref.kind !== ApiResourceKind.api_resource_kind_unknown &&
|
|
227
|
+
ref.kind !== ApiResourceKind.platform_client) {
|
|
228
|
+
throw invalidArgumentError(`kind mismatch: expected platform_client, got ${ApiResourceKind[ref.kind] ?? String(ref.kind)}`);
|
|
229
|
+
}
|
|
230
|
+
requireOrgForReference(ApiResourceKind.platform_client, ref.org);
|
|
231
|
+
const client = await clients.findByOrgAndSlug(ref.org, ref.slug);
|
|
232
|
+
if (client === undefined) {
|
|
233
|
+
throw new ConnectError(referenceNotFoundMessage(ref.org, ref.slug), Code.NotFound);
|
|
234
|
+
}
|
|
235
|
+
ctx.set(TARGET_RESOURCE_KEY, client);
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Replaces the loaded client's secret in place (the client_id is
|
|
241
|
+
* permanent), stamps the update audit as the caller, and parks the
|
|
242
|
+
* plaintext for the response. The expiry is left as it is: rotating a
|
|
243
|
+
* secret does not extend a client the owner set to expire.
|
|
244
|
+
*/
|
|
245
|
+
export function newRotateClientCredentialsStep() {
|
|
246
|
+
return {
|
|
247
|
+
name: "RotateClientCredentials",
|
|
248
|
+
execute(ctx) {
|
|
249
|
+
const client = storedClientOf(ctx, TARGET_RESOURCE_KEY);
|
|
250
|
+
const spec = specOf(client);
|
|
251
|
+
const secret = generateClientSecret();
|
|
252
|
+
spec.clientSecretHash = hashClientSecret(secret);
|
|
253
|
+
spec.secretFingerprint = secretFingerprint(secret);
|
|
254
|
+
// The row is its own "existing": the creation stamp is read before
|
|
255
|
+
// the fresh audit block replaces it.
|
|
256
|
+
updateAuditFields(PlatformClientSchema, client, client, ctx.callerIdentity);
|
|
257
|
+
ctx.set(CLIENT_SECRET_PLAINTEXT_KEY, secret);
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/** Writes the rotated TARGET_RESOURCE_KEY client through the port. */
|
|
262
|
+
export function newPersistTargetClientStep(clients) {
|
|
263
|
+
return {
|
|
264
|
+
name: "Persist",
|
|
265
|
+
async execute(ctx) {
|
|
266
|
+
try {
|
|
267
|
+
await clients.update(storedClientOf(ctx, TARGET_RESOURCE_KEY));
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
throw internalError(error, "failed to save platform client");
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// Delete (ApiResourceDeleteInput; the handler sets RESOURCE_ID_KEY)
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
export function newLoadExistingClientForDeleteStep(clients) {
|
|
279
|
+
return {
|
|
280
|
+
name: "LoadExistingForDelete",
|
|
281
|
+
async execute(ctx) {
|
|
282
|
+
ctx.set(EXISTING_RESOURCE_KEY, await readClient(clients, requireResourceId(ctx)));
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
export function newDeleteClientStep(clients) {
|
|
287
|
+
return {
|
|
288
|
+
name: "DeleteResource",
|
|
289
|
+
async execute(ctx) {
|
|
290
|
+
try {
|
|
291
|
+
await clients.deleteById(requireResourceId(ctx));
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
throw internalError(error, "failed to delete platform client");
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// Shared readers
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
/** The client by id, or the shared NOT_FOUND; any other fault propagates. */
|
|
303
|
+
async function readClient(clients, id) {
|
|
304
|
+
const client = await clients.findById(id);
|
|
305
|
+
if (client === undefined) {
|
|
306
|
+
throw notFoundError(PLATFORM_CLIENT_KIND_NAME, id);
|
|
307
|
+
}
|
|
308
|
+
return client;
|
|
309
|
+
}
|
|
310
|
+
/** The client a load step stashed under `key`, or an ordering fault. */
|
|
311
|
+
export function storedClientOf(ctx, key) {
|
|
312
|
+
const client = ctx.get(key);
|
|
313
|
+
if (client === undefined) {
|
|
314
|
+
throw internalError(new Error(`no platform client under '${key}' — its load step must run first`), "platform client chain ordering");
|
|
315
|
+
}
|
|
316
|
+
return client;
|
|
317
|
+
}
|
|
318
|
+
function requireMetadata(ctx, operation) {
|
|
319
|
+
const metadata = metadataOf(ctx.newState);
|
|
320
|
+
if (metadata === undefined) {
|
|
321
|
+
throw internalError(new Error("platform client metadata is nil"), operation);
|
|
322
|
+
}
|
|
323
|
+
return metadata;
|
|
324
|
+
}
|
|
325
|
+
/** The client's spec, created in place when the caller omitted it (every field defaults). */
|
|
326
|
+
function specOf(client) {
|
|
327
|
+
if (client.spec === undefined) {
|
|
328
|
+
client.spec = create(PlatformClientSpecSchema);
|
|
329
|
+
}
|
|
330
|
+
return client.spec;
|
|
331
|
+
}
|
|
332
|
+
function requireResourceId(ctx) {
|
|
333
|
+
const id = ctx.get(RESOURCE_ID_KEY);
|
|
334
|
+
if (typeof id !== "string" || id === "") {
|
|
335
|
+
throw internalError(new Error("resource id not found in context (the delete handler sets it)"), "platform client delete ordering");
|
|
336
|
+
}
|
|
337
|
+
return id;
|
|
338
|
+
}
|
|
339
|
+
//# sourceMappingURL=steps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../src/domain/platformclient/steps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PortContractCase, PortContractFixture } from "../../store/port-contract.js";
|
|
2
|
+
import type { PlatformClientStore } from "./store.js";
|
|
3
|
+
/** The runner's fixture over this port — the name a driver's test and the barrel know it by. */
|
|
4
|
+
export type PlatformClientStoreContractFixture = PortContractFixture<PlatformClientStore>;
|
|
5
|
+
export type PlatformClientStoreContractCase = PortContractCase;
|
|
6
|
+
/** The port's cases over `makeFixture`, one fresh fixture per case. */
|
|
7
|
+
export declare function platformClientStoreContract(makeFixture: () => Promise<PlatformClientStoreContractFixture>): ReadonlyArray<PlatformClientStoreContractCase>;
|
|
8
|
+
//# sourceMappingURL=store-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-contract.d.ts","sourceRoot":"","sources":["../../../src/domain/platformclient/store-contract.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EACV,gBAAgB,EAEhB,mBAAmB,EACpB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,gGAAgG;AAChG,MAAM,MAAM,kCAAkC,GAC5C,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;AAE3C,MAAM,MAAM,+BAA+B,GAAG,gBAAgB,CAAC;AA2Q/D,uEAAuE;AACvE,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,OAAO,CAAC,kCAAkC,CAAC,GAC7D,aAAa,CAAC,+BAA+B,CAAC,CAEhD"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PlatformClientStore PORT-CONTRACT KIT: every behavior an
|
|
3
|
+
* implementation of store.ts must satisfy identically, as cases a driver's
|
|
4
|
+
* test iterates. Open source runs them over its own adapter
|
|
5
|
+
* (resource-store.ts) on sqlite and Postgres in
|
|
6
|
+
* __tests__/resource-store.test.ts; a composition runs the SAME cases over
|
|
7
|
+
* the store it registers as `drivers.platformClientStore` (the cloud's
|
|
8
|
+
* `cloud.iam_platform_client` store), so "the port holds" is one statement
|
|
9
|
+
* proven per driver, never restated per repository.
|
|
10
|
+
*
|
|
11
|
+
* Shape: declarations over the port-contract runner (store/port-contract.ts),
|
|
12
|
+
* which owns the fresh fixture per case, the cleanup, and the reason the
|
|
13
|
+
* kit returns cases instead of calling vitest's `describe`. The
|
|
14
|
+
* disconnected-store case matters most here: the verifier reads this port
|
|
15
|
+
* on every request a minted token bears, so an outage that read as "no
|
|
16
|
+
* client" would revoke every live token instead of failing loudly.
|
|
17
|
+
*
|
|
18
|
+
* The duplicate arms check `instanceof DuplicatePlatformClientError` on the
|
|
19
|
+
* exported class, because that is how the create chain's Persist step
|
|
20
|
+
* catches it: a driver throwing a lookalike fails here for the reason it
|
|
21
|
+
* would fail a create.
|
|
22
|
+
*
|
|
23
|
+
* Case names are the contract lines, in the port's words. The OSS test pins
|
|
24
|
+
* the full list, so a case cannot drop out of the kit unnoticed.
|
|
25
|
+
*/
|
|
26
|
+
import assert from "node:assert/strict";
|
|
27
|
+
import { create, equals } from "@bufbuild/protobuf";
|
|
28
|
+
import { PlatformClientSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
29
|
+
import { portContractCases } from "../../store/port-contract.js";
|
|
30
|
+
import { DuplicatePlatformClientError } from "./store.js";
|
|
31
|
+
/** A client as the domain writes it; each fixture names its own id, org, slug and client_id. */
|
|
32
|
+
function platformClient(fields) {
|
|
33
|
+
return create(PlatformClientSchema, {
|
|
34
|
+
apiVersion: "iam.stigmer.ai/v1",
|
|
35
|
+
kind: "PlatformClient",
|
|
36
|
+
metadata: {
|
|
37
|
+
id: fields.id,
|
|
38
|
+
name: fields.slug,
|
|
39
|
+
slug: fields.slug,
|
|
40
|
+
org: fields.org,
|
|
41
|
+
},
|
|
42
|
+
spec: {
|
|
43
|
+
clientId: fields.clientId,
|
|
44
|
+
clientSecretHash: `hash-of-${fields.clientId}`,
|
|
45
|
+
secretFingerprint: "abcdef",
|
|
46
|
+
autoProvisionAccounts: true,
|
|
47
|
+
allowedOrigins: fields.allowedOrigins ?? [],
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function assertSameClient(actual, expected, message) {
|
|
52
|
+
assert.ok(actual !== undefined, `${message}: no client was answered`);
|
|
53
|
+
assert.ok(equals(PlatformClientSchema, actual, expected), message);
|
|
54
|
+
}
|
|
55
|
+
async function assertDuplicate(write, message) {
|
|
56
|
+
await assert.rejects(write, (error) => error instanceof DuplicatePlatformClientError, message);
|
|
57
|
+
}
|
|
58
|
+
const ACME_DASHBOARD = platformClient({
|
|
59
|
+
id: "pcl_contract_acme_dashboard",
|
|
60
|
+
org: "acme",
|
|
61
|
+
slug: "dashboard",
|
|
62
|
+
clientId: "stgm_cid_contract_acme_dashboard",
|
|
63
|
+
});
|
|
64
|
+
const CASES = [
|
|
65
|
+
[
|
|
66
|
+
"save then findById, findByClientId and findByOrgAndSlug answer the saved client",
|
|
67
|
+
async ({ store }) => {
|
|
68
|
+
await store.save(ACME_DASHBOARD);
|
|
69
|
+
assertSameClient(await store.findById("pcl_contract_acme_dashboard"), ACME_DASHBOARD, "findById must answer the saved row");
|
|
70
|
+
assertSameClient(await store.findByClientId("stgm_cid_contract_acme_dashboard"), ACME_DASHBOARD, "findByClientId must answer the saved row");
|
|
71
|
+
assertSameClient(await store.findByOrgAndSlug("acme", "dashboard"), ACME_DASHBOARD, "findByOrgAndSlug must answer the saved row");
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
"lookups of a client that is not held answer undefined",
|
|
76
|
+
async ({ store }) => {
|
|
77
|
+
await store.save(ACME_DASHBOARD);
|
|
78
|
+
assert.equal(await store.findById("pcl_contract_nobody"), undefined);
|
|
79
|
+
assert.equal(await store.findByClientId("stgm_cid_contract_nobody"), undefined);
|
|
80
|
+
assert.equal(await store.findByOrgAndSlug("acme", "nobody"), undefined);
|
|
81
|
+
assert.equal(await store.findByOrgAndSlug("globex", "dashboard"), undefined, "a slug is scoped to its organization: another org's lookup must miss");
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
[
|
|
85
|
+
"save under a held id raises DuplicatePlatformClientError",
|
|
86
|
+
async ({ store }) => {
|
|
87
|
+
await store.save(ACME_DASHBOARD);
|
|
88
|
+
await assertDuplicate(store.save(platformClient({
|
|
89
|
+
id: "pcl_contract_acme_dashboard",
|
|
90
|
+
org: "acme",
|
|
91
|
+
slug: "other",
|
|
92
|
+
clientId: "stgm_cid_contract_other",
|
|
93
|
+
})), "a held id must be refused, never overwritten");
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
"save under a held (org, slug) raises DuplicatePlatformClientError",
|
|
98
|
+
async ({ store }) => {
|
|
99
|
+
await store.save(ACME_DASHBOARD);
|
|
100
|
+
await assertDuplicate(store.save(platformClient({
|
|
101
|
+
id: "pcl_contract_acme_second",
|
|
102
|
+
org: "acme",
|
|
103
|
+
slug: "dashboard",
|
|
104
|
+
clientId: "stgm_cid_contract_acme_second",
|
|
105
|
+
})), "a held (org, slug) must be refused");
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
[
|
|
109
|
+
"save under a held client_id raises DuplicatePlatformClientError",
|
|
110
|
+
async ({ store }) => {
|
|
111
|
+
await store.save(ACME_DASHBOARD);
|
|
112
|
+
await assertDuplicate(store.save(platformClient({
|
|
113
|
+
id: "pcl_contract_acme_third",
|
|
114
|
+
org: "acme",
|
|
115
|
+
slug: "third",
|
|
116
|
+
clientId: "stgm_cid_contract_acme_dashboard",
|
|
117
|
+
})), "a held client_id must be refused: the mint resolves a client by it");
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
[
|
|
121
|
+
"the same slug in another organization is a different client",
|
|
122
|
+
async ({ store }) => {
|
|
123
|
+
await store.save(ACME_DASHBOARD);
|
|
124
|
+
const globex = platformClient({
|
|
125
|
+
id: "pcl_contract_globex_dashboard",
|
|
126
|
+
org: "globex",
|
|
127
|
+
slug: "dashboard",
|
|
128
|
+
clientId: "stgm_cid_contract_globex_dashboard",
|
|
129
|
+
});
|
|
130
|
+
await store.save(globex);
|
|
131
|
+
assertSameClient(await store.findByOrgAndSlug("globex", "dashboard"), globex, "each organization answers its own client for the shared slug");
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
[
|
|
135
|
+
"update replaces the row in place",
|
|
136
|
+
async ({ store }) => {
|
|
137
|
+
await store.save(ACME_DASHBOARD);
|
|
138
|
+
const edited = platformClient({
|
|
139
|
+
id: "pcl_contract_acme_dashboard",
|
|
140
|
+
org: "acme",
|
|
141
|
+
slug: "dashboard",
|
|
142
|
+
clientId: "stgm_cid_contract_acme_dashboard",
|
|
143
|
+
allowedOrigins: ["https://app.acme.example"],
|
|
144
|
+
});
|
|
145
|
+
await store.update(edited);
|
|
146
|
+
assertSameClient(await store.findById("pcl_contract_acme_dashboard"), edited, "findById must answer the replaced row");
|
|
147
|
+
assertSameClient(await store.findByClientId("stgm_cid_contract_acme_dashboard"), edited, "findByClientId must answer the replaced row");
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
[
|
|
151
|
+
"update of an unknown id writes nothing",
|
|
152
|
+
async ({ store }) => {
|
|
153
|
+
await store.update(ACME_DASHBOARD);
|
|
154
|
+
assert.equal(await store.findById("pcl_contract_acme_dashboard"), undefined, "update must replace, never create");
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
"deleteById frees the id, the client_id and the slug; deleting an unknown id resolves",
|
|
159
|
+
async ({ store }) => {
|
|
160
|
+
await store.save(ACME_DASHBOARD);
|
|
161
|
+
await store.deleteById("pcl_contract_acme_dashboard");
|
|
162
|
+
assert.equal(await store.findById("pcl_contract_acme_dashboard"), undefined);
|
|
163
|
+
assert.equal(await store.findByClientId("stgm_cid_contract_acme_dashboard"), undefined, "a deleted client's client_id must stop resolving: the mint and the verifier read it");
|
|
164
|
+
assert.equal(await store.findByOrgAndSlug("acme", "dashboard"), undefined);
|
|
165
|
+
await store.save(ACME_DASHBOARD);
|
|
166
|
+
await store.deleteById("pcl_contract_nobody");
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
"findByOrg answers every client of the organization and no other",
|
|
171
|
+
async ({ store }) => {
|
|
172
|
+
await store.save(ACME_DASHBOARD);
|
|
173
|
+
await store.save(platformClient({
|
|
174
|
+
id: "pcl_contract_acme_mobile",
|
|
175
|
+
org: "acme",
|
|
176
|
+
slug: "mobile",
|
|
177
|
+
clientId: "stgm_cid_contract_acme_mobile",
|
|
178
|
+
}));
|
|
179
|
+
await store.save(platformClient({
|
|
180
|
+
id: "pcl_contract_globex_dashboard",
|
|
181
|
+
org: "globex",
|
|
182
|
+
slug: "dashboard",
|
|
183
|
+
clientId: "stgm_cid_contract_globex_dashboard",
|
|
184
|
+
}));
|
|
185
|
+
const ids = (await store.findByOrg("acme"))
|
|
186
|
+
.map((client) => client.metadata?.id ?? "")
|
|
187
|
+
.sort();
|
|
188
|
+
assert.deepEqual(ids, [
|
|
189
|
+
"pcl_contract_acme_dashboard",
|
|
190
|
+
"pcl_contract_acme_mobile",
|
|
191
|
+
]);
|
|
192
|
+
assert.deepEqual(await store.findByOrg("initech"), []);
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
[
|
|
196
|
+
"a disconnected store is an infrastructure fault, never 'not found'",
|
|
197
|
+
async (fixture) => {
|
|
198
|
+
await fixture.disconnect();
|
|
199
|
+
await assert.rejects(fixture.store.findById("pcl_contract_acme_dashboard"), "an id read on a disconnected store must reject: on the verifier's path 'not found' revokes every token");
|
|
200
|
+
await assert.rejects(fixture.store.findByClientId("stgm_cid_contract_acme_dashboard"), "a client_id read on a disconnected store must reject, never read as undefined");
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
];
|
|
204
|
+
/** The port's cases over `makeFixture`, one fresh fixture per case. */
|
|
205
|
+
export function platformClientStoreContract(makeFixture) {
|
|
206
|
+
return portContractCases(CASES, makeFixture);
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=store-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-contract.js","sourceRoot":"","sources":["../../../src/domain/platformclient/store-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PlatformClientStore PORT: the storage contract the platform-client
|
|
3
|
+
* domain writes and reads through, so the cloud serves the same chains
|
|
4
|
+
* over its own `cloud.iam_platform_client` table by registering an
|
|
5
|
+
* implementation as `drivers.platformClientStore` (the identity-account
|
|
6
|
+
* and IamPolicy precedent: a port for an IAM kind whose rows an edition
|
|
7
|
+
* keeps in its own table). Open source's implementation is
|
|
8
|
+
* resource-store.ts over the generic Store.
|
|
9
|
+
*
|
|
10
|
+
* The methods are exactly the lookups the domain asks: by id (every
|
|
11
|
+
* chain, and the verifier's liveness read), by client_id (the mint), by
|
|
12
|
+
* org-scoped slug (getByReference, update addressed by slug, the
|
|
13
|
+
* duplicate check), by organization (listByOrg). A port carries nothing
|
|
14
|
+
* only one edition calls.
|
|
15
|
+
*
|
|
16
|
+
* Contract every implementation must satisfy — proven by the port-contract
|
|
17
|
+
* kit (store-contract.ts, exported), which the OSS adapter's test iterates
|
|
18
|
+
* on both drivers and a composition's driver test iterates too:
|
|
19
|
+
* - `save` is create-only in effect: a held id, a held (org, slug) or a
|
|
20
|
+
* held client_id raises DuplicatePlatformClientError, never a silent
|
|
21
|
+
* overwrite;
|
|
22
|
+
* - `update` replaces by id and never creates: an unknown id writes
|
|
23
|
+
* nothing;
|
|
24
|
+
* - `deleteById` of an unknown id resolves;
|
|
25
|
+
* - `findByOrg` answers every client of the organization and no other,
|
|
26
|
+
* in no promised order (the chain sorts);
|
|
27
|
+
* - a typed not-found reads as `undefined`; any other storage failure
|
|
28
|
+
* propagates as the infrastructure fault it is (the ratified
|
|
29
|
+
* store-fault mapping — an outage must never read as "no client", which
|
|
30
|
+
* on the verifier's path would revoke every live token).
|
|
31
|
+
*/
|
|
32
|
+
import type { PlatformClient } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
33
|
+
/**
|
|
34
|
+
* Raised by `save` when the id, the (org, slug) pair or the client_id is
|
|
35
|
+
* already held, so the create chain answers ALREADY_EXISTS. `cause`
|
|
36
|
+
* carries a driver's own error (the cloud's SQLSTATE 23505) for the logs;
|
|
37
|
+
* the message is the port's.
|
|
38
|
+
*/
|
|
39
|
+
export declare class DuplicatePlatformClientError extends Error {
|
|
40
|
+
constructor(message: string, options?: {
|
|
41
|
+
cause?: unknown;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export interface PlatformClientStore {
|
|
45
|
+
/** Insert; a held id, (org, slug) or client_id raises DuplicatePlatformClientError. */
|
|
46
|
+
save(client: PlatformClient): Promise<void>;
|
|
47
|
+
/** Full-row replace by id; an unknown id writes nothing. */
|
|
48
|
+
update(client: PlatformClient): Promise<void>;
|
|
49
|
+
/** Removes the row; no error when it does not exist. */
|
|
50
|
+
deleteById(id: string): Promise<void>;
|
|
51
|
+
findById(id: string): Promise<PlatformClient | undefined>;
|
|
52
|
+
/** The client holding this public identifier — the mint's lookup. */
|
|
53
|
+
findByClientId(clientId: string): Promise<PlatformClient | undefined>;
|
|
54
|
+
findByOrgAndSlug(org: string, slug: string): Promise<PlatformClient | undefined>;
|
|
55
|
+
/** Every client of the organization, in no promised order. */
|
|
56
|
+
findByOrg(org: string): Promise<ReadonlyArray<PlatformClient>>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/domain/platformclient/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yDAAyD,CAAC;AAE9F;;;;;GAKG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,4DAA4D;IAC5D,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,wDAAwD;IACxD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAC1D,qEAAqE;IACrE,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACtE,gBAAgB,CACd,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACvC,8DAA8D;IAC9D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC;CAChE"}
|