feat(auth): Story 1.4 — seedAdmin + Fail-Fast auf ADMIN_EMAIL
This commit is contained in:
parent
c1fc60cf02
commit
c994dbf3b5
2 changed files with 55 additions and 0 deletions
|
|
@ -95,8 +95,31 @@ function deleteUserSessions(userId) {
|
||||||
db.prepare('DELETE FROM sessions WHERE user_id = ?').run(userId);
|
db.prepare('DELETE FROM sessions WHERE user_id = ?').run(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legt den Admin aus ADMIN_EMAIL an (is_admin=1) bzw. befördert ihn; idempotent.
|
||||||
|
* Fail-Fast: leere/fehlende/ungültige E-Mail wirft — der Server darf dann nicht starten.
|
||||||
|
* @returns {number} adminUserId
|
||||||
|
*/
|
||||||
|
function seedAdmin(adminEmail) {
|
||||||
|
const email = normalizeEmail(adminEmail || '');
|
||||||
|
if (!email || !email.includes('@')) {
|
||||||
|
throw new Error('ADMIN_EMAIL fehlt oder ist ungültig — Server-Start abgebrochen (Fail-Fast).');
|
||||||
|
}
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
const existing = db.prepare('SELECT id, is_admin FROM users WHERE email = ?').get(email);
|
||||||
|
if (!existing) {
|
||||||
|
const info = db.prepare('INSERT INTO users (email, is_admin, created_at) VALUES (?, 1, ?)').run(email, now);
|
||||||
|
return Number(info.lastInsertRowid);
|
||||||
|
}
|
||||||
|
if (!existing.is_admin) {
|
||||||
|
db.prepare('UPDATE users SET is_admin = 1 WHERE id = ?').run(existing.id);
|
||||||
|
}
|
||||||
|
return Number(existing.id);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
normalizeEmail, hashToken, createLoginToken, consumeLoginToken,
|
normalizeEmail, hashToken, createLoginToken, consumeLoginToken,
|
||||||
createSession, validateSession, deleteSession, deleteUserSessions,
|
createSession, validateSession, deleteSession, deleteUserSessions,
|
||||||
|
seedAdmin,
|
||||||
TOKEN_TTL_MIN, SESSION_TTL_DAYS, SESSION_IDLE_HOURS,
|
TOKEN_TTL_MIN, SESSION_TTL_DAYS, SESSION_IDLE_HOURS,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
32
server/seed.test.js
Normal file
32
server/seed.test.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
const { test } = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'dp-seed-'));
|
||||||
|
const { db } = require('./db');
|
||||||
|
const { seedAdmin } = require('./auth');
|
||||||
|
|
||||||
|
test('Fail-Fast: fehlende/leere/ungültige ADMIN_EMAIL wirft', () => {
|
||||||
|
assert.throws(() => seedAdmin(''), /Fail-Fast|ADMIN_EMAIL/);
|
||||||
|
assert.throws(() => seedAdmin(' '), /Fail-Fast|ADMIN_EMAIL/);
|
||||||
|
assert.throws(() => seedAdmin(undefined), /Fail-Fast|ADMIN_EMAIL/);
|
||||||
|
assert.throws(() => seedAdmin('keinklammeraffe'), /Fail-Fast|ADMIN_EMAIL/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('seedAdmin legt Admin an (normalisiert), idempotent, liefert id', () => {
|
||||||
|
const id1 = seedAdmin(' Admin@Klinik.DE ');
|
||||||
|
const row = db.prepare('SELECT email, is_admin FROM users WHERE id = ?').get(id1);
|
||||||
|
assert.strictEqual(row.email, 'admin@klinik.de');
|
||||||
|
assert.strictEqual(row.is_admin, 1);
|
||||||
|
const id2 = seedAdmin('admin@klinik.de');
|
||||||
|
assert.strictEqual(id2, id1, 'idempotent — kein zweiter Nutzer');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('bestehender Nicht-Admin wird zum Admin befördert', () => {
|
||||||
|
db.prepare('INSERT INTO users (email, is_admin, created_at) VALUES (?,0,?)')
|
||||||
|
.run('later@klinik.de', new Date().toISOString());
|
||||||
|
const id = seedAdmin('later@klinik.de');
|
||||||
|
assert.strictEqual(db.prepare('SELECT is_admin FROM users WHERE id=?').get(id).is_admin, 1);
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue