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