@specific.dev/spectest 0.88.0 → 0.88.1
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/components/emulate/service.js +3 -4
- package/dist/components/microsoft.d.ts +2 -2
- package/dist/components/microsoft.js +5 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/project-files.js +6 -6
- package/package.json +1 -1
- package/src/components/emulate/entry.mjs +5 -7
- package/src/components/emulate/service.ts +3 -4
- package/src/components/microsoft.ts +7 -8
- package/src/index.ts +1 -1
- package/src/project-files.ts +6 -6
|
@@ -26,11 +26,10 @@
|
|
|
26
26
|
// `entry.mjs` routes by Host header, applies the per-provider path table,
|
|
27
27
|
// and corrects the discovery document. It also re-issues every id_token
|
|
28
28
|
// with one RSA key it generates at boot, and serves that key at the
|
|
29
|
-
// provider's real JWKS URL.
|
|
29
|
+
// provider's real JWKS URL. This normalizes signing and issuer handling:
|
|
30
30
|
// the Google emulator signs HS256 and publishes an empty JWKS (so nothing
|
|
31
|
-
// can verify its token),
|
|
32
|
-
//
|
|
33
|
-
// handled in advance.
|
|
31
|
+
// can verify its token), and providers whose issuer differs from their
|
|
32
|
+
// base URL can declare it explicitly.
|
|
34
33
|
//
|
|
35
34
|
// The Dockerfile is deliberately CONSTANT — every provider package is
|
|
36
35
|
// installed whether or not this component is the one using it. Two reasons:
|
|
@@ -2,8 +2,8 @@ import type { ProviderOptions } from "./emulate/service.js";
|
|
|
2
2
|
export interface MicrosoftOptions extends ProviderOptions {
|
|
3
3
|
/**
|
|
4
4
|
* Directory (tenant) the app signs in against — whatever its authority
|
|
5
|
-
* URL uses.
|
|
6
|
-
*
|
|
5
|
+
* URL uses. Sets the seeded users' and client's tenant as well as the
|
|
6
|
+
* issuer and endpoint paths. Default `"common"`.
|
|
7
7
|
*/
|
|
8
8
|
tenantId?: string;
|
|
9
9
|
}
|
|
@@ -8,16 +8,15 @@
|
|
|
8
8
|
// widening what it serves is a table entry.
|
|
9
9
|
import { emulatorService, oauthClients, resolveUsers } from "./emulate/service.js";
|
|
10
10
|
function spec(users, opts) {
|
|
11
|
-
const
|
|
11
|
+
const tenantId = opts.tenantId || "common";
|
|
12
|
+
const authority = `https://login.microsoftonline.com/${tenantId}`;
|
|
12
13
|
return {
|
|
13
14
|
name: "microsoft",
|
|
14
15
|
module: "@emulators/microsoft",
|
|
15
16
|
pluginExport: "microsoftPlugin",
|
|
16
17
|
baseUrl: "https://login.microsoftonline.com",
|
|
17
18
|
hosts: ["login.microsoftonline.com", "graph.microsoft.com"],
|
|
18
|
-
//
|
|
19
|
-
// that checks the issuer against the authority it configured would
|
|
20
|
-
// reject that; the re-issue step corrects it.
|
|
19
|
+
// Keep the re-issued token aligned with discovery and the seeded tenant.
|
|
21
20
|
issuer: `${authority}/v2.0`,
|
|
22
21
|
discovery: {
|
|
23
22
|
issuer: `${authority}/v2.0`,
|
|
@@ -40,8 +39,8 @@ function spec(users, opts) {
|
|
|
40
39
|
scopes: ["openid", "email", "profile", "User.Read"],
|
|
41
40
|
},
|
|
42
41
|
seed: {
|
|
43
|
-
users: users.map((u) => ({ email: u.email, name: u.name })),
|
|
44
|
-
...oauthClients(opts.client),
|
|
42
|
+
users: users.map((u) => ({ email: u.email, name: u.name, tenant_id: tenantId })),
|
|
43
|
+
...oauthClients(opts.client, { tenant_id: tenantId }),
|
|
45
44
|
},
|
|
46
45
|
};
|
|
47
46
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -823,7 +823,7 @@ export type ServiceImage = {
|
|
|
823
823
|
* Runs in `defineEnvironment`, and in the daemon's runtime `startService`
|
|
824
824
|
* twin, inside the VM, where the repo sits at the project root. Existence
|
|
825
825
|
* checks go through the project-file resolver, so the same rules as
|
|
826
|
-
* `ctx.readProjectFile` apply (a `spectest/.
|
|
826
|
+
* `ctx.readProjectFile` apply (a `spectest/.specignore`d path is refused
|
|
827
827
|
* instead of read stale).
|
|
828
828
|
*/
|
|
829
829
|
export declare function validateServiceImage(serviceName: string, image: ServiceImage): ServiceImage;
|
package/dist/index.js
CHANGED
|
@@ -253,7 +253,7 @@ const BUILD_TARGET_RE = /^[^\s]+$/;
|
|
|
253
253
|
* Runs in `defineEnvironment`, and in the daemon's runtime `startService`
|
|
254
254
|
* twin, inside the VM, where the repo sits at the project root. Existence
|
|
255
255
|
* checks go through the project-file resolver, so the same rules as
|
|
256
|
-
* `ctx.readProjectFile` apply (a `spectest/.
|
|
256
|
+
* `ctx.readProjectFile` apply (a `spectest/.specignore`d path is refused
|
|
257
257
|
* instead of read stale).
|
|
258
258
|
*/
|
|
259
259
|
export function validateServiceImage(serviceName, image) {
|
package/dist/project-files.js
CHANGED
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
// is correct for every file that is IN that hash. It can be behind for the two
|
|
13
13
|
// kinds of file the hash excludes: `spectest/tests/**` (excluded so a test-only
|
|
14
14
|
// edit keeps the fast start) and anything a project lists in
|
|
15
|
-
// `spectest/.
|
|
15
|
+
// `spectest/.specignore`.
|
|
16
16
|
//
|
|
17
17
|
// Hence the rule below: a path under `spectest/` resolves against the app copy
|
|
18
18
|
// first, because that copy is always current — this is what lets a fixture live
|
|
19
19
|
// in `spectest/tests/fixtures/` and still be found after it was added. Any
|
|
20
20
|
// other path resolves against /workspace, which the hash keeps current.
|
|
21
21
|
//
|
|
22
|
-
// The remaining hole is a file that `.
|
|
22
|
+
// The remaining hole is a file that `.specignore` excludes AND that sits outside
|
|
23
23
|
// `spectest/`: /workspace holds whatever the cold start uploaded, so a later
|
|
24
24
|
// edit is invisible. Nothing can repair those bytes at read time, so we refuse
|
|
25
25
|
// to read them instead of returning stale content. The control plane writes the
|
|
@@ -31,8 +31,8 @@ export const WORKSPACE = process.env.SPECTEST_WORKSPACE ?? "/workspace";
|
|
|
31
31
|
/** The app dir; the user's `spectest/` sits directly under it. */
|
|
32
32
|
export const APP_DIR = process.env.SPECTEST_APP_DIR ?? "/opt/spectest/app";
|
|
33
33
|
/** Paths (repo-relative) the warm-template hash skipped because of
|
|
34
|
-
* `spectest/.
|
|
35
|
-
* when the project ships no `.
|
|
34
|
+
* `spectest/.specignore`. Written by the control plane at env start; absent
|
|
35
|
+
* when the project ships no `.specignore`. */
|
|
36
36
|
const ENV_IGNORED_FILE = "/run/spectest-env-ignored.json";
|
|
37
37
|
let _envIgnored;
|
|
38
38
|
function envIgnored() {
|
|
@@ -44,7 +44,7 @@ function envIgnored() {
|
|
|
44
44
|
paths = raw.paths ?? [];
|
|
45
45
|
}
|
|
46
46
|
catch {
|
|
47
|
-
/* No file (no .
|
|
47
|
+
/* No file (no .specignore, or an older env) — nothing to refuse. */
|
|
48
48
|
}
|
|
49
49
|
_envIgnored = new Set(paths);
|
|
50
50
|
return _envIgnored;
|
|
@@ -90,7 +90,7 @@ export function resolveProjectPath(p, opts = {}) {
|
|
|
90
90
|
return app;
|
|
91
91
|
}
|
|
92
92
|
if (envIgnored().has(rel)) {
|
|
93
|
-
throw new Error(`project file "${p}" is excluded by
|
|
93
|
+
throw new Error(`project file "${p}" is excluded by the environment cache ignore rules, so the copy in the VM ` +
|
|
94
94
|
`is whatever a cold start uploaded and can be out of date. Move it under ` +
|
|
95
95
|
`spectest/tests/ (still cache-free, and always re-uploaded), or drop the pattern.`);
|
|
96
96
|
}
|
package/package.json
CHANGED
|
@@ -17,13 +17,11 @@
|
|
|
17
17
|
// origin. Real providers spread them over several hosts. We merge the
|
|
18
18
|
// real URLs in.
|
|
19
19
|
// * JWKS — we serve one RSA public key, for every provider.
|
|
20
|
-
// * id_token — we re-issue it: same claims, RS256, our key, and
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// And any future provider whose issuer does not match its base URL is
|
|
26
|
-
// covered in advance. No signature check is needed on the way in: the
|
|
20
|
+
// * id_token — we re-issue it: same claims, RS256, our key, and an
|
|
21
|
+
// explicit `iss` where the provider declares one. The Google emulator
|
|
22
|
+
// signs HS256 and serves an empty JWKS, so no client can verify its
|
|
23
|
+
// token. Providers whose issuer differs from their base URL can
|
|
24
|
+
// declare it explicitly. No signature check is needed on the way in: the
|
|
27
25
|
// token comes from an in-process function call, not from the network.
|
|
28
26
|
// * the account picker page — each button gets a `data-testid`, so a
|
|
29
27
|
// browser test has a stable locator.
|
|
@@ -26,11 +26,10 @@
|
|
|
26
26
|
// `entry.mjs` routes by Host header, applies the per-provider path table,
|
|
27
27
|
// and corrects the discovery document. It also re-issues every id_token
|
|
28
28
|
// with one RSA key it generates at boot, and serves that key at the
|
|
29
|
-
// provider's real JWKS URL.
|
|
29
|
+
// provider's real JWKS URL. This normalizes signing and issuer handling:
|
|
30
30
|
// the Google emulator signs HS256 and publishes an empty JWKS (so nothing
|
|
31
|
-
// can verify its token),
|
|
32
|
-
//
|
|
33
|
-
// handled in advance.
|
|
31
|
+
// can verify its token), and providers whose issuer differs from their
|
|
32
|
+
// base URL can declare it explicitly.
|
|
34
33
|
//
|
|
35
34
|
// The Dockerfile is deliberately CONSTANT — every provider package is
|
|
36
35
|
// installed whether or not this component is the one using it. Two reasons:
|
|
@@ -13,23 +13,22 @@ import { emulatorService, oauthClients, resolveUsers } from "./emulate/service.j
|
|
|
13
13
|
export interface MicrosoftOptions extends ProviderOptions {
|
|
14
14
|
/**
|
|
15
15
|
* Directory (tenant) the app signs in against — whatever its authority
|
|
16
|
-
* URL uses.
|
|
17
|
-
*
|
|
16
|
+
* URL uses. Sets the seeded users' and client's tenant as well as the
|
|
17
|
+
* issuer and endpoint paths. Default `"common"`.
|
|
18
18
|
*/
|
|
19
19
|
tenantId?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function spec(users: ProviderUser[], opts: MicrosoftOptions): ProviderSpec {
|
|
23
|
-
const
|
|
23
|
+
const tenantId = opts.tenantId || "common";
|
|
24
|
+
const authority = `https://login.microsoftonline.com/${tenantId}`;
|
|
24
25
|
return {
|
|
25
26
|
name: "microsoft",
|
|
26
27
|
module: "@emulators/microsoft",
|
|
27
28
|
pluginExport: "microsoftPlugin",
|
|
28
29
|
baseUrl: "https://login.microsoftonline.com",
|
|
29
30
|
hosts: ["login.microsoftonline.com", "graph.microsoft.com"],
|
|
30
|
-
//
|
|
31
|
-
// that checks the issuer against the authority it configured would
|
|
32
|
-
// reject that; the re-issue step corrects it.
|
|
31
|
+
// Keep the re-issued token aligned with discovery and the seeded tenant.
|
|
33
32
|
issuer: `${authority}/v2.0`,
|
|
34
33
|
discovery: {
|
|
35
34
|
issuer: `${authority}/v2.0`,
|
|
@@ -52,8 +51,8 @@ function spec(users: ProviderUser[], opts: MicrosoftOptions): ProviderSpec {
|
|
|
52
51
|
scopes: ["openid", "email", "profile", "User.Read"],
|
|
53
52
|
},
|
|
54
53
|
seed: {
|
|
55
|
-
users: users.map((u) => ({ email: u.email, name: u.name })),
|
|
56
|
-
...oauthClients(opts.client),
|
|
54
|
+
users: users.map((u) => ({ email: u.email, name: u.name, tenant_id: tenantId })),
|
|
55
|
+
...oauthClients(opts.client, { tenant_id: tenantId }),
|
|
57
56
|
},
|
|
58
57
|
};
|
|
59
58
|
}
|
package/src/index.ts
CHANGED
|
@@ -1254,7 +1254,7 @@ const BUILD_TARGET_RE = /^[^\s]+$/;
|
|
|
1254
1254
|
* Runs in `defineEnvironment`, and in the daemon's runtime `startService`
|
|
1255
1255
|
* twin, inside the VM, where the repo sits at the project root. Existence
|
|
1256
1256
|
* checks go through the project-file resolver, so the same rules as
|
|
1257
|
-
* `ctx.readProjectFile` apply (a `spectest/.
|
|
1257
|
+
* `ctx.readProjectFile` apply (a `spectest/.specignore`d path is refused
|
|
1258
1258
|
* instead of read stale).
|
|
1259
1259
|
*/
|
|
1260
1260
|
export function validateServiceImage(serviceName: string, image: ServiceImage): ServiceImage {
|
package/src/project-files.ts
CHANGED
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
// is correct for every file that is IN that hash. It can be behind for the two
|
|
13
13
|
// kinds of file the hash excludes: `spectest/tests/**` (excluded so a test-only
|
|
14
14
|
// edit keeps the fast start) and anything a project lists in
|
|
15
|
-
// `spectest/.
|
|
15
|
+
// `spectest/.specignore`.
|
|
16
16
|
//
|
|
17
17
|
// Hence the rule below: a path under `spectest/` resolves against the app copy
|
|
18
18
|
// first, because that copy is always current — this is what lets a fixture live
|
|
19
19
|
// in `spectest/tests/fixtures/` and still be found after it was added. Any
|
|
20
20
|
// other path resolves against /workspace, which the hash keeps current.
|
|
21
21
|
//
|
|
22
|
-
// The remaining hole is a file that `.
|
|
22
|
+
// The remaining hole is a file that `.specignore` excludes AND that sits outside
|
|
23
23
|
// `spectest/`: /workspace holds whatever the cold start uploaded, so a later
|
|
24
24
|
// edit is invisible. Nothing can repair those bytes at read time, so we refuse
|
|
25
25
|
// to read them instead of returning stale content. The control plane writes the
|
|
@@ -34,8 +34,8 @@ export const WORKSPACE = process.env.SPECTEST_WORKSPACE ?? "/workspace";
|
|
|
34
34
|
export const APP_DIR = process.env.SPECTEST_APP_DIR ?? "/opt/spectest/app";
|
|
35
35
|
|
|
36
36
|
/** Paths (repo-relative) the warm-template hash skipped because of
|
|
37
|
-
* `spectest/.
|
|
38
|
-
* when the project ships no `.
|
|
37
|
+
* `spectest/.specignore`. Written by the control plane at env start; absent
|
|
38
|
+
* when the project ships no `.specignore`. */
|
|
39
39
|
const ENV_IGNORED_FILE = "/run/spectest-env-ignored.json";
|
|
40
40
|
|
|
41
41
|
let _envIgnored: Set<string> | undefined;
|
|
@@ -46,7 +46,7 @@ function envIgnored(): Set<string> {
|
|
|
46
46
|
const raw = JSON.parse(readFileSync(ENV_IGNORED_FILE, "utf8")) as { paths?: string[] };
|
|
47
47
|
paths = raw.paths ?? [];
|
|
48
48
|
} catch {
|
|
49
|
-
/* No file (no .
|
|
49
|
+
/* No file (no .specignore, or an older env) — nothing to refuse. */
|
|
50
50
|
}
|
|
51
51
|
_envIgnored = new Set(paths);
|
|
52
52
|
return _envIgnored;
|
|
@@ -94,7 +94,7 @@ export function resolveProjectPath(p: string, opts: { record?: boolean } = {}):
|
|
|
94
94
|
}
|
|
95
95
|
if (envIgnored().has(rel)) {
|
|
96
96
|
throw new Error(
|
|
97
|
-
`project file "${p}" is excluded by
|
|
97
|
+
`project file "${p}" is excluded by the environment cache ignore rules, so the copy in the VM ` +
|
|
98
98
|
`is whatever a cold start uploaded and can be out of date. Move it under ` +
|
|
99
99
|
`spectest/tests/ (still cache-free, and always re-uploaded), or drop the pattern.`,
|
|
100
100
|
);
|