Import source: backend/src/middleware/adminAuth
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { timingSafeEqual } from 'node:crypto';
|
||||
import type { RequestHandler } from 'express';
|
||||
import { env } from '../config/env.js';
|
||||
import { HttpError } from './errorHandler.js';
|
||||
import { sha256 } from '../utils/crypto.js';
|
||||
|
||||
function safeEqual(left: string, right: string) {
|
||||
const a = Buffer.from(sha256(left));
|
||||
const b = Buffer.from(sha256(right));
|
||||
return a.length === b.length && timingSafeEqual(a, b);
|
||||
}
|
||||
|
||||
export const requireAdmin: RequestHandler = (req, _res, next) => {
|
||||
if (!env.adminApiKey) {
|
||||
throw new HttpError(503, 'ADMIN_DISABLED', 'Admin API is not configured');
|
||||
}
|
||||
|
||||
// IP Whitelisting (defense-in-depth)
|
||||
const allowedIpsStr = process.env.ADMIN_ALLOWED_IPS;
|
||||
if (allowedIpsStr) {
|
||||
const allowedIps = allowedIpsStr.split(',').map((ip) => ip.trim());
|
||||
const clientIp = req.ip ?? req.socket.remoteAddress ?? '';
|
||||
if (!allowedIps.includes(clientIp) && !allowedIps.includes('::ffff:' + clientIp)) {
|
||||
throw new HttpError(403, 'ADMIN_IP_RESTRICTED', 'Admin access denied from this IP address');
|
||||
}
|
||||
}
|
||||
|
||||
const key = req.header('x-admin-key') ?? '';
|
||||
if (!key || !safeEqual(key, env.adminApiKey)) {
|
||||
throw new HttpError(401, 'ADMIN_KEY_REQUIRED', 'Admin API key is required');
|
||||
}
|
||||
next();
|
||||
};
|
||||
Reference in New Issue
Block a user