Phase 19: Local Auth (No-OIDC Mode) #23
@@ -51,16 +51,43 @@ function hashPassword(password: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── CLI arg parsing (no new deps — process.argv only) ───────────────────────────────────
|
// ── CLI arg parsing (no new deps — process.argv only) ───────────────────────────────────
|
||||||
function parseArgs(argv: string[]): Record<string, string> {
|
// WR-01: support both `--key=value` and `--key value`, and parse values EXPLICITLY rather
|
||||||
const result: Record<string, string> = {};
|
// than inferring an empty string whenever the next token starts with '--'. The old heuristic
|
||||||
|
// coerced `--password --foo` (and a legitimately `--`-prefixed or empty password) silently to
|
||||||
|
// ''. Here, known value-taking flags (--username, --password) always consume the next token
|
||||||
|
// verbatim as their value; the only boolean flag (--dry-run) takes no value. This keeps a
|
||||||
|
// password that begins with '--', or an intentionally empty password, intact.
|
||||||
|
const VALUE_FLAGS = new Set(['username', 'password']);
|
||||||
|
const BOOLEAN_FLAGS = new Set(['dry-run']);
|
||||||
|
|
||||||
|
function parseArgs(argv: string[]): Record<string, string | undefined> {
|
||||||
|
const result: Record<string, string | undefined> = {};
|
||||||
for (let i = 0; i < argv.length; i++) {
|
for (let i = 0; i < argv.length; i++) {
|
||||||
const arg = argv[i];
|
const arg = argv[i];
|
||||||
if (arg.startsWith('--')) {
|
if (!arg.startsWith('--')) continue;
|
||||||
const key = arg.slice(2);
|
|
||||||
const value = argv[i + 1] !== undefined && !argv[i + 1].startsWith('--') ? argv[i + 1] : '';
|
const eq = arg.indexOf('=');
|
||||||
result[key] = value;
|
if (eq !== -1) {
|
||||||
if (value) i++; // skip the value token
|
// `--key=value` form — value is everything after the first '=', taken verbatim
|
||||||
|
// (so `--password=--weird` and `--password=` both work correctly).
|
||||||
|
result[arg.slice(2, eq)] = arg.slice(eq + 1);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const key = arg.slice(2);
|
||||||
|
if (BOOLEAN_FLAGS.has(key)) {
|
||||||
|
result[key] = ''; // presence-only flag; detected via hasOwnProperty
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (VALUE_FLAGS.has(key)) {
|
||||||
|
// Consume the NEXT token verbatim as the value — even if it starts with '--' or is
|
||||||
|
// empty. If there is no next token, record undefined (genuinely absent, not '').
|
||||||
|
result[key] = argv[i + 1];
|
||||||
|
if (i + 1 < argv.length) i++; // skip the consumed value token
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Unknown flag — record presence with no value (forward-compatible, no crash).
|
||||||
|
result[key] = '';
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -120,7 +147,8 @@ try {
|
|||||||
// Existing local_credentials row — update password and ensure is_admin
|
// Existing local_credentials row — update password and ensure is_admin
|
||||||
userId = lcRows[0].user_id;
|
userId = lcRows[0].user_id;
|
||||||
await conn.execute('UPDATE users SET is_admin = true, claimed = true WHERE id = ?', [userId]);
|
await conn.execute('UPDATE users SET is_admin = true, claimed = true WHERE id = ?', [userId]);
|
||||||
console.log(`[reset-admin] Found existing user id=${userId} for username="${username}"`);
|
// WR-01: do not echo the username — log only the resolved user id (no credential data).
|
||||||
|
console.log(`[reset-admin] Found existing user id=${userId}`);
|
||||||
} else {
|
} else {
|
||||||
// No existing row — insert a new user
|
// No existing row — insert a new user
|
||||||
const displayName = username;
|
const displayName = username;
|
||||||
@@ -130,7 +158,8 @@ try {
|
|||||||
[displayName],
|
[displayName],
|
||||||
);
|
);
|
||||||
userId = (insertResult as unknown as { insertId: number }).insertId;
|
userId = (insertResult as unknown as { insertId: number }).insertId;
|
||||||
console.log(`[reset-admin] Created new user id=${userId} for username="${username}"`);
|
// WR-01: do not echo the username — log only the resolved user id.
|
||||||
|
console.log(`[reset-admin] Created new user id=${userId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Upsert local_credentials row ─────────────────────────────────────────────────────
|
// ── Upsert local_credentials row ─────────────────────────────────────────────────────
|
||||||
@@ -142,7 +171,7 @@ try {
|
|||||||
[userId, username, passwordHash],
|
[userId, username, passwordHash],
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(`[reset-admin] Local credential upserted for user id=${userId} username="${username}"`);
|
console.log(`[reset-admin] Local credential upserted for user id=${userId}`);
|
||||||
console.log(`[reset-admin] Done. User id=${userId} is now a local admin.`);
|
console.log(`[reset-admin] Done. User id=${userId} is now a local admin.`);
|
||||||
} finally {
|
} finally {
|
||||||
await conn.end();
|
await conn.end();
|
||||||
|
|||||||
Reference in New Issue
Block a user