@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,472 @@
|
|
|
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 type { DescMessage } from "@bufbuild/protobuf";
|
|
39
|
+
|
|
40
|
+
import type { ApiResourceReferenceSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
|
|
41
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
42
|
+
import type { PlatformClient } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
43
|
+
import { PlatformClientSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
44
|
+
import type { PlatformClientSpec } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/spec_pb";
|
|
45
|
+
import { PlatformClientSpecSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/spec_pb";
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
RESERVED_LABEL_TRUE,
|
|
49
|
+
SYSTEM_MANAGED_LABEL,
|
|
50
|
+
} from "../../pipeline/apiresource-labels.js";
|
|
51
|
+
import {
|
|
52
|
+
alreadyExistsError,
|
|
53
|
+
failedPreconditionError,
|
|
54
|
+
internalError,
|
|
55
|
+
invalidArgumentError,
|
|
56
|
+
notFoundError,
|
|
57
|
+
} from "../../pipeline/errors.js";
|
|
58
|
+
import type { PipelineStep } from "../../pipeline/pipeline.js";
|
|
59
|
+
import type { RequestContext } from "../../pipeline/request-context.js";
|
|
60
|
+
import { updateAuditFields } from "../../pipeline/steps/build-update-state.js";
|
|
61
|
+
import { RESOURCE_ID_KEY } from "../../pipeline/steps/delete.js";
|
|
62
|
+
import { requireOrgForReference } from "../../pipeline/steps/helpers.js";
|
|
63
|
+
import { EXISTING_RESOURCE_KEY } from "../../pipeline/steps/load-existing.js";
|
|
64
|
+
import { TARGET_RESOURCE_KEY } from "../../pipeline/steps/load-target.js";
|
|
65
|
+
import { idValueOf, metadataOf } from "../../pipeline/steps/shapes.js";
|
|
66
|
+
import {
|
|
67
|
+
PLATFORM_CLIENT_KIND_NAME,
|
|
68
|
+
RESERVED_PLATFORM_CLIENT_SLUGS,
|
|
69
|
+
referenceNotFoundMessage,
|
|
70
|
+
reservedSlugMessage,
|
|
71
|
+
systemManagedMessage,
|
|
72
|
+
} from "./constants.js";
|
|
73
|
+
import type { SystemManagedMutation } from "./constants.js";
|
|
74
|
+
import {
|
|
75
|
+
generateClientId,
|
|
76
|
+
generateClientSecret,
|
|
77
|
+
hashClientSecret,
|
|
78
|
+
secretFingerprint,
|
|
79
|
+
} from "./credentials.js";
|
|
80
|
+
import { DuplicatePlatformClientError } from "./store.js";
|
|
81
|
+
import type { PlatformClientStore } from "./store.js";
|
|
82
|
+
|
|
83
|
+
type ClientStep = PipelineStep<typeof PlatformClientSchema>;
|
|
84
|
+
type ClientContext = RequestContext<typeof PlatformClientSchema>;
|
|
85
|
+
|
|
86
|
+
/** Context key for the plaintext secret the create and rotate responses show once. */
|
|
87
|
+
export const CLIENT_SECRET_PLAINTEXT_KEY = "platformClientSecretPlaintext";
|
|
88
|
+
|
|
89
|
+
/** Clears the stored hash in place and answers the client — every response goes through it. */
|
|
90
|
+
export function redactPlatformClient(client: PlatformClient): PlatformClient {
|
|
91
|
+
if (client.spec !== undefined) {
|
|
92
|
+
client.spec.clientSecretHash = "";
|
|
93
|
+
}
|
|
94
|
+
return client;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The plaintext secret a credential step parked, or an ordering fault. */
|
|
98
|
+
export function parkedClientSecret<Desc extends DescMessage>(
|
|
99
|
+
ctx: RequestContext<Desc>,
|
|
100
|
+
): string {
|
|
101
|
+
const secret = ctx.get(CLIENT_SECRET_PLAINTEXT_KEY);
|
|
102
|
+
if (typeof secret !== "string" || secret === "") {
|
|
103
|
+
throw internalError(
|
|
104
|
+
new Error("no client secret parked — a credential step must run first"),
|
|
105
|
+
"platform client credentials",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return secret;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Create
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
export function newRefuseReservedSlugStep(): ClientStep {
|
|
116
|
+
return {
|
|
117
|
+
name: "RefuseReservedSlug",
|
|
118
|
+
execute(ctx: ClientContext): void {
|
|
119
|
+
const slug = metadataOf(ctx.newState)?.slug ?? "";
|
|
120
|
+
if (RESERVED_PLATFORM_CLIENT_SLUGS.has(slug)) {
|
|
121
|
+
throw invalidArgumentError(reservedSlugMessage(slug));
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Rejects a create whose slug the organization already holds. */
|
|
128
|
+
export function newCheckDuplicateStep(
|
|
129
|
+
clients: PlatformClientStore,
|
|
130
|
+
): ClientStep {
|
|
131
|
+
return {
|
|
132
|
+
name: "CheckDuplicate",
|
|
133
|
+
async execute(ctx: ClientContext): Promise<void> {
|
|
134
|
+
const metadata = requireMetadata(ctx, "duplicate check");
|
|
135
|
+
const existing = await clients.findByOrgAndSlug(
|
|
136
|
+
metadata.org,
|
|
137
|
+
metadata.slug,
|
|
138
|
+
);
|
|
139
|
+
if (existing !== undefined) {
|
|
140
|
+
throw alreadyExistsError(
|
|
141
|
+
PLATFORM_CLIENT_KIND_NAME,
|
|
142
|
+
`slug '${metadata.slug}' in org '${metadata.org}' (id: ${existing.metadata?.id ?? ""})`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Generates the credential fields after BuildNewState, parking the plaintext for the response. */
|
|
150
|
+
export function newGenerateClientCredentialsStep(): ClientStep {
|
|
151
|
+
return {
|
|
152
|
+
name: "GenerateClientCredentials",
|
|
153
|
+
execute(ctx: ClientContext): void {
|
|
154
|
+
const spec = specOf(ctx.newState);
|
|
155
|
+
const secret = generateClientSecret();
|
|
156
|
+
spec.clientId = generateClientId();
|
|
157
|
+
spec.clientSecretHash = hashClientSecret(secret);
|
|
158
|
+
spec.secretFingerprint = secretFingerprint(secret);
|
|
159
|
+
ctx.set(CLIENT_SECRET_PLAINTEXT_KEY, secret);
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Saves the new client through the port; a held slug or client_id is ALREADY_EXISTS. */
|
|
165
|
+
export function newPersistNewClientStep(
|
|
166
|
+
clients: PlatformClientStore,
|
|
167
|
+
): ClientStep {
|
|
168
|
+
return {
|
|
169
|
+
name: "Persist",
|
|
170
|
+
async execute(ctx: ClientContext): Promise<void> {
|
|
171
|
+
try {
|
|
172
|
+
await clients.save(ctx.newState);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error instanceof DuplicatePlatformClientError) {
|
|
175
|
+
// The race the duplicate check could not see.
|
|
176
|
+
const metadata = requireMetadata(ctx, "persist");
|
|
177
|
+
throw alreadyExistsError(
|
|
178
|
+
PLATFORM_CLIENT_KIND_NAME,
|
|
179
|
+
`slug '${metadata.slug}' in org '${metadata.org}'`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
throw internalError(error, "failed to save platform client");
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
// Update
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
/** Loads the client by id, else by org-scoped slug (the id written back), into EXISTING_RESOURCE_KEY. */
|
|
193
|
+
export function newLoadExistingClientStep(
|
|
194
|
+
clients: PlatformClientStore,
|
|
195
|
+
): ClientStep {
|
|
196
|
+
return {
|
|
197
|
+
name: "LoadExisting",
|
|
198
|
+
async execute(ctx: ClientContext): Promise<void> {
|
|
199
|
+
const metadata = requireMetadata(ctx, "load existing");
|
|
200
|
+
let existing: PlatformClient | undefined;
|
|
201
|
+
if (metadata.id !== "") {
|
|
202
|
+
existing = await clients.findById(metadata.id);
|
|
203
|
+
if (existing === undefined) {
|
|
204
|
+
throw notFoundError(PLATFORM_CLIENT_KIND_NAME, metadata.id);
|
|
205
|
+
}
|
|
206
|
+
} else if (metadata.slug !== "") {
|
|
207
|
+
existing = await clients.findByOrgAndSlug(metadata.org, metadata.slug);
|
|
208
|
+
if (existing === undefined) {
|
|
209
|
+
throw notFoundError(PLATFORM_CLIENT_KIND_NAME, metadata.slug);
|
|
210
|
+
}
|
|
211
|
+
metadata.id = existing.metadata?.id ?? "";
|
|
212
|
+
} else {
|
|
213
|
+
throw invalidArgumentError(
|
|
214
|
+
"resource id or slug is required for update",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
ctx.set(EXISTING_RESOURCE_KEY, existing);
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Restores the credential fields from the stored row after BuildUpdateState. */
|
|
223
|
+
export function newPreserveClientCredentialsStep(): ClientStep {
|
|
224
|
+
return {
|
|
225
|
+
name: "PreserveClientCredentials",
|
|
226
|
+
execute(ctx: ClientContext): void {
|
|
227
|
+
const existing = storedClientOf(ctx, EXISTING_RESOURCE_KEY);
|
|
228
|
+
const spec = specOf(ctx.newState);
|
|
229
|
+
spec.clientId = existing.spec?.clientId ?? "";
|
|
230
|
+
spec.clientSecretHash = existing.spec?.clientSecretHash ?? "";
|
|
231
|
+
spec.secretFingerprint = existing.spec?.secretFingerprint ?? "";
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Replaces the row through the port. */
|
|
237
|
+
export function newPersistUpdatedClientStep(
|
|
238
|
+
clients: PlatformClientStore,
|
|
239
|
+
): ClientStep {
|
|
240
|
+
return {
|
|
241
|
+
name: "Persist",
|
|
242
|
+
async execute(ctx: ClientContext): Promise<void> {
|
|
243
|
+
try {
|
|
244
|
+
await clients.update(ctx.newState);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
throw internalError(error, "failed to save platform client");
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
// Update, delete and rotate: the platform's own clients
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
/** Refuses a mutation of a client carrying the system-managed label, read from `key`. */
|
|
257
|
+
export function newRefuseSystemManagedStep<Desc extends DescMessage>(
|
|
258
|
+
verb: SystemManagedMutation,
|
|
259
|
+
key: typeof EXISTING_RESOURCE_KEY | typeof TARGET_RESOURCE_KEY,
|
|
260
|
+
): PipelineStep<Desc> {
|
|
261
|
+
return {
|
|
262
|
+
name: "RefuseSystemManaged",
|
|
263
|
+
execute(ctx: RequestContext<Desc>): void {
|
|
264
|
+
const client = storedClientOf(ctx, key);
|
|
265
|
+
if (
|
|
266
|
+
client.metadata?.labels[SYSTEM_MANAGED_LABEL] === RESERVED_LABEL_TRUE
|
|
267
|
+
) {
|
|
268
|
+
throw failedPreconditionError(systemManagedMessage(verb));
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
// Get, getByReference, rotate
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
|
|
278
|
+
/** Loads the client named by an ID-wrapper input into TARGET_RESOURCE_KEY. */
|
|
279
|
+
export function newLoadTargetClientStep<InputDesc extends DescMessage>(
|
|
280
|
+
clients: PlatformClientStore,
|
|
281
|
+
): PipelineStep<InputDesc> {
|
|
282
|
+
return {
|
|
283
|
+
name: "LoadTarget",
|
|
284
|
+
async execute(ctx: RequestContext<InputDesc>): Promise<void> {
|
|
285
|
+
const id = idValueOf(ctx.input);
|
|
286
|
+
if (id === "") {
|
|
287
|
+
throw invalidArgumentError("resource id is required");
|
|
288
|
+
}
|
|
289
|
+
ctx.set(TARGET_RESOURCE_KEY, await readClient(clients, id));
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Loads the client an org/slug reference names into TARGET_RESOURCE_KEY, with the cloud's copy. */
|
|
295
|
+
export function newLoadClientByReferenceStep(
|
|
296
|
+
clients: PlatformClientStore,
|
|
297
|
+
): PipelineStep<typeof ApiResourceReferenceSchema> {
|
|
298
|
+
return {
|
|
299
|
+
name: "LoadByReference",
|
|
300
|
+
async execute(
|
|
301
|
+
ctx: RequestContext<typeof ApiResourceReferenceSchema>,
|
|
302
|
+
): Promise<void> {
|
|
303
|
+
const ref = ctx.input;
|
|
304
|
+
if (ref.slug === "") {
|
|
305
|
+
throw invalidArgumentError("slug is required in reference");
|
|
306
|
+
}
|
|
307
|
+
if (
|
|
308
|
+
ref.kind !== ApiResourceKind.api_resource_kind_unknown &&
|
|
309
|
+
ref.kind !== ApiResourceKind.platform_client
|
|
310
|
+
) {
|
|
311
|
+
throw invalidArgumentError(
|
|
312
|
+
`kind mismatch: expected platform_client, got ${ApiResourceKind[ref.kind] ?? String(ref.kind)}`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
requireOrgForReference(ApiResourceKind.platform_client, ref.org);
|
|
316
|
+
const client = await clients.findByOrgAndSlug(ref.org, ref.slug);
|
|
317
|
+
if (client === undefined) {
|
|
318
|
+
throw new ConnectError(
|
|
319
|
+
referenceNotFoundMessage(ref.org, ref.slug),
|
|
320
|
+
Code.NotFound,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
ctx.set(TARGET_RESOURCE_KEY, client);
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Replaces the loaded client's secret in place (the client_id is
|
|
330
|
+
* permanent), stamps the update audit as the caller, and parks the
|
|
331
|
+
* plaintext for the response. The expiry is left as it is: rotating a
|
|
332
|
+
* secret does not extend a client the owner set to expire.
|
|
333
|
+
*/
|
|
334
|
+
export function newRotateClientCredentialsStep<
|
|
335
|
+
InputDesc extends DescMessage,
|
|
336
|
+
>(): PipelineStep<InputDesc> {
|
|
337
|
+
return {
|
|
338
|
+
name: "RotateClientCredentials",
|
|
339
|
+
execute(ctx: RequestContext<InputDesc>): void {
|
|
340
|
+
const client = storedClientOf(ctx, TARGET_RESOURCE_KEY);
|
|
341
|
+
const spec = specOf(client);
|
|
342
|
+
const secret = generateClientSecret();
|
|
343
|
+
spec.clientSecretHash = hashClientSecret(secret);
|
|
344
|
+
spec.secretFingerprint = secretFingerprint(secret);
|
|
345
|
+
// The row is its own "existing": the creation stamp is read before
|
|
346
|
+
// the fresh audit block replaces it.
|
|
347
|
+
updateAuditFields(
|
|
348
|
+
PlatformClientSchema,
|
|
349
|
+
client,
|
|
350
|
+
client,
|
|
351
|
+
ctx.callerIdentity,
|
|
352
|
+
);
|
|
353
|
+
ctx.set(CLIENT_SECRET_PLAINTEXT_KEY, secret);
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Writes the rotated TARGET_RESOURCE_KEY client through the port. */
|
|
359
|
+
export function newPersistTargetClientStep<InputDesc extends DescMessage>(
|
|
360
|
+
clients: PlatformClientStore,
|
|
361
|
+
): PipelineStep<InputDesc> {
|
|
362
|
+
return {
|
|
363
|
+
name: "Persist",
|
|
364
|
+
async execute(ctx: RequestContext<InputDesc>): Promise<void> {
|
|
365
|
+
try {
|
|
366
|
+
await clients.update(storedClientOf(ctx, TARGET_RESOURCE_KEY));
|
|
367
|
+
} catch (error) {
|
|
368
|
+
throw internalError(error, "failed to save platform client");
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ---------------------------------------------------------------------------
|
|
375
|
+
// Delete (ApiResourceDeleteInput; the handler sets RESOURCE_ID_KEY)
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
|
|
378
|
+
export function newLoadExistingClientForDeleteStep<
|
|
379
|
+
InputDesc extends DescMessage,
|
|
380
|
+
>(clients: PlatformClientStore): PipelineStep<InputDesc> {
|
|
381
|
+
return {
|
|
382
|
+
name: "LoadExistingForDelete",
|
|
383
|
+
async execute(ctx: RequestContext<InputDesc>): Promise<void> {
|
|
384
|
+
ctx.set(
|
|
385
|
+
EXISTING_RESOURCE_KEY,
|
|
386
|
+
await readClient(clients, requireResourceId(ctx)),
|
|
387
|
+
);
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export function newDeleteClientStep<InputDesc extends DescMessage>(
|
|
393
|
+
clients: PlatformClientStore,
|
|
394
|
+
): PipelineStep<InputDesc> {
|
|
395
|
+
return {
|
|
396
|
+
name: "DeleteResource",
|
|
397
|
+
async execute(ctx: RequestContext<InputDesc>): Promise<void> {
|
|
398
|
+
try {
|
|
399
|
+
await clients.deleteById(requireResourceId(ctx));
|
|
400
|
+
} catch (error) {
|
|
401
|
+
throw internalError(error, "failed to delete platform client");
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
// Shared readers
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
410
|
+
|
|
411
|
+
/** The client by id, or the shared NOT_FOUND; any other fault propagates. */
|
|
412
|
+
async function readClient(
|
|
413
|
+
clients: PlatformClientStore,
|
|
414
|
+
id: string,
|
|
415
|
+
): Promise<PlatformClient> {
|
|
416
|
+
const client = await clients.findById(id);
|
|
417
|
+
if (client === undefined) {
|
|
418
|
+
throw notFoundError(PLATFORM_CLIENT_KIND_NAME, id);
|
|
419
|
+
}
|
|
420
|
+
return client;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** The client a load step stashed under `key`, or an ordering fault. */
|
|
424
|
+
export function storedClientOf<Desc extends DescMessage>(
|
|
425
|
+
ctx: RequestContext<Desc>,
|
|
426
|
+
key: string,
|
|
427
|
+
): PlatformClient {
|
|
428
|
+
const client = ctx.get(key) as PlatformClient | undefined;
|
|
429
|
+
if (client === undefined) {
|
|
430
|
+
throw internalError(
|
|
431
|
+
new Error(
|
|
432
|
+
`no platform client under '${key}' — its load step must run first`,
|
|
433
|
+
),
|
|
434
|
+
"platform client chain ordering",
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
return client;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function requireMetadata(ctx: ClientContext, operation: string) {
|
|
441
|
+
const metadata = metadataOf(ctx.newState);
|
|
442
|
+
if (metadata === undefined) {
|
|
443
|
+
throw internalError(
|
|
444
|
+
new Error("platform client metadata is nil"),
|
|
445
|
+
operation,
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
return metadata;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** The client's spec, created in place when the caller omitted it (every field defaults). */
|
|
452
|
+
function specOf(client: PlatformClient): PlatformClientSpec {
|
|
453
|
+
if (client.spec === undefined) {
|
|
454
|
+
client.spec = create(PlatformClientSpecSchema);
|
|
455
|
+
}
|
|
456
|
+
return client.spec;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function requireResourceId<Desc extends DescMessage>(
|
|
460
|
+
ctx: RequestContext<Desc>,
|
|
461
|
+
): string {
|
|
462
|
+
const id = ctx.get(RESOURCE_ID_KEY);
|
|
463
|
+
if (typeof id !== "string" || id === "") {
|
|
464
|
+
throw internalError(
|
|
465
|
+
new Error(
|
|
466
|
+
"resource id not found in context (the delete handler sets it)",
|
|
467
|
+
),
|
|
468
|
+
"platform client delete ordering",
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
return id;
|
|
472
|
+
}
|