import { randomUUID } from 'crypto'; import { Router } from 'express'; import db from '../db.js'; import { loadIntegrations } from '../integrations.js'; import { authenticateLdap } from '../ldap-auth.js'; import { badRequest } from '../lib/http.js'; import { hashPassword, verifyPassword } from '../password.js'; const router = Router(); function userPayload(row) { if (!row) return null; return { id: row.id, username: row.username, role: row.role, firstName: row.firstname ?? null, lastName: row.lastname ?? null, }; } router.get('/status', (req, res) => { const count = db.prepare('SELECT COUNT(*) AS c FROM users').get().c; const needsBootstrap = count === 0; if (needsBootstrap) { return res.json({ needsBootstrap: true, loggedIn: false, user: null }); } if (!req.session?.userId) { return res.json({ needsBootstrap: false, loggedIn: false, user: null }); } const u = db .prepare( 'SELECT id, username, firstname, lastname, role, active FROM users WHERE id = ?', ) .get(req.session.userId); if (!u || !u.active) { req.session.destroy(() => {}); return res.json({ needsBootstrap: false, loggedIn: false, user: null }); } res.json({ needsBootstrap: false, loggedIn: true, user: userPayload(u), }); }); router.post('/bootstrap', async (req, res) => { const count = db.prepare('SELECT COUNT(*) AS c FROM users').get().c; if (count > 0) { return res.status(403).json({ message: 'Initialisierung nicht mehr möglich.' }); } const { username, password } = req.body || {}; const un = String(username || '') .trim() .toLowerCase(); if (!un || !password || password.length < 8) { return badRequest(res, 'Benutzername und Passwort (min. 8 Zeichen) erforderlich.'); } const id = randomUUID(); const ph = await hashPassword(password); db.prepare( `INSERT INTO users (id, username, password_hash, role, source, active, updated_at) VALUES (?, ?, ?, 'admin', 'local', 1, datetime('now'))`, ).run(id, un, ph); req.session.userId = id; req.session.role = 'admin'; req.session.username = un; res.status(201).json({ user: userPayload( db.prepare( 'SELECT id, username, firstname, lastname, role FROM users WHERE id = ?', ).get(id), ), }); }); router.post('/login', async (req, res) => { const { username, password } = req.body || {}; const rawUsername = String(username || '').trim(); const unLower = rawUsername.toLowerCase(); if (!rawUsername || !password) { return badRequest(res, 'Benutzername und Passwort erforderlich.'); } const ldapCfg = loadIntegrations().ldap || {}; const useLdap = Boolean(ldapCfg.syncEnabled) && String(ldapCfg.serverUrl || '').trim() !== '' && String(ldapCfg.searchBase || '').trim() !== ''; if (useLdap) { const ar = await authenticateLdap(rawUsername, password, loadIntegrations); if (ar.ok) { const lookup = ar.canonicalUsername || rawUsername; const u = db .prepare('SELECT * FROM users WHERE username = ? COLLATE NOCASE') .get(lookup); if (!u || !u.active) { return res.status(401).json({ message: 'Benutzer nach LDAP nicht in der Anwendung gefunden. Bitte zuerst eine LDAP-Synchronisation ausführen.', }); } req.session.userId = u.id; req.session.role = u.role; req.session.username = u.username; return res.json({ user: userPayload(u), }); } if (!ar.skipLdap) { const u = db .prepare('SELECT * FROM users WHERE username = ? COLLATE NOCASE') .get(unLower); if (u && u.active && u.password_hash) { const pwOk = await verifyPassword(password, u.password_hash); if (pwOk) { req.session.userId = u.id; req.session.role = u.role; req.session.username = u.username; return res.json({ user: userPayload(u), }); } } return res.status(401).json({ message: 'Ungültige Zugangsdaten.' }); } } const u = db.prepare('SELECT * FROM users WHERE username = ?').get(unLower); if (!u || !u.active) { return res.status(401).json({ message: 'Ungültige Zugangsdaten.' }); } if (!u.password_hash) { return res.status(401).json({ message: 'Kein lokales Passwort. LDAP ist nicht konfiguriert oder die Anmeldung ist nur mit Verzeichnis möglich.', }); } const ok = await verifyPassword(password, u.password_hash); if (!ok) return res.status(401).json({ message: 'Ungültige Zugangsdaten.' }); req.session.userId = u.id; req.session.role = u.role; req.session.username = u.username; res.json({ user: userPayload(u), }); }); router.post('/logout', (req, res) => { req.session.destroy((err) => { if (err) return res.status(500).json({ message: 'Abmelden fehlgeschlagen.' }); res.json({ ok: true }); }); }); export default router;