@open-mercato/cli 0.6.6-develop.5612.1.d382eb2f33 → 0.6.6-develop.5619.1.29f01e2c42
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/lib/init-secrets.js +11 -4
- package/dist/lib/init-secrets.js.map +2 -2
- package/dist/lib/testing/integration.js +14 -0
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/mercato.js +6 -1
- package/dist/mercato.js.map +2 -2
- package/package.json +5 -5
- package/src/lib/__tests__/init-secrets.test.ts +37 -6
- package/src/lib/init-secrets.ts +25 -6
- package/src/lib/testing/integration.ts +14 -0
- package/src/mercato.ts +5 -0
package/dist/lib/init-secrets.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
|
-
const DEFAULT_DERIVED_PASSWORD = "secret";
|
|
4
3
|
function readEnvValue(env, key) {
|
|
5
4
|
const value = env[key];
|
|
6
5
|
if (typeof value !== "string") return void 0;
|
|
@@ -11,20 +10,28 @@ function resolveEmailFromDomain(domain, prefix) {
|
|
|
11
10
|
if (!domain) return null;
|
|
12
11
|
return `${prefix}@${domain}`;
|
|
13
12
|
}
|
|
13
|
+
const DEMO_DERIVED_PASSWORD = "secret";
|
|
14
|
+
let warnedAboutDeprecatedRandomToggle = false;
|
|
14
15
|
function resolveInitDerivedSecrets(options) {
|
|
15
16
|
const env = options.env ?? process.env;
|
|
16
17
|
const envRead = (key) => readEnvValue(env, key);
|
|
17
18
|
const [, domain] = String(options.email ?? "").split("@");
|
|
18
19
|
const adminEmail = envRead("OM_INIT_ADMIN_EMAIL") ?? resolveEmailFromDomain(domain, "admin");
|
|
19
20
|
const employeeEmail = envRead("OM_INIT_EMPLOYEE_EMAIL") ?? resolveEmailFromDomain(domain, "employee");
|
|
20
|
-
|
|
21
|
+
if (parseBooleanToken(envRead("OM_INIT_GENERATE_RANDOM_PASSWORD") ?? "") === true && !warnedAboutDeprecatedRandomToggle) {
|
|
22
|
+
warnedAboutDeprecatedRandomToggle = true;
|
|
23
|
+
console.warn(
|
|
24
|
+
"\u26A0\uFE0F OM_INIT_GENERATE_RANDOM_PASSWORD is deprecated and no longer required: derived admin/employee passwords are always randomized in production when overrides are unset."
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const isProduction = (env.NODE_ENV ?? "").trim().toLowerCase() === "production";
|
|
21
28
|
const randomize = options.randomSource ?? randomBytes;
|
|
22
|
-
const randomSecret = () => randomize(
|
|
29
|
+
const randomSecret = () => randomize(12).toString("base64url");
|
|
23
30
|
const resolvePassword = (key, emailValue) => {
|
|
24
31
|
if (!emailValue) return null;
|
|
25
32
|
const envValue = envRead(key);
|
|
26
33
|
if (envValue) return envValue;
|
|
27
|
-
return
|
|
34
|
+
return isProduction ? randomSecret() : DEMO_DERIVED_PASSWORD;
|
|
28
35
|
};
|
|
29
36
|
return {
|
|
30
37
|
adminEmail,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/init-secrets.ts"],
|
|
4
|
-
"sourcesContent": ["import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { randomBytes } from 'node:crypto'\n\ntype EnvReader = (key: string) => string | undefined\n\
|
|
5
|
-
"mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAI5B,
|
|
4
|
+
"sourcesContent": ["import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { randomBytes } from 'node:crypto'\n\ntype EnvReader = (key: string) => string | undefined\n\nfunction readEnvValue(env: NodeJS.ProcessEnv, key: string): string | undefined {\n const value = env[key]\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\nfunction resolveEmailFromDomain(domain: string | undefined, prefix: string): string | null {\n if (!domain) return null\n return `${prefix}@${domain}`\n}\n\nexport type InitDerivedSecrets = {\n adminEmail: string | null\n employeeEmail: string | null\n adminPassword: string | null\n employeePassword: string | null\n}\n\n// Well-known dev/demo password baked into local bootstrap flows so developers\n// keep the documented \"log in as admin@<domain> with password 'secret'\" UX.\n// Only used when NODE_ENV !== 'production' and the OM_INIT_*_PASSWORD env vars\n// are unset \u2014 production paths always use a random 96-bit secret.\nconst DEMO_DERIVED_PASSWORD = 'secret'\n\nlet warnedAboutDeprecatedRandomToggle = false\n\nexport function resolveInitDerivedSecrets(options: {\n email: string\n env?: NodeJS.ProcessEnv\n randomSource?: (size: number) => Buffer\n}): InitDerivedSecrets {\n const env = options.env ?? process.env\n const envRead: EnvReader = (key) => readEnvValue(env, key)\n const [, domain] = String(options.email ?? '').split('@')\n const adminEmail = envRead('OM_INIT_ADMIN_EMAIL') ?? resolveEmailFromDomain(domain, 'admin')\n const employeeEmail = envRead('OM_INIT_EMPLOYEE_EMAIL') ?? resolveEmailFromDomain(domain, 'employee')\n // OM_INIT_GENERATE_RANDOM_PASSWORD used to be an opt-in toggle for random\n // derived secrets; in production it is now the unconditional behaviour when\n // no override is set. Surface a one-time deprecation warning so existing\n // operators notice their config is no longer required.\n if (parseBooleanToken(envRead('OM_INIT_GENERATE_RANDOM_PASSWORD') ?? '') === true && !warnedAboutDeprecatedRandomToggle) {\n warnedAboutDeprecatedRandomToggle = true\n console.warn(\n '\u26A0\uFE0F OM_INIT_GENERATE_RANDOM_PASSWORD is deprecated and no longer required: derived admin/employee passwords are always randomized in production when overrides are unset.',\n )\n }\n const isProduction = (env.NODE_ENV ?? '').trim().toLowerCase() === 'production'\n const randomize = options.randomSource ?? randomBytes\n const randomSecret = () => randomize(12).toString('base64url')\n const resolvePassword = (key: string, emailValue: string | null) => {\n if (!emailValue) return null\n const envValue = envRead(key)\n if (envValue) return envValue\n // Non-production (dev/test/staging defaults): seed the documented demo\n // password so `yarn dev` / `mercato init` workflows stay predictable.\n // Production: generate a fresh random secret so credentials are never\n // hardcoded and the operator-facing CLI prints the value once.\n return isProduction ? randomSecret() : DEMO_DERIVED_PASSWORD\n }\n\n return {\n adminEmail,\n employeeEmail,\n adminPassword: resolvePassword('OM_INIT_ADMIN_PASSWORD', adminEmail),\n employeePassword: resolvePassword('OM_INIT_EMPLOYEE_PASSWORD', employeeEmail),\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAI5B,SAAS,aAAa,KAAwB,KAAiC;AAC7E,QAAM,QAAQ,IAAI,GAAG;AACrB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEA,SAAS,uBAAuB,QAA4B,QAA+B;AACzF,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,GAAG,MAAM,IAAI,MAAM;AAC5B;AAaA,MAAM,wBAAwB;AAE9B,IAAI,oCAAoC;AAEjC,SAAS,0BAA0B,SAInB;AACrB,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,UAAqB,CAAC,QAAQ,aAAa,KAAK,GAAG;AACzD,QAAM,CAAC,EAAE,MAAM,IAAI,OAAO,QAAQ,SAAS,EAAE,EAAE,MAAM,GAAG;AACxD,QAAM,aAAa,QAAQ,qBAAqB,KAAK,uBAAuB,QAAQ,OAAO;AAC3F,QAAM,gBAAgB,QAAQ,wBAAwB,KAAK,uBAAuB,QAAQ,UAAU;AAKpG,MAAI,kBAAkB,QAAQ,kCAAkC,KAAK,EAAE,MAAM,QAAQ,CAAC,mCAAmC;AACvH,wCAAoC;AACpC,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACA,QAAM,gBAAgB,IAAI,YAAY,IAAI,KAAK,EAAE,YAAY,MAAM;AACnE,QAAM,YAAY,QAAQ,gBAAgB;AAC1C,QAAM,eAAe,MAAM,UAAU,EAAE,EAAE,SAAS,WAAW;AAC7D,QAAM,kBAAkB,CAAC,KAAa,eAA8B;AAClE,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,WAAW,QAAQ,GAAG;AAC5B,QAAI,SAAU,QAAO;AAKrB,WAAO,eAAe,aAAa,IAAI;AAAA,EACzC;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe,gBAAgB,0BAA0B,UAAU;AAAA,IACnE,kBAAkB,gBAAgB,6BAA6B,aAAa;AAAA,EAC9E;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1215,6 +1215,15 @@ function buildReusableEnvironment(baseUrl, databaseUrl, queueBaseDir, captureScr
|
|
|
1215
1215
|
NODE_ENV: "production",
|
|
1216
1216
|
JWT_SECRET: process.env.JWT_SECRET ?? "om-ephemeral-integration-jwt-secret",
|
|
1217
1217
|
OM_SECURITY_MFA_SETUP_SECRET: process.env.OM_SECURITY_MFA_SETUP_SECRET ?? "om-ephemeral-integration-mfa-setup-secret",
|
|
1218
|
+
// Integration probe + tests expect `admin@acme.com / secret` and
|
|
1219
|
+
// `employee@acme.com / secret`. NODE_ENV=production routes derived-user
|
|
1220
|
+
// password resolution through the random-fallback branch unless these
|
|
1221
|
+
// env vars are explicitly set; without the override every fresh
|
|
1222
|
+
// ephemeral run would mint random passwords and the login probe would
|
|
1223
|
+
// never converge. This is the documented production contract: set
|
|
1224
|
+
// OM_INIT_*_PASSWORD to fix the seeded credential.
|
|
1225
|
+
OM_INIT_ADMIN_PASSWORD: process.env.OM_INIT_ADMIN_PASSWORD ?? "secret",
|
|
1226
|
+
OM_INIT_EMPLOYEE_PASSWORD: process.env.OM_INIT_EMPLOYEE_PASSWORD ?? "secret",
|
|
1218
1227
|
OM_INTEGRATION_TEST: "true",
|
|
1219
1228
|
OM_ENABLE_ENTERPRISE_MODULES: enterpriseModulesFlag,
|
|
1220
1229
|
OM_ENABLE_ENTERPRISE_MODULES_SSO: process.env.OM_ENABLE_ENTERPRISE_MODULES_SSO ?? enterpriseModulesFlag,
|
|
@@ -2348,6 +2357,11 @@ async function startEphemeralEnvironment(options) {
|
|
|
2348
2357
|
JWT_SECRET: process.env.JWT_SECRET ?? "om-ephemeral-integration-jwt-secret",
|
|
2349
2358
|
OM_SECURITY_MFA_SETUP_SECRET: process.env.OM_SECURITY_MFA_SETUP_SECRET ?? "om-ephemeral-integration-mfa-setup-secret",
|
|
2350
2359
|
NODE_ENV: "production",
|
|
2360
|
+
// See the auth-probe block above: pin derived-user passwords to the
|
|
2361
|
+
// documented 'secret' so the ephemeral login probe converges under
|
|
2362
|
+
// NODE_ENV=production.
|
|
2363
|
+
OM_INIT_ADMIN_PASSWORD: process.env.OM_INIT_ADMIN_PASSWORD ?? "secret",
|
|
2364
|
+
OM_INIT_EMPLOYEE_PASSWORD: process.env.OM_INIT_EMPLOYEE_PASSWORD ?? "secret",
|
|
2351
2365
|
// Pool sizing for the ephemeral integration runtime. Defaults were once
|
|
2352
2366
|
// very aggressive (max=5, idle=1000) which exposed flaky 'timeout exceeded
|
|
2353
2367
|
// when trying to connect' errors on `progressService.createJob`-backed
|