archetecture security fix
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireAdminAuth = requireAdminAuth;
|
||||
exports.requireAdminRole = requireAdminRole;
|
||||
exports.requireFreshAdmin2FA = requireFreshAdmin2FA;
|
||||
const prisma_1 = require("../lib/prisma");
|
||||
const authHelpers_1 = require("./authHelpers");
|
||||
const tokens_1 = require("../security/tokens");
|
||||
const sessionCookies_1 = require("../security/sessionCookies");
|
||||
const ROLE_RANK = {
|
||||
SUPER_ADMIN: 5,
|
||||
ADMIN: 4,
|
||||
SUPPORT: 3,
|
||||
FINANCE: 2,
|
||||
VIEWER: 1,
|
||||
};
|
||||
const ADMIN_2FA_ENROLLMENT_EXEMPT_PATHS = new Set([
|
||||
'/auth/me',
|
||||
'/auth/logout',
|
||||
'/auth/2fa/setup',
|
||||
'/auth/2fa/verify',
|
||||
]);
|
||||
const FRESH_2FA_WINDOW_MS = Number(process.env.ADMIN_FRESH_2FA_WINDOW_MS ?? 10 * 60 * 1000);
|
||||
function is2faEnrollmentExempt(req) {
|
||||
return ADMIN_2FA_ENROLLMENT_EXEMPT_PATHS.has(req.path);
|
||||
}
|
||||
/**
|
||||
* Requires a valid admin session token.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.admin — the full AdminUser record
|
||||
*/
|
||||
async function requireAdminAuth(req, res, next) {
|
||||
const token = (0, authHelpers_1.getAuthToken)(req, (0, sessionCookies_1.getSessionCookieName)('admin'));
|
||||
if (!token)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Admin authentication required');
|
||||
let payload;
|
||||
try {
|
||||
payload = (0, tokens_1.verifyActorToken)(token, 'admin');
|
||||
}
|
||||
catch {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'invalid_token', 'Invalid or expired admin token');
|
||||
}
|
||||
const admin = await prisma_1.prisma.adminUser.findUnique({ where: { id: payload.sub } });
|
||||
if (!admin || !admin.isActive) {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Admin account not found or deactivated');
|
||||
}
|
||||
if (!admin.totpEnabled && !is2faEnrollmentExempt(req)) {
|
||||
return (0, authHelpers_1.sendForbidden)(res, 'admin_2fa_required', 'Admin 2FA enrollment is required before using privileged admin routes');
|
||||
}
|
||||
req.admin = admin;
|
||||
req.adminAuthLast2faAt = typeof payload.last2faAt === 'number' ? payload.last2faAt : undefined;
|
||||
next();
|
||||
}
|
||||
/**
|
||||
* Requires the authenticated admin to have at least `minimumRole`.
|
||||
* Must be applied after `requireAdminAuth`.
|
||||
*/
|
||||
function requireAdminRole(minimumRole) {
|
||||
return (req, res, next) => {
|
||||
const admin = req.admin;
|
||||
if (!admin)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Admin authentication required');
|
||||
const rank = ROLE_RANK[admin.role] ?? 0;
|
||||
const required = ROLE_RANK[minimumRole] ?? 99;
|
||||
if (rank < required) {
|
||||
return (0, authHelpers_1.sendForbidden)(res, 'forbidden', `This action requires the ${minimumRole} role or higher`);
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
function requireFreshAdmin2FA(req, res, next) {
|
||||
const admin = req.admin;
|
||||
if (!admin)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Admin authentication required');
|
||||
if (!admin.totpEnabled) {
|
||||
return (0, authHelpers_1.sendForbidden)(res, 'admin_2fa_required', 'Admin 2FA enrollment is required for this action');
|
||||
}
|
||||
const last2faAt = req.adminAuthLast2faAt;
|
||||
if (!last2faAt || Date.now() - last2faAt > FRESH_2FA_WINDOW_MS) {
|
||||
return (0, authHelpers_1.sendForbidden)(res, 'fresh_2fa_required', 'Fresh admin 2FA verification is required for this action');
|
||||
}
|
||||
next();
|
||||
}
|
||||
//# sourceMappingURL=requireAdminAuth.js.map
|
||||
Reference in New Issue
Block a user