mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(auth): reset-admin.js ends the account's sessions and matches its email in any case
Resetting a locked-out or compromised admin left every session on that account valid, because the script did not move password_version on the way every password change in the app does. It also looked the address up case-sensitively and created a second account when the stored one had capitals.
This commit is contained in:
@@ -58,11 +58,14 @@ db.exec(`PRAGMA journal_mode = ${journalMode}`);
|
||||
db.exec(`PRAGMA synchronous = ${synchronous}`);
|
||||
|
||||
const hash = bcrypt.hashSync(password, BCRYPT_COST);
|
||||
const existing = db.prepare('SELECT id FROM users WHERE email = ?').get(email);
|
||||
// Case-insensitive like login, so Admin@example.com is found as admin@example.com.
|
||||
const existing = db.prepare('SELECT id FROM users WHERE LOWER(email) = LOWER(?)').get(email);
|
||||
|
||||
if (existing) {
|
||||
db.prepare('UPDATE users SET password_hash = ?, role = ?, must_change_password = 1 WHERE email = ?')
|
||||
.run(hash, 'admin', email);
|
||||
// password_version moves on, as on every password change in the app: it is
|
||||
// what ends the sessions still signed in to an account that needed resetting.
|
||||
db.prepare('UPDATE users SET password_hash = ?, role = ?, must_change_password = 1, password_version = COALESCE(password_version, 0) + 1 WHERE id = ?')
|
||||
.run(hash, 'admin', existing.id);
|
||||
console.log(`\n✓ Admin password reset: ${email}`);
|
||||
} else {
|
||||
// 'admin' is usually taken by the first-run seed — pick the first free username
|
||||
|
||||
@@ -184,6 +184,7 @@ describe('standalone scripts honour the same configuration', () => {
|
||||
password_hash TEXT,
|
||||
role TEXT,
|
||||
must_change_password INTEGER DEFAULT 0,
|
||||
password_version INTEGER NOT NULL DEFAULT 0,
|
||||
mfa_secret TEXT,
|
||||
maps_api_key TEXT,
|
||||
openweather_api_key TEXT,
|
||||
@@ -226,6 +227,24 @@ describe('standalone scripts honour the same configuration', () => {
|
||||
expect(admin?.role).toBe('admin');
|
||||
}, 60000);
|
||||
|
||||
it('reset-admin.js finds the account whatever the case, and ends its sessions', () => {
|
||||
seedWalDb();
|
||||
const seed = new Database(dbPath);
|
||||
seed.prepare("INSERT INTO users (username, email, password_hash, role, password_version) VALUES ('boss', 'Boss@Example.com', 'x', 'user', 3)").run();
|
||||
seed.close();
|
||||
|
||||
execFileSync(process.execPath, ['reset-admin.js'], {
|
||||
cwd: SERVER_ROOT,
|
||||
env: { ...process.env, TREK_DB_FILE: dbPath, RESET_ADMIN_EMAIL: 'boss@example.com', RESET_ADMIN_PASSWORD: 'Recovery12345!' },
|
||||
stdio: 'pipe',
|
||||
});
|
||||
|
||||
const db = new Database(dbPath);
|
||||
const rows = db.prepare('SELECT email, role, password_version, must_change_password FROM users').all();
|
||||
db.close();
|
||||
expect(rows).toEqual([{ email: 'Boss@Example.com', role: 'admin', password_version: 4, must_change_password: 1 }]);
|
||||
}, 60000);
|
||||
|
||||
it('migrate-encryption.ts no longer forces the database back to WAL', () => {
|
||||
seedWalDb();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user