@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
|
@@ -80,6 +80,8 @@ import {
|
|
|
80
80
|
} from "../../pipeline/steps/load-target.js";
|
|
81
81
|
import { newValidateProtoStep } from "../../pipeline/steps/validation.js";
|
|
82
82
|
import type { Store } from "../../store/interface.js";
|
|
83
|
+
import type { ListIndexRow } from "../../store/list-index.js";
|
|
84
|
+
import { artifactListIndex } from "./list-index.js";
|
|
83
85
|
import {
|
|
84
86
|
ARTIFACT_KIND,
|
|
85
87
|
DEFAULT_MAX_CONTENT_BYTES,
|
|
@@ -457,36 +459,44 @@ async function listByExecution(
|
|
|
457
459
|
.addStep({
|
|
458
460
|
name: "QueryArtifactsByExecution",
|
|
459
461
|
async execute(stepCtx): Promise<void> {
|
|
462
|
+
// The execution's artifacts through the list index's source key,
|
|
463
|
+
// whole and newest first: an execution's artifacts are returned as
|
|
464
|
+
// one (the request's paging fields are not read).
|
|
460
465
|
const input = stepCtx.input;
|
|
461
|
-
let
|
|
466
|
+
let rows: ListIndexRow[];
|
|
462
467
|
try {
|
|
463
|
-
|
|
468
|
+
rows = await deps.store.queryResources(artifactListIndex, {
|
|
469
|
+
anyKey: [
|
|
470
|
+
...(input.workflowExecutionId === ""
|
|
471
|
+
? []
|
|
472
|
+
: [
|
|
473
|
+
{
|
|
474
|
+
name: "workflow_execution" as const,
|
|
475
|
+
value: input.workflowExecutionId,
|
|
476
|
+
},
|
|
477
|
+
]),
|
|
478
|
+
...(input.agentExecutionId === ""
|
|
479
|
+
? []
|
|
480
|
+
: [
|
|
481
|
+
{
|
|
482
|
+
name: "agent_execution" as const,
|
|
483
|
+
value: input.agentExecutionId,
|
|
484
|
+
},
|
|
485
|
+
]),
|
|
486
|
+
],
|
|
487
|
+
});
|
|
464
488
|
} catch (error) {
|
|
465
489
|
throw internalError(error, "failed to list artifacts");
|
|
466
490
|
}
|
|
467
491
|
|
|
468
492
|
const artifacts: Artifact[] = [];
|
|
469
|
-
for (const
|
|
470
|
-
let artifact: Artifact;
|
|
493
|
+
for (const row of rows) {
|
|
471
494
|
try {
|
|
472
|
-
|
|
495
|
+
artifacts.push(fromBinary(ArtifactSchema, row.data));
|
|
473
496
|
} catch (error) {
|
|
474
497
|
deps.logger.warn("failed to unmarshal artifact, skipping", {
|
|
475
498
|
error: error instanceof Error ? error.message : String(error),
|
|
476
499
|
});
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
const source = artifact.spec?.source;
|
|
480
|
-
if (
|
|
481
|
-
input.workflowExecutionId !== "" &&
|
|
482
|
-
source?.workflowExecutionId === input.workflowExecutionId
|
|
483
|
-
) {
|
|
484
|
-
artifacts.push(artifact);
|
|
485
|
-
} else if (
|
|
486
|
-
input.agentExecutionId !== "" &&
|
|
487
|
-
source?.agentExecutionId === input.agentExecutionId
|
|
488
|
-
) {
|
|
489
|
-
artifacts.push(artifact);
|
|
490
500
|
}
|
|
491
501
|
}
|
|
492
502
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The artifact list index (store/list-index.ts): an artifact names the
|
|
3
|
+
* execution that produced it, agent or workflow, and
|
|
4
|
+
* `listByExecution` reads the one the request names.
|
|
5
|
+
*
|
|
6
|
+
* A change to `keys` bumps `revision` (boot/__tests__/list-indexes.test.ts
|
|
7
|
+
* pins the pair).
|
|
8
|
+
*/
|
|
9
|
+
import { ArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/artifact/v1/api_pb";
|
|
10
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
11
|
+
|
|
12
|
+
import { declareListIndex, field } from "../../store/list-index.js";
|
|
13
|
+
|
|
14
|
+
export const artifactListIndex = declareListIndex({
|
|
15
|
+
kind: ApiResourceKind.artifact,
|
|
16
|
+
schema: ArtifactSchema,
|
|
17
|
+
revision: 1,
|
|
18
|
+
keys: {
|
|
19
|
+
agent_execution: field("spec.source.agent_execution_id"),
|
|
20
|
+
workflow_execution: field("spec.source.workflow_execution_id"),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -639,6 +639,13 @@ describe("membership rules", () => {
|
|
|
639
639
|
IdentityAccountProvisioningMode.machine;
|
|
640
640
|
expect(isPersonAccount(machineMode)).toBe(false);
|
|
641
641
|
});
|
|
642
|
+
|
|
643
|
+
it("a platform-client end user is not: the mint grants it its client's role and nothing else", () => {
|
|
644
|
+
const endUser = account("stgm_pc|acme|user-7", OPERATOR_EMAIL);
|
|
645
|
+
endUser.spec!.provisioningMode =
|
|
646
|
+
IdentityAccountProvisioningMode.platform_client;
|
|
647
|
+
expect(isPersonAccount(endUser)).toBe(false);
|
|
648
|
+
});
|
|
642
649
|
});
|
|
643
650
|
|
|
644
651
|
describe("ensureRolesForExistingAccounts", () => {
|
|
@@ -195,14 +195,20 @@ export function isPersonStamp(stamp: string): boolean {
|
|
|
195
195
|
* `user`-class gate, for the reconciliation, which has a row and no
|
|
196
196
|
* caller. A machine account (`is_machine_account`, or the `machine`
|
|
197
197
|
* provisioning mode) earns no role, exactly as a `machine`-class caller
|
|
198
|
-
* earns none at the hook.
|
|
198
|
+
* earns none at the hook. Nor does a platform-client end user: it is a
|
|
199
|
+
* person on its platform's product, not on this install — the mint grants
|
|
200
|
+
* it exactly its client's auto-grant role on the owning organization and
|
|
201
|
+
* never runs these rules, and the reconciliation must not either, or every
|
|
202
|
+
* product user would become a member of every organization (and the
|
|
203
|
+
* operator-email arm would read an email the platform asserted).
|
|
199
204
|
*/
|
|
200
205
|
export function isPersonAccount(account: IdentityAccount): boolean {
|
|
201
206
|
const spec = account.spec;
|
|
202
207
|
return (
|
|
203
208
|
spec !== undefined &&
|
|
204
209
|
!spec.isMachineAccount &&
|
|
205
|
-
spec.provisioningMode !== IdentityAccountProvisioningMode.machine
|
|
210
|
+
spec.provisioningMode !== IdentityAccountProvisioningMode.machine &&
|
|
211
|
+
spec.provisioningMode !== IdentityAccountProvisioningMode.platform_client
|
|
206
212
|
);
|
|
207
213
|
}
|
|
208
214
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the identity-account create path's platform-client arm over the OSS
|
|
3
|
+
* adapter on sqlite:
|
|
4
|
+
* - `platform_client` provisioning writes the mode, the owning
|
|
5
|
+
* organization and the derived id, with the actor the caller named;
|
|
6
|
+
* - the `stgm_pc|` subject namespace is reserved in both directions: a
|
|
7
|
+
* direct account refuses it INVALID_ARGUMENT before anything is written,
|
|
8
|
+
* and a platform-client account without it is the caller's bug;
|
|
9
|
+
* - the direct lookups never answer the platform-client account, while the
|
|
10
|
+
* any-mode lookup (the mint's) does.
|
|
11
|
+
*/
|
|
12
|
+
import { Code, ConnectError } from "@connectrpc/connect";
|
|
13
|
+
import { create } from "@bufbuild/protobuf";
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
15
|
+
|
|
16
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
17
|
+
import { IdentityAccountProvisioningMode } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/enum_pb";
|
|
18
|
+
import { IdentityAccountSpecSchema } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/spec_pb";
|
|
19
|
+
|
|
20
|
+
import { silentLogger } from "../../../extensions/__tests__/composed-support.js";
|
|
21
|
+
import { serverActingFor } from "../../../pipeline/interceptors/auth.js";
|
|
22
|
+
import { newPermissiveSingleTeamAuthorizer } from "../../../pipeline/steps/authorize.js";
|
|
23
|
+
import { tempStore } from "../../../store/sqlite/__tests__/support.js";
|
|
24
|
+
import type { TempStore } from "../../../store/sqlite/__tests__/support.js";
|
|
25
|
+
import { accountIdFor, reservedSubjectMessage } from "../constants.js";
|
|
26
|
+
import { newCreateAccountPath } from "../controller.js";
|
|
27
|
+
import type { CreateAccount } from "../provisioning.js";
|
|
28
|
+
import { newResourceIdentityAccountStore } from "../resource-store.js";
|
|
29
|
+
import type { IdentityAccountStore } from "../store.js";
|
|
30
|
+
|
|
31
|
+
let temp: TempStore;
|
|
32
|
+
let accounts: IdentityAccountStore;
|
|
33
|
+
let createAccount: CreateAccount;
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
temp = tempStore();
|
|
37
|
+
accounts = newResourceIdentityAccountStore(temp.store);
|
|
38
|
+
createAccount = newCreateAccountPath(
|
|
39
|
+
{
|
|
40
|
+
accounts,
|
|
41
|
+
logger: silentLogger,
|
|
42
|
+
authorizer: newPermissiveSingleTeamAuthorizer(),
|
|
43
|
+
authorizationLifecycle: undefined,
|
|
44
|
+
},
|
|
45
|
+
ApiResourceKind.identity_account,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
temp.cleanup();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const SUBJECT = "stgm_pc|acme|user-7";
|
|
54
|
+
const CLIENT = serverActingFor("pcl_dashboard");
|
|
55
|
+
|
|
56
|
+
async function refusal(promise: Promise<unknown>): Promise<ConnectError> {
|
|
57
|
+
try {
|
|
58
|
+
await promise;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error instanceof ConnectError) return error;
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
throw new Error("expected a ConnectError refusal");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("the create path's platform-client arm", () => {
|
|
67
|
+
it("writes the mode, the owning organization and the derived id, stamped as the client", async () => {
|
|
68
|
+
const account = await createAccount(
|
|
69
|
+
{
|
|
70
|
+
name: "pat@example.com",
|
|
71
|
+
spec: create(IdentityAccountSpecSchema, {
|
|
72
|
+
idpId: SUBJECT,
|
|
73
|
+
email: "pat@example.com",
|
|
74
|
+
}),
|
|
75
|
+
provisioning: { mode: "platform_client", org: "acme" },
|
|
76
|
+
},
|
|
77
|
+
CLIENT,
|
|
78
|
+
);
|
|
79
|
+
expect(account.metadata?.id).toBe(accountIdFor(SUBJECT));
|
|
80
|
+
expect(account.metadata?.org).toBe("acme");
|
|
81
|
+
expect(account.spec?.provisioningMode).toBe(
|
|
82
|
+
IdentityAccountProvisioningMode.platform_client,
|
|
83
|
+
);
|
|
84
|
+
expect(account.status?.audit?.specAudit?.createdBy?.id).toBe(
|
|
85
|
+
"pcl_dashboard",
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(await accounts.findDirectByIdpId(SUBJECT)).toBeUndefined();
|
|
89
|
+
expect(await accounts.findDirectByEmail("pat@example.com")).toBeUndefined();
|
|
90
|
+
expect((await accounts.findByIdpId(SUBJECT))?.metadata?.id).toBe(
|
|
91
|
+
accountIdFor(SUBJECT),
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("refuses a direct account under the reserved subject prefix, writing nothing", async () => {
|
|
96
|
+
const denied = await refusal(
|
|
97
|
+
createAccount(
|
|
98
|
+
{
|
|
99
|
+
name: "impostor",
|
|
100
|
+
spec: create(IdentityAccountSpecSchema, { idpId: SUBJECT }),
|
|
101
|
+
provisioning: { mode: "direct" },
|
|
102
|
+
},
|
|
103
|
+
CLIENT,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
expect(denied.code).toBe(Code.InvalidArgument);
|
|
107
|
+
expect(denied.rawMessage).toBe(reservedSubjectMessage(SUBJECT));
|
|
108
|
+
expect(await accounts.findByIdpId(SUBJECT)).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("refuses a platform-client account without the reserved prefix as a server fault", async () => {
|
|
112
|
+
const denied = await refusal(
|
|
113
|
+
createAccount(
|
|
114
|
+
{
|
|
115
|
+
name: "stray",
|
|
116
|
+
spec: create(IdentityAccountSpecSchema, { idpId: "auth0|stray" }),
|
|
117
|
+
provisioning: { mode: "platform_client", org: "acme" },
|
|
118
|
+
},
|
|
119
|
+
CLIENT,
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
expect(denied.code).toBe(Code.Internal);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -54,6 +54,7 @@ const CONTRACT_CASE_NAMES = [
|
|
|
54
54
|
"a disconnected store is an infrastructure fault, never 'not found'",
|
|
55
55
|
"two concurrent saves of one subject end in fulfilments and DuplicateAccountErrors only, and the winner is readable by subject",
|
|
56
56
|
"findDirectByIdpId and findDirectByEmail never answer a federated account, while findByIdpId (any mode) does",
|
|
57
|
+
"findDirectByIdpId and findDirectByEmail never answer a platform-client account, while findByIdpId (any mode) does",
|
|
57
58
|
"findByIds answers one row per distinct id, in first-occurrence order; an empty request answers an empty list",
|
|
58
59
|
"update of an unknown id is a no-op: no row appears",
|
|
59
60
|
"deleteById of an unknown id resolves",
|
|
@@ -110,6 +110,26 @@ export function isMachineSubject(idpId: string): boolean {
|
|
|
110
110
|
return idpId.endsWith(MACHINE_ACCOUNT_SUFFIX);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* The subject namespace of PlatformClient end users:
|
|
115
|
+
* `stgm_pc|<org>|<external_user_id>` (the composite every client of one
|
|
116
|
+
* organization resolves a user to; domain/platformclient/mint.ts builds
|
|
117
|
+
* it). Reserved in both directions, because derived account ids make every
|
|
118
|
+
* subject one namespace: a direct account may not take a `stgm_pc|`
|
|
119
|
+
* subject (an issuer emitting one would otherwise provision an account the
|
|
120
|
+
* mint then resolves), and the mint refuses a `stgm_pc|` account in any
|
|
121
|
+
* other mode.
|
|
122
|
+
*/
|
|
123
|
+
export const PLATFORM_CLIENT_SUBJECT_PREFIX = "stgm_pc|";
|
|
124
|
+
|
|
125
|
+
export function isPlatformClientSubject(idpId: string): boolean {
|
|
126
|
+
return idpId.startsWith(PLATFORM_CLIENT_SUBJECT_PREFIX);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function reservedSubjectMessage(idpId: string): string {
|
|
130
|
+
return `subject '${idpId}' uses the '${PLATFORM_CLIENT_SUBJECT_PREFIX}' prefix reserved for accounts a platform client provisions`;
|
|
131
|
+
}
|
|
132
|
+
|
|
113
133
|
// ---------------------------------------------------------------------------
|
|
114
134
|
// Byte-pinned copy (the cloud handlers' sentences, moved as-is).
|
|
115
135
|
// ---------------------------------------------------------------------------
|
|
@@ -108,6 +108,7 @@ import {
|
|
|
108
108
|
import type {
|
|
109
109
|
AccountCreatedHook,
|
|
110
110
|
CreateAccount,
|
|
111
|
+
AccountProvisioning,
|
|
111
112
|
CreateAccountInput,
|
|
112
113
|
DirectAccountProvisioner,
|
|
113
114
|
} from "./provisioning.js";
|
|
@@ -230,6 +231,7 @@ async function runCreateChain(
|
|
|
230
231
|
account: IdentityAccount,
|
|
231
232
|
caller: CallerIdentity,
|
|
232
233
|
kind: ApiResourceKind,
|
|
234
|
+
provisioning: AccountProvisioning,
|
|
233
235
|
): Promise<IdentityAccount> {
|
|
234
236
|
const reqCtx = new RequestContext(
|
|
235
237
|
IdentityAccountSchema,
|
|
@@ -254,7 +256,7 @@ async function runCreateChain(
|
|
|
254
256
|
.addStep(newCheckDuplicateStep(deps.accounts))
|
|
255
257
|
.addStep(newBuildNewStateStep())
|
|
256
258
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
257
|
-
.addStep(newAssignBackendFieldsStep())
|
|
259
|
+
.addStep(newAssignBackendFieldsStep(provisioning))
|
|
258
260
|
.addStep(newPersistNewAccountStep(deps.accounts))
|
|
259
261
|
.addStep(
|
|
260
262
|
newCreateAuthorizationTuplesStep(
|
|
@@ -270,7 +272,9 @@ async function runCreateChain(
|
|
|
270
272
|
/**
|
|
271
273
|
* The create path as its in-process callers see it (provisioning.ts
|
|
272
274
|
* `CreateAccount`): the input becomes the resource envelope the chain
|
|
273
|
-
* expects, tagged with the kind this controller serves.
|
|
275
|
+
* expects, tagged with the kind this controller serves. A platform-client
|
|
276
|
+
* account carries its owning organization, as the cloud has always
|
|
277
|
+
* written it; a direct account belongs to no organization.
|
|
274
278
|
*/
|
|
275
279
|
export function newCreateAccountPath(
|
|
276
280
|
deps: CreateAccountPathDeps,
|
|
@@ -282,11 +286,17 @@ export function newCreateAccountPath(
|
|
|
282
286
|
create(IdentityAccountSchema, {
|
|
283
287
|
apiVersion: "iam.stigmer.ai/v1",
|
|
284
288
|
kind: "IdentityAccount",
|
|
285
|
-
metadata: {
|
|
289
|
+
metadata: {
|
|
290
|
+
name: input.name,
|
|
291
|
+
...(input.provisioning.mode === "platform_client"
|
|
292
|
+
? { org: input.provisioning.org }
|
|
293
|
+
: {}),
|
|
294
|
+
},
|
|
286
295
|
spec: input.spec,
|
|
287
296
|
}),
|
|
288
297
|
caller,
|
|
289
298
|
kind,
|
|
299
|
+
input.provisioning,
|
|
290
300
|
);
|
|
291
301
|
}
|
|
292
302
|
|
|
@@ -303,7 +313,9 @@ function createRpc(
|
|
|
303
313
|
): Promise<IdentityAccount> {
|
|
304
314
|
const caller = callerIdentityOf(ctx);
|
|
305
315
|
guardInternalRpc(caller);
|
|
306
|
-
|
|
316
|
+
// The RPC creates the platform's own accounts only: a platform-client
|
|
317
|
+
// account is the mint's to provision, never a wire caller's.
|
|
318
|
+
return runCreateChain(deps, account, caller, kindOf(ctx), { mode: "direct" });
|
|
307
319
|
}
|
|
308
320
|
|
|
309
321
|
function guardInternalRpc(caller: CallerIdentity): void {
|
|
@@ -74,11 +74,27 @@ export class UserInfoFetchError extends Error {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* How the account being created was provisioned — the one input that
|
|
79
|
+
* decides its backend-assigned fields (steps.ts AssignBackendFields):
|
|
80
|
+
* - `direct`: the platform's own subject (a first sign-in, the boot
|
|
81
|
+
* operator, a machine account created over the RPC). Its subject may
|
|
82
|
+
* not use the `stgm_pc|` prefix platform-client accounts reserve.
|
|
83
|
+
* - `platform_client`: an end user a PlatformClient's mint provisioned
|
|
84
|
+
* (domain/platformclient/mint.ts). Its subject is the reserved
|
|
85
|
+
* composite, and it belongs to the client's owning organization, which
|
|
86
|
+
* only this arm carries.
|
|
87
|
+
*/
|
|
88
|
+
export type AccountProvisioning =
|
|
89
|
+
| { readonly mode: "direct" }
|
|
90
|
+
| { readonly mode: "platform_client"; readonly org: string };
|
|
91
|
+
|
|
77
92
|
/** What a caller of the create path supplies: the rest is the chain's. */
|
|
78
93
|
export interface CreateAccountInput {
|
|
79
94
|
/** `metadata.name`; the chain defaults an empty one. */
|
|
80
95
|
readonly name: string;
|
|
81
96
|
readonly spec: IdentityAccountSpec;
|
|
97
|
+
readonly provisioning: AccountProvisioning;
|
|
82
98
|
}
|
|
83
99
|
|
|
84
100
|
/**
|
|
@@ -152,6 +168,7 @@ export function newDirectAccountProvisioner(
|
|
|
152
168
|
pictureUrl: profile.pictureUrl,
|
|
153
169
|
}),
|
|
154
170
|
),
|
|
171
|
+
provisioning: { mode: "direct" },
|
|
155
172
|
};
|
|
156
173
|
|
|
157
174
|
try {
|
|
@@ -144,14 +144,18 @@ export function newResourceIdentityAccountStore(
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
147
|
+
* The platform's own subject: neither federated nor a platform-client end
|
|
148
|
+
* user. A federated account carries its identity_provider_ref (open source
|
|
149
|
+
* never writes one, so that arm is the port's contract stated); a
|
|
150
|
+
* platform-client account is the mint's, whose email and name the platform
|
|
151
|
+
* asserted, so no direct lookup — a verifier's subject resolve, getByEmail —
|
|
152
|
+
* may answer it.
|
|
151
153
|
*/
|
|
152
154
|
function isDirect(account: IdentityAccount): boolean {
|
|
155
|
+
const mode = account.spec?.provisioningMode;
|
|
153
156
|
return (
|
|
154
157
|
account.spec?.identityProviderRef === undefined &&
|
|
155
|
-
|
|
158
|
+
mode !== IdentityAccountProvisioningMode.federated &&
|
|
159
|
+
mode !== IdentityAccountProvisioningMode.platform_client
|
|
156
160
|
);
|
|
157
161
|
}
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
* subject, never its slug (the guideline's "domains with different
|
|
27
27
|
* uniqueness rules bring their own step under the same name").
|
|
28
28
|
* - AssignBackendFields: `is_machine_account` from the `@clients`
|
|
29
|
-
* suffix and
|
|
29
|
+
* suffix and the provisioning mode the path was given (`direct`, or
|
|
30
|
+
* `platform_client` for the mint's end users) on create (the spec's
|
|
30
31
|
* "assigned by backend" fields); on update, every backend-assigned
|
|
31
32
|
* field and the email are preserved from the existing row (A9: the
|
|
32
33
|
* cloud's writable surface — first/last name, picture, preferences —
|
|
@@ -63,11 +64,15 @@ import { EXISTING_RESOURCE_KEY } from "../../pipeline/steps/load-existing.js";
|
|
|
63
64
|
import { TARGET_RESOURCE_KEY } from "../../pipeline/steps/load-target.js";
|
|
64
65
|
import { idValueOf, metadataOf } from "../../pipeline/steps/shapes.js";
|
|
65
66
|
import {
|
|
67
|
+
PLATFORM_CLIENT_SUBJECT_PREFIX,
|
|
66
68
|
accountIdFor,
|
|
67
69
|
accountNotFoundMessage,
|
|
68
70
|
idpIdImmutableMessage,
|
|
69
71
|
isMachineSubject,
|
|
72
|
+
isPlatformClientSubject,
|
|
73
|
+
reservedSubjectMessage,
|
|
70
74
|
} from "./constants.js";
|
|
75
|
+
import type { AccountProvisioning } from "./provisioning.js";
|
|
71
76
|
import { DuplicateAccountError } from "./store.js";
|
|
72
77
|
import type { IdentityAccountStore } from "./store.js";
|
|
73
78
|
|
|
@@ -158,11 +163,61 @@ export function assignDirectBackendFields(
|
|
|
158
163
|
return spec;
|
|
159
164
|
}
|
|
160
165
|
|
|
161
|
-
|
|
166
|
+
/**
|
|
167
|
+
* The backend-assigned fields of a PLATFORM-CLIENT account: the machine
|
|
168
|
+
* flag from the subject's shape (the cloud's rule for every mode), mode
|
|
169
|
+
* `platform_client`, no provider ref — the PlatformClient admits the user
|
|
170
|
+
* at mint time and is no ongoing authentication authority.
|
|
171
|
+
*/
|
|
172
|
+
function assignPlatformClientBackendFields(
|
|
173
|
+
spec: IdentityAccountSpec,
|
|
174
|
+
): IdentityAccountSpec {
|
|
175
|
+
spec.isMachineAccount = isMachineSubject(spec.idpId);
|
|
176
|
+
spec.provisioningMode = IdentityAccountProvisioningMode.platform_client;
|
|
177
|
+
spec.identityProviderRef = undefined;
|
|
178
|
+
return spec;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* AssignBackendFields on create, by how the account was provisioned. The
|
|
183
|
+
* `stgm_pc|` subject namespace is reserved in both directions: a direct
|
|
184
|
+
* account refuses it (INVALID_ARGUMENT, before anything is written), and a
|
|
185
|
+
* platform-client account must carry it — its absence is the mint's bug,
|
|
186
|
+
* never a client's input.
|
|
187
|
+
*/
|
|
188
|
+
export function newAssignBackendFieldsStep(
|
|
189
|
+
provisioning: AccountProvisioning,
|
|
190
|
+
): AccountStep {
|
|
162
191
|
return {
|
|
163
192
|
name: "AssignBackendFields",
|
|
164
193
|
execute(ctx: AccountContext): void {
|
|
165
|
-
|
|
194
|
+
const spec = specOf(ctx);
|
|
195
|
+
switch (provisioning.mode) {
|
|
196
|
+
case "direct":
|
|
197
|
+
if (isPlatformClientSubject(spec.idpId)) {
|
|
198
|
+
throw invalidArgumentError(reservedSubjectMessage(spec.idpId));
|
|
199
|
+
}
|
|
200
|
+
assignDirectBackendFields(spec);
|
|
201
|
+
return;
|
|
202
|
+
case "platform_client":
|
|
203
|
+
if (!isPlatformClientSubject(spec.idpId)) {
|
|
204
|
+
throw internalError(
|
|
205
|
+
new Error(
|
|
206
|
+
`a platform-client account's subject must carry the '${PLATFORM_CLIENT_SUBJECT_PREFIX}' prefix`,
|
|
207
|
+
),
|
|
208
|
+
"assign backend fields",
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
assignPlatformClientBackendFields(spec);
|
|
212
|
+
return;
|
|
213
|
+
default: {
|
|
214
|
+
const exhaustive: never = provisioning;
|
|
215
|
+
throw internalError(
|
|
216
|
+
new Error(`unknown provisioning ${JSON.stringify(exhaustive)}`),
|
|
217
|
+
"assign backend fields",
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
166
221
|
},
|
|
167
222
|
};
|
|
168
223
|
}
|
|
@@ -109,6 +109,31 @@ function federatedAccount(overrides: {
|
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* A platform-client end user as the mint provisions it: the reserved
|
|
114
|
+
* composite subject, mode `platform_client`, the owning organization, and
|
|
115
|
+
* the derived id of its subject (the id every subject lookup reads).
|
|
116
|
+
*/
|
|
117
|
+
function platformClientAccount(overrides: {
|
|
118
|
+
idpId: string;
|
|
119
|
+
email: string;
|
|
120
|
+
}): IdentityAccount {
|
|
121
|
+
return create(IdentityAccountSchema, {
|
|
122
|
+
apiVersion: "iam.stigmer.ai/v1",
|
|
123
|
+
kind: "IdentityAccount",
|
|
124
|
+
metadata: {
|
|
125
|
+
id: accountIdFor(overrides.idpId),
|
|
126
|
+
name: overrides.email,
|
|
127
|
+
org: "acme",
|
|
128
|
+
},
|
|
129
|
+
spec: {
|
|
130
|
+
idpId: overrides.idpId,
|
|
131
|
+
email: overrides.email,
|
|
132
|
+
provisioningMode: IdentityAccountProvisioningMode.platform_client,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
112
137
|
/** A direct account with no subject — the row no lookup could ever reach. */
|
|
113
138
|
function subjectlessDirectAccount(): IdentityAccount {
|
|
114
139
|
return create(IdentityAccountSchema, {
|
|
@@ -357,6 +382,32 @@ const CASES: ReadonlyArray<PortContractDeclaration<IdentityAccountStore>> = [
|
|
|
357
382
|
);
|
|
358
383
|
},
|
|
359
384
|
],
|
|
385
|
+
[
|
|
386
|
+
"findDirectByIdpId and findDirectByEmail never answer a platform-client account, while findByIdpId (any mode) does",
|
|
387
|
+
async ({ store }) => {
|
|
388
|
+
await store.save(
|
|
389
|
+
platformClientAccount({
|
|
390
|
+
idpId: "stgm_pc|acme|user-7",
|
|
391
|
+
email: "pat@example.com",
|
|
392
|
+
}),
|
|
393
|
+
);
|
|
394
|
+
assert.equal(
|
|
395
|
+
await store.findDirectByIdpId("stgm_pc|acme|user-7"),
|
|
396
|
+
undefined,
|
|
397
|
+
"the direct-subject lookup must not resolve to an account a platform client provisioned",
|
|
398
|
+
);
|
|
399
|
+
assert.equal(
|
|
400
|
+
await store.findDirectByEmail("pat@example.com"),
|
|
401
|
+
undefined,
|
|
402
|
+
"the direct-email lookup must not answer an email a platform asserted",
|
|
403
|
+
);
|
|
404
|
+
assert.equal(
|
|
405
|
+
(await store.findByIdpId("stgm_pc|acme|user-7"))?.spec?.provisioningMode,
|
|
406
|
+
IdentityAccountProvisioningMode.platform_client,
|
|
407
|
+
"the any-mode subject lookup — the mint's — must answer the platform-client account",
|
|
408
|
+
);
|
|
409
|
+
},
|
|
410
|
+
],
|
|
360
411
|
[
|
|
361
412
|
"findByIds answers one row per distinct id, in first-occurrence order; an empty request answers an empty list",
|
|
362
413
|
async ({ store }) => {
|
|
@@ -22,12 +22,14 @@
|
|
|
22
22
|
* with an empty `idp_id` is refused, since no lookup could reach it;
|
|
23
23
|
* - `update` replaces, never creates: an unknown id writes nothing;
|
|
24
24
|
* - `deleteById` of an unknown id resolves;
|
|
25
|
-
* - `findDirectByIdpId` answers only
|
|
26
|
-
*
|
|
27
|
-
* same `auth0|…` shape and the platform lane must never resolve to it
|
|
25
|
+
* - `findDirectByIdpId` answers only the platform's own subjects —
|
|
26
|
+
* never a federated account (a customer's federated IdP may mint the
|
|
27
|
+
* same `auth0|…` shape and the platform lane must never resolve to it)
|
|
28
|
+
* and never a platform-client end user (its subject is the mint's);
|
|
28
29
|
* - `findDirectByEmail` answers direct accounts only, by exact match:
|
|
29
30
|
* federation legitimately duplicates emails, so the email axis is
|
|
30
|
-
* non-unique
|
|
31
|
+
* non-unique, and a platform-client account's email is one its
|
|
32
|
+
* platform asserted, so no email lookup may answer it;
|
|
31
33
|
* - `findByIds` answers one row per distinct id, in first-occurrence
|
|
32
34
|
* order, and skips unknown ids;
|
|
33
35
|
* - a typed not-found reads as `undefined`; any other storage failure
|
|
@@ -59,7 +61,7 @@ export interface IdentityAccountStore {
|
|
|
59
61
|
findById(id: string): Promise<IdentityAccount | undefined>;
|
|
60
62
|
/** Any provisioning mode — the getByIdpId RPC's lookup. */
|
|
61
63
|
findByIdpId(idpId: string): Promise<IdentityAccount | undefined>;
|
|
62
|
-
/**
|
|
64
|
+
/** The platform's own subjects only (no federated, no platform-client) — the verifiers' and whoAmI's lookup. */
|
|
63
65
|
findDirectByIdpId(idpId: string): Promise<IdentityAccount | undefined>;
|
|
64
66
|
/** Direct accounts only, exact match — the getByEmail RPC's lookup. */
|
|
65
67
|
findDirectByEmail(email: string): Promise<IdentityAccount | undefined>;
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* with the token fields empty (minting a proxy token is cloud-only);
|
|
18
18
|
* - getServerInfo reports the build stamp by default and the release a
|
|
19
19
|
* library consumer states through ComposeOptions.version otherwise
|
|
20
|
-
* (stigmer#1168: unbundled, the stamp is always "dev")
|
|
20
|
+
* (stigmer#1168: unbundled, the stamp is always "dev"), and a
|
|
21
|
+
* trusted-local server's posture as an explicit false;
|
|
21
22
|
* - getLicenseStatus answers `absent` with no claims and no key when no
|
|
22
23
|
* provider is composed, and the composed provider's report verbatim
|
|
23
24
|
* when one is — both stamped with the ONE instant the controller
|
|
@@ -138,6 +139,9 @@ describe("platform domain (composed server)", () => {
|
|
|
138
139
|
// Unbundled runs report Go's unstamped default; release bundles stamp
|
|
139
140
|
// the esbuild define (scripts/bundle-slim.mjs).
|
|
140
141
|
expect(first.version).toBe("dev");
|
|
142
|
+
// A server that trusts every request says so on the wire: an explicit
|
|
143
|
+
// false, never an absent field a console would read as "unknown".
|
|
144
|
+
expect(first.authenticationRequired).toBe(false);
|
|
141
145
|
|
|
142
146
|
const second = await client.getServerInfo({});
|
|
143
147
|
expect(second.edition).toBe(first.edition);
|
|
@@ -285,6 +289,7 @@ describe("platform domain (keyless runner-token service)", () => {
|
|
|
285
289
|
),
|
|
286
290
|
edition: ServerEdition.oss,
|
|
287
291
|
version: "dev",
|
|
292
|
+
authenticationRequired: false,
|
|
288
293
|
licenseStatus: ABSENT_LICENSE_STATUS,
|
|
289
294
|
now: () => new Date(),
|
|
290
295
|
logger: silentLogger,
|
|
@@ -320,6 +325,7 @@ describe("platform domain (license status)", () => {
|
|
|
320
325
|
),
|
|
321
326
|
edition: ServerEdition.enterprise,
|
|
322
327
|
version: "dev",
|
|
328
|
+
authenticationRequired: false,
|
|
323
329
|
licenseStatus: provider,
|
|
324
330
|
now: () => instant,
|
|
325
331
|
logger: silentLogger,
|