@stigmer/server 3.14.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boot/compose.d.ts +8 -1
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +98 -12
- package/dist/boot/compose.js.map +1 -1
- package/dist/domain/agentexecution/constants.d.ts +16 -0
- package/dist/domain/agentexecution/constants.d.ts.map +1 -1
- package/dist/domain/agentexecution/constants.js +22 -0
- package/dist/domain/agentexecution/constants.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +10 -3
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentexecution/run-target.d.ts +26 -0
- package/dist/domain/agentexecution/run-target.d.ts.map +1 -0
- package/dist/domain/agentexecution/run-target.js +31 -0
- package/dist/domain/agentexecution/run-target.js.map +1 -0
- package/dist/domain/apikey/verifier.d.ts +8 -1
- package/dist/domain/apikey/verifier.d.ts.map +1 -1
- package/dist/domain/apikey/verifier.js +14 -2
- package/dist/domain/apikey/verifier.js.map +1 -1
- package/dist/domain/identityaccount/constants.d.ts +66 -0
- package/dist/domain/identityaccount/constants.d.ts.map +1 -0
- package/dist/domain/identityaccount/constants.js +153 -0
- package/dist/domain/identityaccount/constants.js.map +1 -0
- package/dist/domain/identityaccount/controller.d.ts +34 -0
- package/dist/domain/identityaccount/controller.d.ts.map +1 -0
- package/dist/domain/identityaccount/controller.js +342 -0
- package/dist/domain/identityaccount/controller.js.map +1 -0
- package/dist/domain/identityaccount/operator.d.ts +14 -0
- package/dist/domain/identityaccount/operator.d.ts.map +1 -0
- package/dist/domain/identityaccount/operator.js +67 -0
- package/dist/domain/identityaccount/operator.js.map +1 -0
- package/dist/domain/identityaccount/provisioning.d.ts +52 -0
- package/dist/domain/identityaccount/provisioning.d.ts.map +1 -0
- package/dist/domain/identityaccount/provisioning.js +114 -0
- package/dist/domain/identityaccount/provisioning.js.map +1 -0
- package/dist/domain/identityaccount/resolve.d.ts +36 -0
- package/dist/domain/identityaccount/resolve.d.ts.map +1 -0
- package/dist/domain/identityaccount/resolve.js +12 -0
- package/dist/domain/identityaccount/resolve.js.map +1 -0
- package/dist/domain/identityaccount/resource-store.d.ts +4 -0
- package/dist/domain/identityaccount/resource-store.d.ts.map +1 -0
- package/dist/domain/identityaccount/resource-store.js +131 -0
- package/dist/domain/identityaccount/resource-store.js.map +1 -0
- package/dist/domain/identityaccount/steps.d.ts +79 -0
- package/dist/domain/identityaccount/steps.d.ts.map +1 -0
- package/dist/domain/identityaccount/steps.js +291 -0
- package/dist/domain/identityaccount/steps.js.map +1 -0
- package/dist/domain/identityaccount/store-contract.d.ts +22 -0
- package/dist/domain/identityaccount/store-contract.d.ts.map +1 -0
- package/dist/domain/identityaccount/store-contract.js +307 -0
- package/dist/domain/identityaccount/store-contract.js.map +1 -0
- package/dist/domain/identityaccount/store.d.ts +66 -0
- package/dist/domain/identityaccount/store.d.ts.map +1 -0
- package/dist/domain/identityaccount/store.js +13 -0
- package/dist/domain/identityaccount/store.js.map +1 -0
- package/dist/domain/session/constants.d.ts +15 -0
- package/dist/domain/session/constants.d.ts.map +1 -0
- package/dist/domain/session/constants.js +17 -0
- package/dist/domain/session/constants.js.map +1 -0
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +10 -0
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/session/run-target.d.ts +15 -0
- package/dist/domain/session/run-target.d.ts.map +1 -0
- package/dist/domain/session/run-target.js +14 -0
- package/dist/domain/session/run-target.js.map +1 -0
- package/dist/domain/workflowexecution/constants.d.ts +9 -0
- package/dist/domain/workflowexecution/constants.d.ts.map +1 -1
- package/dist/domain/workflowexecution/constants.js +13 -0
- package/dist/domain/workflowexecution/constants.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +8 -0
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowexecution/run-target.d.ts +18 -0
- package/dist/domain/workflowexecution/run-target.d.ts.map +1 -0
- package/dist/domain/workflowexecution/run-target.js +23 -0
- package/dist/domain/workflowexecution/run-target.js.map +1 -0
- package/dist/extensions/drivers.d.ts +26 -0
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/gate-slots.d.ts +16 -1
- package/dist/extensions/gate-slots.d.ts.map +1 -1
- package/dist/extensions/gate-slots.js +1 -0
- package/dist/extensions/gate-slots.js.map +1 -1
- package/dist/extensions/identity-federation.d.ts +36 -0
- package/dist/extensions/identity-federation.d.ts.map +1 -0
- package/dist/extensions/identity-federation.js +2 -0
- package/dist/extensions/identity-federation.js.map +1 -0
- package/dist/extensions/registry.d.ts +18 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +20 -0
- package/dist/extensions/registry.js.map +1 -1
- package/dist/identity/oidc-discovery.d.ts +36 -0
- package/dist/identity/oidc-discovery.d.ts.map +1 -0
- package/dist/identity/oidc-discovery.js +60 -0
- package/dist/identity/oidc-discovery.js.map +1 -0
- package/dist/identity/oidc-userinfo.d.ts +30 -0
- package/dist/identity/oidc-userinfo.d.ts.map +1 -0
- package/dist/identity/oidc-userinfo.js +28 -0
- package/dist/identity/oidc-userinfo.js.map +1 -0
- package/dist/identity/oidc-verifier.d.ts +6 -0
- package/dist/identity/oidc-verifier.d.ts.map +1 -1
- package/dist/identity/oidc-verifier.js +35 -34
- package/dist/identity/oidc-verifier.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/interceptors/auth.d.ts +10 -0
- package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
- package/dist/pipeline/interceptors/auth.js +9 -1
- package/dist/pipeline/interceptors/auth.js.map +1 -1
- package/dist/pipeline/steps/authorize-run-target.d.ts +105 -0
- package/dist/pipeline/steps/authorize-run-target.d.ts.map +1 -0
- package/dist/pipeline/steps/authorize-run-target.js +61 -0
- package/dist/pipeline/steps/authorize-run-target.js.map +1 -0
- package/dist/query/search/criteria.d.ts +10 -3
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +11 -4
- package/dist/query/search/criteria.js.map +1 -1
- package/package.json +4 -4
- package/src/boot/__tests__/auth-enabled-composition.test.ts +226 -2
- package/src/boot/compose.ts +118 -13
- package/src/domain/agentexecution/__tests__/run-gate.test.ts +245 -0
- package/src/domain/agentexecution/__tests__/run-target.test.ts +117 -0
- package/src/domain/agentexecution/constants.ts +25 -0
- package/src/domain/agentexecution/controller.ts +10 -3
- package/src/domain/agentexecution/run-target.ts +64 -0
- package/src/domain/apikey/__tests__/verifier.test.ts +146 -22
- package/src/domain/apikey/verifier.ts +24 -2
- package/src/domain/identityaccount/__tests__/constants.test.ts +155 -0
- package/src/domain/identityaccount/__tests__/identityaccount.test.ts +484 -0
- package/src/domain/identityaccount/__tests__/operator.test.ts +180 -0
- package/src/domain/identityaccount/__tests__/provisioning.test.ts +281 -0
- package/src/domain/identityaccount/__tests__/resolve.test.ts +114 -0
- package/src/domain/identityaccount/__tests__/resource-store.test.ts +216 -0
- package/src/domain/identityaccount/__tests__/support.ts +58 -0
- package/src/domain/identityaccount/constants.ts +178 -0
- package/src/domain/identityaccount/controller.ts +667 -0
- package/src/domain/identityaccount/operator.ts +94 -0
- package/src/domain/identityaccount/provisioning.ts +182 -0
- package/src/domain/identityaccount/resolve.ts +52 -0
- package/src/domain/identityaccount/resource-store.ts +157 -0
- package/src/domain/identityaccount/steps.ts +377 -0
- package/src/domain/identityaccount/store-contract.ts +471 -0
- package/src/domain/identityaccount/store.ts +70 -0
- package/src/domain/session/__tests__/run-gate.test.ts +219 -0
- package/src/domain/session/__tests__/run-target.test.ts +43 -0
- package/src/domain/session/constants.ts +17 -0
- package/src/domain/session/controller.ts +10 -0
- package/src/domain/session/run-target.ts +30 -0
- package/src/domain/workflowexecution/__tests__/run-gate.test.ts +200 -0
- package/src/domain/workflowexecution/__tests__/run-target.test.ts +80 -0
- package/src/domain/workflowexecution/constants.ts +17 -0
- package/src/domain/workflowexecution/controller.ts +10 -0
- package/src/domain/workflowexecution/run-target.ts +47 -0
- package/src/extensions/__tests__/extension-composition.test.ts +49 -0
- package/src/extensions/__tests__/identity-account-composed.test.ts +803 -0
- package/src/extensions/__tests__/identity-account-points.test.ts +129 -0
- package/src/extensions/__tests__/registry.test.ts +4 -0
- package/src/extensions/__tests__/tier-truthfulness.test.ts +299 -0
- package/src/extensions/drivers.ts +26 -0
- package/src/extensions/gate-slots.ts +16 -0
- package/src/extensions/identity-federation.ts +58 -0
- package/src/extensions/registry.ts +44 -1
- package/src/identity/__tests__/oidc-discovery.test.ts +122 -0
- package/src/identity/__tests__/oidc-userinfo.test.ts +103 -0
- package/src/identity/__tests__/oidc-verifier.test.ts +173 -3
- package/src/identity/oidc-discovery.ts +84 -0
- package/src/identity/oidc-userinfo.ts +64 -0
- package/src/identity/oidc-verifier.ts +42 -45
- package/src/index.ts +26 -0
- package/src/pipeline/__tests__/support.ts +34 -0
- package/src/pipeline/interceptors/auth.ts +13 -1
- package/src/pipeline/steps/__tests__/authorize-annotation-completeness.test.ts +184 -0
- package/src/pipeline/steps/__tests__/authorize-run-target.test.ts +261 -0
- package/src/pipeline/steps/authorize-run-target.ts +148 -0
- package/src/query/search/__tests__/criteria.test.ts +18 -1
- package/src/query/search/criteria.ts +11 -4
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* discover).
|
|
6
6
|
*
|
|
7
7
|
* The searchable-kind set is DERIVED from the proto kind_meta extension
|
|
8
|
-
* (not_search_indexed: false, tier
|
|
8
|
+
* (not_search_indexed: false, tier == open_source) — Go's
|
|
9
9
|
* SearchIndexedKinds() posture since stigmer/stigmer#439, where hand-copied
|
|
10
10
|
* kind lists drifted from the proto and from each other. Go still carries a
|
|
11
11
|
* hand map (SearchableKinds) pinned to the derivation by an invariant test;
|
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
* registry unit test pins the registered extractors against this same
|
|
14
14
|
* derivation (the #439 invariant's TS home).
|
|
15
15
|
*
|
|
16
|
+
* The tier clause admits open_source alone, not "anything but cloud_only":
|
|
17
|
+
* the index is the core's, and a kind tiered enterprise (editions program,
|
|
18
|
+
* DD-001) must not enter it by accident. This derivation is edition-blind
|
|
19
|
+
* by design (the same code runs in every composition); an edition-aware
|
|
20
|
+
* derivation is a design act with a named trigger — the first searchable
|
|
21
|
+
* kind above open_source.
|
|
22
|
+
*
|
|
16
23
|
* kinds are kept VERBATIM from the request; filtering to the searchable set
|
|
17
24
|
* happens in effectiveKinds(). Keeping the raw request is what
|
|
18
25
|
* distinguishes "no kinds requested" (discover mode) from "kinds requested
|
|
@@ -37,8 +44,8 @@ let searchIndexedKindsCache;
|
|
|
37
44
|
let searchableKindSetCache;
|
|
38
45
|
/**
|
|
39
46
|
* Go SearchIndexedKinds(): every kind this edition's search read side
|
|
40
|
-
* serves — kind_meta declares it search-indexed and
|
|
41
|
-
*
|
|
47
|
+
* serves — kind_meta declares it search-indexed and open_source-tier.
|
|
48
|
+
* Sorted ascending by enum value, exactly Go's sort. Kinds whose
|
|
42
49
|
* kind_meta cannot be read are skipped (Go's contract; the registry unit
|
|
43
50
|
* test separately fails on such a defect).
|
|
44
51
|
*/
|
|
@@ -57,7 +64,7 @@ export function searchIndexedKinds() {
|
|
|
57
64
|
catch {
|
|
58
65
|
continue;
|
|
59
66
|
}
|
|
60
|
-
if (meta.notSearchIndexed || meta.tier
|
|
67
|
+
if (meta.notSearchIndexed || meta.tier !== ResourceTier.open_source) {
|
|
61
68
|
continue;
|
|
62
69
|
}
|
|
63
70
|
kinds.push(kind);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"criteria.js","sourceRoot":"","sources":["../../../src/query/search/criteria.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"criteria.js","sourceRoot":"","sources":["../../../src/query/search/criteria.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "The Stigmer OSS control plane as a composable library: composeServer, the extension registry and the driver seams a composition builds on. The self-contained deployable of the same server is @stigmer/server-slim.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"@connectrpc/connect-node": "^2.0.0",
|
|
63
63
|
"@kubernetes/client-node": "2.0.0",
|
|
64
64
|
"@opentelemetry/api": "^1.9.0",
|
|
65
|
-
"@stigmer/protos": "3.
|
|
66
|
-
"@stigmer/temporal-codecs": "3.
|
|
67
|
-
"@stigmer/zip-structure": "3.
|
|
65
|
+
"@stigmer/protos": "3.15.0",
|
|
66
|
+
"@stigmer/temporal-codecs": "3.15.0",
|
|
67
|
+
"@stigmer/zip-structure": "3.15.0",
|
|
68
68
|
"@temporalio/activity": "1.16.2",
|
|
69
69
|
"@temporalio/client": "1.16.2",
|
|
70
70
|
"@temporalio/common": "1.16.2",
|
|
@@ -14,7 +14,14 @@
|
|
|
14
14
|
* owning user (the runner's credential lane, ruling Q3 — the runner
|
|
15
15
|
* presents exactly such a key via STIGMER_TOKEN);
|
|
16
16
|
* 4. deleting the key revokes it on the very next request;
|
|
17
|
-
* 5. garbage credentials keep the Q6 unclaimed-token rejection
|
|
17
|
+
* 5. garbage credentials keep the Q6 unclaimed-token rejection;
|
|
18
|
+
* 6. (20260911.11) the identity-account arms: an unprovisioned subject is
|
|
19
|
+
* idp-shaped (whoAmI NOT_FOUND, writes stamped with the raw sub);
|
|
20
|
+
* provisionMyAccount creates the account under the derived id with
|
|
21
|
+
* the issuer's userinfo profile; from then on the verifier resolves
|
|
22
|
+
* the subject to the account id and API keys minted before OR after
|
|
23
|
+
* provisioning answer whoAmI with the owner's account; no operator
|
|
24
|
+
* account exists under the posture.
|
|
18
25
|
*/
|
|
19
26
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
20
27
|
import { tmpdir } from "node:os";
|
|
@@ -30,8 +37,12 @@ import { SignJWT, exportJWK, generateKeyPair } from "jose";
|
|
|
30
37
|
import type { GenerateKeyPairResult } from "jose";
|
|
31
38
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
32
39
|
|
|
40
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
33
41
|
import { ApiKeyCommandController } from "@stigmer/protos/ai/stigmer/iam/apikey/v1/command_pb";
|
|
34
42
|
import { ApiKeyQueryController } from "@stigmer/protos/ai/stigmer/iam/apikey/v1/query_pb";
|
|
43
|
+
import { IdentityAccountCommandController } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/command_pb";
|
|
44
|
+
import { IdentityAccountProvisioningMode } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/enum_pb";
|
|
45
|
+
import { IdentityAccountQueryController } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/query_pb";
|
|
35
46
|
import { PlatformQueryController } from "@stigmer/protos/ai/stigmer/platform/v1/server_info_pb";
|
|
36
47
|
import {
|
|
37
48
|
Health,
|
|
@@ -42,6 +53,11 @@ import { loadConfig } from "../config.js";
|
|
|
42
53
|
import { composeServer } from "../compose.js";
|
|
43
54
|
import type { ComposedServer } from "../compose.js";
|
|
44
55
|
import { createLogger } from "../logger.js";
|
|
56
|
+
import {
|
|
57
|
+
ACCOUNT_NOT_FOUND_FOR_CALLER_MESSAGE,
|
|
58
|
+
USERINFO_UNAVAILABLE_PREFIX,
|
|
59
|
+
accountIdFor,
|
|
60
|
+
} from "../../domain/identityaccount/constants.js";
|
|
45
61
|
import { AUTHENTICATION_TOKEN_MISSING_MESSAGE } from "../../pipeline/interceptors/auth.js";
|
|
46
62
|
|
|
47
63
|
const AUDIENCE = "https://api.stigmer.test/";
|
|
@@ -50,8 +66,15 @@ let dir: string;
|
|
|
50
66
|
let issuerServer: Server;
|
|
51
67
|
let issuer: string;
|
|
52
68
|
let privateKey: GenerateKeyPairResult["privateKey"];
|
|
69
|
+
// A lever for the UNAVAILABLE arm: the issuer's /userinfo answers 503 for
|
|
70
|
+
// this subject while set. Undefined = every subject is served.
|
|
71
|
+
let userinfoRefusesSub: string | undefined;
|
|
53
72
|
let server: ComposedServer;
|
|
54
73
|
let port: number;
|
|
74
|
+
// A key minted while its owner was still idp-shaped: its creator stamp is
|
|
75
|
+
// the raw sub. The identity-account arms prove that stamp resolves to the
|
|
76
|
+
// owner's account once one exists (20260911.11 A1's derived id).
|
|
77
|
+
let keyMintedBeforeProvisioning: string;
|
|
55
78
|
|
|
56
79
|
function bearer(token: string): Interceptor {
|
|
57
80
|
return (next) => (request) => {
|
|
@@ -88,8 +111,16 @@ beforeAll(async () => {
|
|
|
88
111
|
};
|
|
89
112
|
issuerServer = createServer((req, res) => {
|
|
90
113
|
if (req.url === "/.well-known/openid-configuration") {
|
|
114
|
+
// The provisioning lane reads userinfo_endpoint from here (A8) —
|
|
115
|
+
// never a guessed `<issuer>/userinfo`.
|
|
91
116
|
res.setHeader("content-type", "application/json");
|
|
92
|
-
res.end(
|
|
117
|
+
res.end(
|
|
118
|
+
JSON.stringify({
|
|
119
|
+
issuer,
|
|
120
|
+
jwks_uri: `${issuer}/jwks`,
|
|
121
|
+
userinfo_endpoint: `${issuer}/userinfo`,
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
93
124
|
return;
|
|
94
125
|
}
|
|
95
126
|
if (req.url === "/jwks") {
|
|
@@ -97,6 +128,43 @@ beforeAll(async () => {
|
|
|
97
128
|
res.end(JSON.stringify({ keys: [jwk] }));
|
|
98
129
|
return;
|
|
99
130
|
}
|
|
131
|
+
if (req.url === "/userinfo") {
|
|
132
|
+
// The provisioning lane's profile source (20260911.11 A1/Q-IA-2):
|
|
133
|
+
// answers the bearer's own claims as an IdP would, given_name and
|
|
134
|
+
// family_name included — the signature was already proven upstream.
|
|
135
|
+
const header = req.headers.authorization ?? "";
|
|
136
|
+
const token = header.startsWith("Bearer ")
|
|
137
|
+
? header.slice("Bearer ".length)
|
|
138
|
+
: "";
|
|
139
|
+
const payloadSegment = token.split(".")[1];
|
|
140
|
+
if (payloadSegment === undefined) {
|
|
141
|
+
res.statusCode = 401;
|
|
142
|
+
res.end();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const claims = JSON.parse(
|
|
146
|
+
Buffer.from(payloadSegment, "base64url").toString("utf8"),
|
|
147
|
+
) as { sub?: string; email?: string };
|
|
148
|
+
if (
|
|
149
|
+
userinfoRefusesSub !== undefined &&
|
|
150
|
+
claims.sub === userinfoRefusesSub
|
|
151
|
+
) {
|
|
152
|
+
res.statusCode = 503;
|
|
153
|
+
res.end();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
res.setHeader("content-type", "application/json");
|
|
157
|
+
res.end(
|
|
158
|
+
JSON.stringify({
|
|
159
|
+
sub: claims.sub ?? "",
|
|
160
|
+
email: claims.email ?? "",
|
|
161
|
+
given_name: "Composed",
|
|
162
|
+
family_name: "User",
|
|
163
|
+
picture: `${issuer}/pictures/${encodeURIComponent(claims.sub ?? "")}`,
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
100
168
|
res.statusCode = 404;
|
|
101
169
|
res.end();
|
|
102
170
|
});
|
|
@@ -221,6 +289,7 @@ describe("the full credential loop (OIDC login → API key → revocation)", ()
|
|
|
221
289
|
expect(second.status?.audit?.specAudit?.createdBy?.id).toBe(
|
|
222
290
|
"auth0|loop-user",
|
|
223
291
|
);
|
|
292
|
+
keyMintedBeforeProvisioning = second.spec?.keyHash ?? "";
|
|
224
293
|
});
|
|
225
294
|
|
|
226
295
|
it("deleting the key revokes it on the very next request", async () => {
|
|
@@ -243,3 +312,158 @@ describe("the full credential loop (OIDC login → API key → revocation)", ()
|
|
|
243
312
|
expect(ConnectError.from(error).rawMessage).toBe("invalid token");
|
|
244
313
|
});
|
|
245
314
|
});
|
|
315
|
+
|
|
316
|
+
describe("identity accounts under the OIDC posture (20260911.11; Q-IA-2, A1, A2)", () => {
|
|
317
|
+
const SUB = "auth0|loop-user";
|
|
318
|
+
const accountId = accountIdFor(SUB);
|
|
319
|
+
|
|
320
|
+
it("no operator account exists under an authentication posture — the boot-time ensure is trusted-local only", async () => {
|
|
321
|
+
const rows = await server.store.listResources(
|
|
322
|
+
ApiResourceKind.identity_account,
|
|
323
|
+
);
|
|
324
|
+
expect(rows).toHaveLength(0);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("whoAmI for an unprovisioned subject is NOT_FOUND with the cloud's copy, and its writes stay stamped with the raw sub", async () => {
|
|
328
|
+
const token = await mintOidcToken(SUB, "loop@example.com");
|
|
329
|
+
const query = createClient(
|
|
330
|
+
IdentityAccountQueryController,
|
|
331
|
+
transportWith(token),
|
|
332
|
+
);
|
|
333
|
+
const error = await query.whoAmI({}).catch((e: unknown) => e);
|
|
334
|
+
expect(ConnectError.from(error).code).toBe(Code.NotFound);
|
|
335
|
+
expect(ConnectError.from(error).rawMessage).toBe(
|
|
336
|
+
ACCOUNT_NOT_FOUND_FOR_CALLER_MESSAGE,
|
|
337
|
+
);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("provisionMyAccount creates the account under the derived id with the userinfo profile", async () => {
|
|
341
|
+
const token = await mintOidcToken(SUB, "loop@example.com");
|
|
342
|
+
const command = createClient(
|
|
343
|
+
IdentityAccountCommandController,
|
|
344
|
+
transportWith(token),
|
|
345
|
+
);
|
|
346
|
+
const account = await command.provisionMyAccount({});
|
|
347
|
+
expect(account.metadata?.id).toBe(accountId);
|
|
348
|
+
expect(account.metadata?.name).toBe("loop@example.com");
|
|
349
|
+
expect(account.spec).toMatchObject({
|
|
350
|
+
idpId: SUB,
|
|
351
|
+
email: "loop@example.com",
|
|
352
|
+
firstName: "Composed",
|
|
353
|
+
lastName: "User",
|
|
354
|
+
provisioningMode: IdentityAccountProvisioningMode.direct,
|
|
355
|
+
isMachineAccount: false,
|
|
356
|
+
});
|
|
357
|
+
// The account is created BY the subject it is for: the stamp is the
|
|
358
|
+
// sub the verifier admitted idp-shaped — no account existed yet.
|
|
359
|
+
expect(account.status?.audit?.specAudit?.createdBy?.id).toBe(SUB);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("from the next request on, the verifier resolves the subject to the account id — whoAmI answers and writes are stamped with it", async () => {
|
|
363
|
+
const token = await mintOidcToken(SUB, "loop@example.com");
|
|
364
|
+
const query = createClient(
|
|
365
|
+
IdentityAccountQueryController,
|
|
366
|
+
transportWith(token),
|
|
367
|
+
);
|
|
368
|
+
const me = await query.whoAmI({});
|
|
369
|
+
expect(me.metadata?.id).toBe(accountId);
|
|
370
|
+
|
|
371
|
+
const keys = createClient(ApiKeyCommandController, transportWith(token));
|
|
372
|
+
const key = await keys.create({
|
|
373
|
+
apiVersion: "iam.stigmer.ai/v1",
|
|
374
|
+
kind: "ApiKey",
|
|
375
|
+
metadata: { name: "minted after provisioning", org: "local" },
|
|
376
|
+
spec: {},
|
|
377
|
+
});
|
|
378
|
+
expect(key.status?.audit?.specAudit?.createdBy?.id).toBe(accountId);
|
|
379
|
+
expect(key.status?.audit?.specAudit?.createdBy?.email).toBe(
|
|
380
|
+
"loop@example.com",
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Over that key, whoAmI is the owner's account — the key is the user.
|
|
384
|
+
const overKey = createClient(
|
|
385
|
+
IdentityAccountQueryController,
|
|
386
|
+
transportWith(key.spec?.keyHash ?? ""),
|
|
387
|
+
);
|
|
388
|
+
expect((await overKey.whoAmI({})).metadata?.id).toBe(accountId);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it("a key minted BEFORE provisioning still resolves to the owner's account — the raw-sub stamp derives to the same id", async () => {
|
|
392
|
+
const overLegacyKey = createClient(
|
|
393
|
+
IdentityAccountQueryController,
|
|
394
|
+
transportWith(keyMintedBeforeProvisioning),
|
|
395
|
+
);
|
|
396
|
+
expect((await overLegacyKey.whoAmI({})).metadata?.id).toBe(accountId);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("provisionMyAccount is idempotent for a provisioned subject", async () => {
|
|
400
|
+
const token = await mintOidcToken(SUB, "loop@example.com");
|
|
401
|
+
const command = createClient(
|
|
402
|
+
IdentityAccountCommandController,
|
|
403
|
+
transportWith(token),
|
|
404
|
+
);
|
|
405
|
+
const before = (
|
|
406
|
+
await server.store.listResources(ApiResourceKind.identity_account)
|
|
407
|
+
).length;
|
|
408
|
+
const again = await command.provisionMyAccount({});
|
|
409
|
+
expect(again.metadata?.id).toBe(accountId);
|
|
410
|
+
expect(
|
|
411
|
+
(await server.store.listResources(ApiResourceKind.identity_account))
|
|
412
|
+
.length,
|
|
413
|
+
).toBe(before);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("two concurrent first logins for one subject end in exactly one account", async () => {
|
|
417
|
+
const token = await mintOidcToken("auth0|racer", "racer@example.com");
|
|
418
|
+
const command = createClient(
|
|
419
|
+
IdentityAccountCommandController,
|
|
420
|
+
transportWith(token),
|
|
421
|
+
);
|
|
422
|
+
const before = (
|
|
423
|
+
await server.store.listResources(ApiResourceKind.identity_account)
|
|
424
|
+
).length;
|
|
425
|
+
const [a, b] = await Promise.all([
|
|
426
|
+
command.provisionMyAccount({}),
|
|
427
|
+
command.provisionMyAccount({}),
|
|
428
|
+
]);
|
|
429
|
+
expect(a.metadata?.id).toBe(accountIdFor("auth0|racer"));
|
|
430
|
+
expect(b.metadata?.id).toBe(a.metadata?.id);
|
|
431
|
+
expect(
|
|
432
|
+
(await server.store.listResources(ApiResourceKind.identity_account))
|
|
433
|
+
.length,
|
|
434
|
+
).toBe(before + 1);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it("a userinfo failure is UNAVAILABLE with the cloud's copy, and no account is created", async () => {
|
|
438
|
+
// A real token (the verifier must admit it) whose subject the issuer's
|
|
439
|
+
// /userinfo refuses while the lever is set.
|
|
440
|
+
const token = await mintOidcToken(
|
|
441
|
+
"auth0|userinfo-down",
|
|
442
|
+
"down@example.com",
|
|
443
|
+
);
|
|
444
|
+
userinfoRefusesSub = "auth0|userinfo-down";
|
|
445
|
+
try {
|
|
446
|
+
const command = createClient(
|
|
447
|
+
IdentityAccountCommandController,
|
|
448
|
+
transportWith(token),
|
|
449
|
+
);
|
|
450
|
+
const error = await command
|
|
451
|
+
.provisionMyAccount({})
|
|
452
|
+
.catch((e: unknown) => e);
|
|
453
|
+
expect(ConnectError.from(error).code).toBe(Code.Unavailable);
|
|
454
|
+
expect(
|
|
455
|
+
ConnectError.from(error).rawMessage.startsWith(
|
|
456
|
+
USERINFO_UNAVAILABLE_PREFIX,
|
|
457
|
+
),
|
|
458
|
+
).toBe(true);
|
|
459
|
+
const query = createClient(
|
|
460
|
+
IdentityAccountQueryController,
|
|
461
|
+
transportWith(token),
|
|
462
|
+
);
|
|
463
|
+
const missing = await query.whoAmI({}).catch((e: unknown) => e);
|
|
464
|
+
expect(ConnectError.from(missing).code).toBe(Code.NotFound);
|
|
465
|
+
} finally {
|
|
466
|
+
userinfoRefusesSub = undefined;
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
});
|
package/src/boot/compose.ts
CHANGED
|
@@ -28,6 +28,7 @@ import path from "node:path";
|
|
|
28
28
|
|
|
29
29
|
import type { ConnectRouter, Transport } from "@connectrpc/connect";
|
|
30
30
|
|
|
31
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
31
32
|
import { HealthCheckResponse_ServingStatus as ServingStatus } from "@stigmer/protos/grpc/health/v1/health_pb";
|
|
32
33
|
|
|
33
34
|
import { registerAgentServices } from "../domain/agent/controller.js";
|
|
@@ -64,6 +65,15 @@ import { registerChannelConversationServices } from "../domain/agentchannel/conv
|
|
|
64
65
|
import { registerChannelMessageServices } from "../domain/agentchannel/message.js";
|
|
65
66
|
import { registerAgentShareServices } from "../domain/agentshare/controller.js";
|
|
66
67
|
import { registerApiKeyServices } from "../domain/apikey/controller.js";
|
|
68
|
+
import {
|
|
69
|
+
newCreateAccountPath,
|
|
70
|
+
registerIdentityAccountServices,
|
|
71
|
+
} from "../domain/identityaccount/controller.js";
|
|
72
|
+
import { ensureOperatorAccount } from "../domain/identityaccount/operator.js";
|
|
73
|
+
import { newDirectAccountProvisioner } from "../domain/identityaccount/provisioning.js";
|
|
74
|
+
import { newResourceIdentityAccountStore } from "../domain/identityaccount/resource-store.js";
|
|
75
|
+
import { newIssuerDiscovery } from "../identity/oidc-discovery.js";
|
|
76
|
+
import { newOidcUserInfoClient } from "../identity/oidc-userinfo.js";
|
|
67
77
|
import { newApiKeyIdentityVerifier } from "../domain/apikey/verifier.js";
|
|
68
78
|
import type { IdentityVerifier } from "../extensions/identity.js";
|
|
69
79
|
import { registerArtifactServices } from "../domain/artifact/controller.js";
|
|
@@ -117,6 +127,7 @@ import {
|
|
|
117
127
|
} from "../observability/rpc-metrics.js";
|
|
118
128
|
import { buildInterceptorChain } from "../pipeline/chain.js";
|
|
119
129
|
import { createVerifierChainInterceptor } from "../pipeline/interceptors/auth.js";
|
|
130
|
+
import { operatorIdentitySnapshot } from "../pipeline/steps/defaults.js";
|
|
120
131
|
import { createErrorBoundaryInterceptor } from "../pipeline/interceptors/error-boundary.js";
|
|
121
132
|
import { createRequestMetricsInterceptor } from "../pipeline/interceptors/request-metrics.js";
|
|
122
133
|
import { newPermissiveSingleTeamAuthorizer } from "../pipeline/steps/authorize.js";
|
|
@@ -221,6 +232,13 @@ export interface ComposedServer {
|
|
|
221
232
|
* them or reorder them, and both are the drift DD-007 rules out.
|
|
222
233
|
*/
|
|
223
234
|
identityVerifiers: ReadonlyArray<IdentityVerifier>;
|
|
235
|
+
/**
|
|
236
|
+
* The one routes function both routers are built from (exposed for
|
|
237
|
+
* tests, the `store` posture — the tier-truthfulness invariant
|
|
238
|
+
* enumerates the services a composition registers through it; the
|
|
239
|
+
* server registers no reflection service, deliberately).
|
|
240
|
+
*/
|
|
241
|
+
routes: (router: ConnectRouter) => void;
|
|
224
242
|
/** Completes wiring, flips SERVING, binds the port; returns the bound port. */
|
|
225
243
|
start(): Promise<number>;
|
|
226
244
|
/** NOT_SERVING first, stop background work, drain connections. */
|
|
@@ -294,6 +312,75 @@ export async function composeServer(
|
|
|
294
312
|
});
|
|
295
313
|
}
|
|
296
314
|
|
|
315
|
+
// The require-authentication posture has two sources OR'd here: the
|
|
316
|
+
// OSS OIDC issuer (O3 rulings Q1+Q2) and a composed unit's declaration
|
|
317
|
+
// (registry point, entry 20260904.02). Neither source = the OSS
|
|
318
|
+
// trusted-local posture, byte-identical wire behavior. Resolved in the
|
|
319
|
+
// storage stage because the posture decides a WRITE below (the operator
|
|
320
|
+
// account); the verifier chain it also governs is built at the end.
|
|
321
|
+
const authEnabled = config.oidcIssuer !== "";
|
|
322
|
+
const requireAuthentication =
|
|
323
|
+
authEnabled || extensions.requireAuthentication !== undefined;
|
|
324
|
+
|
|
325
|
+
// Stage: identity accounts (20260911.11) — the first domain whose
|
|
326
|
+
// persistence is a PORT rather than the generic Store. The composed
|
|
327
|
+
// driver (`drivers.identityAccountStore`, Q-IA-9) serves when a unit
|
|
328
|
+
// registers one; otherwise open source's adapter over `store` installs
|
|
329
|
+
// HERE (the default lives with the consumer, never in the registry).
|
|
330
|
+
// This ONE binding is what the create path, the provisioner, the
|
|
331
|
+
// operator ensure and both OSS verifiers read, so a composed driver is
|
|
332
|
+
// followed everywhere by construction. The domain's one create path is
|
|
333
|
+
// built once and shared by the create RPC, provisioning and the operator
|
|
334
|
+
// ensure below.
|
|
335
|
+
const identityAccounts =
|
|
336
|
+
extensions.drivers.identityAccountStore ??
|
|
337
|
+
newResourceIdentityAccountStore(store);
|
|
338
|
+
const identityAccountPath = {
|
|
339
|
+
accounts: identityAccounts,
|
|
340
|
+
logger,
|
|
341
|
+
authorizer,
|
|
342
|
+
authorizationLifecycle,
|
|
343
|
+
};
|
|
344
|
+
const createIdentityAccount = newCreateAccountPath(
|
|
345
|
+
identityAccountPath,
|
|
346
|
+
ApiResourceKind.identity_account,
|
|
347
|
+
);
|
|
348
|
+
// ONE issuer discovery for every lane that reads a well-known document
|
|
349
|
+
// (A8): provisioning's userinfo client here and the OIDC verifier's
|
|
350
|
+
// JWKS location below share it, so an issuer is discovered once per
|
|
351
|
+
// process and validated the same way wherever it is consumed. Built in
|
|
352
|
+
// this stage, not inside routes() (which runs twice) — the same reason
|
|
353
|
+
// the searchRegistry lives here.
|
|
354
|
+
const issuerDiscovery = newIssuerDiscovery();
|
|
355
|
+
// Provisioning reads the caller's profile from the userinfo endpoint of
|
|
356
|
+
// the issuer that vouched for the token, discovered per issuer and
|
|
357
|
+
// cached (A8) — so a composition whose own verifiers name their issuer
|
|
358
|
+
// on the identity needs no knob here.
|
|
359
|
+
const identityAccountProvisioner = newDirectAccountProvisioner({
|
|
360
|
+
accounts: identityAccounts,
|
|
361
|
+
createAccount: createIdentityAccount,
|
|
362
|
+
userInfo: newOidcUserInfoClient(issuerDiscovery),
|
|
363
|
+
});
|
|
364
|
+
// Under the trusted-local posture the server knows its one principal
|
|
365
|
+
// from the operator seam (the SAME seam the interceptor stamps callers
|
|
366
|
+
// from — never config directly, so the row and the stamp cannot
|
|
367
|
+
// disagree; T01_1_review.md A2, A5) and states the fact at boot:
|
|
368
|
+
// create-if-absent, so preferences set later survive every reboot. A
|
|
369
|
+
// composition with an authentication posture never creates one — its
|
|
370
|
+
// users provision themselves on first sign-in. A stage that writes a
|
|
371
|
+
// resource is new here; it earns its place because it is the truth of
|
|
372
|
+
// that posture, and a failure is a loud boot throw like the storage
|
|
373
|
+
// stage's own.
|
|
374
|
+
if (!requireAuthentication) {
|
|
375
|
+
const operator = await ensureOperatorAccount(
|
|
376
|
+
{ accounts: identityAccounts, createAccount: createIdentityAccount },
|
|
377
|
+
operatorIdentitySnapshot(),
|
|
378
|
+
);
|
|
379
|
+
logger.info("trusted-local operator account ensured", {
|
|
380
|
+
accountId: operator.metadata?.id ?? "",
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
297
384
|
// Stage: keys — the ratified fail-loud boot ASYMMETRY (D2 cross-domain
|
|
298
385
|
// invariants; Go server.go:277-293):
|
|
299
386
|
//
|
|
@@ -799,6 +886,17 @@ export async function composeServer(
|
|
|
799
886
|
authorizationLifecycle,
|
|
800
887
|
listReadScope: extensions.drivers.listReadScope,
|
|
801
888
|
});
|
|
889
|
+
// IdentityAccount (20260911.11): served once by open source in every
|
|
890
|
+
// edition over the store PORT bound in the identity-accounts stage; a
|
|
891
|
+
// composition adds what differs per edition through the registry —
|
|
892
|
+
// the federation capability (the four federated RPC arms) and the
|
|
893
|
+
// provision slot's gate steps (the cloud's personal organization).
|
|
894
|
+
registerIdentityAccountServices(router, {
|
|
895
|
+
...identityAccountPath,
|
|
896
|
+
provisioner: identityAccountProvisioner,
|
|
897
|
+
federation: extensions.drivers.identityFederation,
|
|
898
|
+
gateSteps: extensions.gateSteps,
|
|
899
|
+
});
|
|
802
900
|
registerEnvironmentServices(router, {
|
|
803
901
|
store,
|
|
804
902
|
logger,
|
|
@@ -1092,14 +1190,8 @@ export async function composeServer(
|
|
|
1092
1190
|
const inProcessWiring = createInProcessClients(routes, logger);
|
|
1093
1191
|
inProcess = inProcessWiring.clients;
|
|
1094
1192
|
|
|
1095
|
-
//
|
|
1096
|
-
//
|
|
1097
|
-
// (registry point, entry 20260904.02). Neither source = the OSS
|
|
1098
|
-
// trusted-local posture, byte-identical wire behavior.
|
|
1099
|
-
const authEnabled = config.oidcIssuer !== "";
|
|
1100
|
-
const requireAuthentication =
|
|
1101
|
-
authEnabled || extensions.requireAuthentication !== undefined;
|
|
1102
|
-
// What the posture registers, in Java's ProviderManager order (opaque
|
|
1193
|
+
// What the posture (resolved in the storage stage) registers, in Java's
|
|
1194
|
+
// ProviderManager order (opaque
|
|
1103
1195
|
// keys first — the cheapest claim — then the JWT lanes), ahead of the
|
|
1104
1196
|
// extension entries ("OSS entries first", the registry contract):
|
|
1105
1197
|
// - the API-key verifier rides the POSTURE, not the issuer
|
|
@@ -1108,19 +1200,31 @@ export async function composeServer(
|
|
|
1108
1200
|
// authentication posture must honor the keys it issues. Gating it on
|
|
1109
1201
|
// the issuer left a posture-declaring composition minting keys
|
|
1110
1202
|
// nothing verified.
|
|
1111
|
-
// - the OIDC verifier rides the ISSUER alone: it
|
|
1112
|
-
//
|
|
1113
|
-
//
|
|
1114
|
-
//
|
|
1203
|
+
// - the OIDC verifier rides the ISSUER alone: it is open source's
|
|
1204
|
+
// single-issuer lane, and a composition with verifiers of its own
|
|
1205
|
+
// names their issuers and audiences itself and declares the posture
|
|
1206
|
+
// instead — so the knob and the extension entries never both claim
|
|
1207
|
+
// one JWT.
|
|
1208
|
+
// - both OSS verifiers resolve their subject through the identity-
|
|
1209
|
+
// account domain (20260911.11 Q-IA-2, A6): a provisioned user is
|
|
1210
|
+
// stamped with the account id whichever credential they present,
|
|
1211
|
+
// an unprovisioned one is admitted idp-shaped. They read the SAME
|
|
1212
|
+
// port the domain writes through (`identityAccounts`, the
|
|
1213
|
+
// identity-accounts stage — a composed driver included), so a row
|
|
1214
|
+
// provisioning creates resolves on the very next request.
|
|
1115
1215
|
// The runner's credential in either posture is an operator-minted API
|
|
1116
1216
|
// token via STIGMER_TOKEN (O3 ruling Q3) — no runner-specific verifier.
|
|
1117
1217
|
const identityVerifiers = [
|
|
1118
|
-
...(requireAuthentication
|
|
1218
|
+
...(requireAuthentication
|
|
1219
|
+
? [newApiKeyIdentityVerifier({ store, accounts: identityAccounts })]
|
|
1220
|
+
: []),
|
|
1119
1221
|
...(authEnabled
|
|
1120
1222
|
? [
|
|
1121
1223
|
newOidcIdentityVerifier({
|
|
1122
1224
|
issuer: config.oidcIssuer,
|
|
1123
1225
|
audience: config.oidcAudience,
|
|
1226
|
+
accounts: identityAccounts,
|
|
1227
|
+
discovery: issuerDiscovery,
|
|
1124
1228
|
}),
|
|
1125
1229
|
]
|
|
1126
1230
|
: []),
|
|
@@ -1203,6 +1307,7 @@ export async function composeServer(
|
|
|
1203
1307
|
workflowExecutionStreamBroker,
|
|
1204
1308
|
inProcessTransport: inProcessWiring.transport,
|
|
1205
1309
|
identityVerifiers,
|
|
1310
|
+
routes,
|
|
1206
1311
|
|
|
1207
1312
|
async start(): Promise<number> {
|
|
1208
1313
|
// Temporal boot is NON-fatal end to end (Go server.go): a failed
|