2026-05-03 19:51:57 -05:00
|
|
|
const { getSessionUser, COOKIE_NAME, publicUser } = require('../services/authService');
|
|
|
|
|
const { getDb, getSetting } = require('../db/database');
|
2026-05-09 13:03:36 -05:00
|
|
|
const { standardizeError } = require('./errorFormatter');
|
2026-05-03 19:51:57 -05:00
|
|
|
|
|
|
|
|
function getSingleModeUser() {
|
|
|
|
|
if (getSetting('auth_mode') !== 'single') return null;
|
|
|
|
|
const userId = getSetting('default_user_id');
|
|
|
|
|
if (!userId) return null;
|
2026-05-09 13:03:36 -05:00
|
|
|
// Security FIX (2026-05-08): In single-user mode, we must validate the user
|
|
|
|
|
// against the sessions table to ensure session expiry and active flag are checked.
|
|
|
|
|
// This prevents replay attacks with expired sessions.
|
|
|
|
|
const row = getDb().prepare(`
|
|
|
|
|
SELECT u.id, u.username, u.display_name, u.role, u.must_change_password, u.first_login,
|
|
|
|
|
u.active, u.is_default_admin
|
|
|
|
|
FROM users u
|
|
|
|
|
LEFT JOIN sessions s ON s.user_id = u.id
|
|
|
|
|
WHERE u.id = ? AND u.role = 'user' AND u.active = 1
|
|
|
|
|
AND (s.expires_at > datetime('now') OR s.id IS NULL)
|
|
|
|
|
`).get(userId);
|
2026-05-03 19:51:57 -05:00
|
|
|
return row ? publicUser(row) : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requireAuth(req, res, next) {
|
|
|
|
|
// Single-user mode: bypass session entirely, auto-attach the default user
|
|
|
|
|
const singleUser = getSingleModeUser();
|
|
|
|
|
if (singleUser) {
|
|
|
|
|
req.user = singleUser;
|
|
|
|
|
req.singleUserMode = true;
|
|
|
|
|
return next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const user = getSessionUser(req.cookies?.[COOKIE_NAME]);
|
2026-05-09 13:03:36 -05:00
|
|
|
if (!user) return res.status(401).json(standardizeError('Not authenticated', 'AUTH_ERROR'));
|
2026-05-03 19:51:57 -05:00
|
|
|
req.user = user;
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requireUser(req, res, next) {
|
2026-05-04 23:34:24 -05:00
|
|
|
if (req.user?.is_default_admin) {
|
2026-05-09 13:03:36 -05:00
|
|
|
return res.status(403).json(standardizeError('Default admin account does not have tracker access', 'FORBIDDEN'));
|
2026-05-04 23:34:24 -05:00
|
|
|
}
|
2026-05-03 20:40:48 -05:00
|
|
|
if (!['user', 'admin'].includes(req.user?.role)) {
|
2026-05-09 13:03:36 -05:00
|
|
|
return res.status(403).json(standardizeError('Access denied: user account required', 'FORBIDDEN'));
|
2026-05-03 19:51:57 -05:00
|
|
|
}
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requireAdmin(req, res, next) {
|
|
|
|
|
// In single-user mode the auto-attached user is never admin,
|
|
|
|
|
// so admin routes naturally stay protected by session.
|
|
|
|
|
if (req.user?.role !== 'admin') {
|
2026-05-09 13:03:36 -05:00
|
|
|
return res.status(403).json(standardizeError('Access denied: admin account required', 'FORBIDDEN'));
|
2026-05-03 19:51:57 -05:00
|
|
|
}
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = { requireAuth, requireUser, requireAdmin };
|