archetecture security fix
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
import { Request, Response } from 'express';
|
||||
/**
|
||||
* Sends a uniform auth-error JSON response.
|
||||
* All auth middleware must go through this function so the shape is identical
|
||||
* to what the errorMiddleware produces for AppError instances.
|
||||
*/
|
||||
export declare function sendUnauthorized(res: Response, error: string, message: string): Response<any, Record<string, any>>;
|
||||
export declare function getCookie(req: Request, name: string): string | null;
|
||||
export declare function getBearerToken(req: Request): string | null;
|
||||
export declare function getAuthToken(req: Request, cookieName?: string): string | null;
|
||||
export declare function sendForbidden(res: Response, error: string, message: string, extra?: Record<string, unknown>): Response<any, Record<string, any>>;
|
||||
export declare function sendPaymentRequired(res: Response, error: string, message: string, extra?: Record<string, unknown>): Response<any, Record<string, any>>;
|
||||
//# sourceMappingURL=authHelpers.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"authHelpers.d.ts","sourceRoot":"","sources":["../../src/middleware/authHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAE3C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,sCAE7E;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYnE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK1D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7E;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,sCAE3G;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,sCAEjH"}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.sendUnauthorized = sendUnauthorized;
|
||||
exports.getCookie = getCookie;
|
||||
exports.getBearerToken = getBearerToken;
|
||||
exports.getAuthToken = getAuthToken;
|
||||
exports.sendForbidden = sendForbidden;
|
||||
exports.sendPaymentRequired = sendPaymentRequired;
|
||||
/**
|
||||
* Sends a uniform auth-error JSON response.
|
||||
* All auth middleware must go through this function so the shape is identical
|
||||
* to what the errorMiddleware produces for AppError instances.
|
||||
*/
|
||||
function sendUnauthorized(res, error, message) {
|
||||
return res.status(401).json({ error, message, statusCode: 401 });
|
||||
}
|
||||
function getCookie(req, name) {
|
||||
const cookieHeader = req.headers.cookie;
|
||||
if (!cookieHeader)
|
||||
return null;
|
||||
for (const chunk of cookieHeader.split(';')) {
|
||||
const [rawName, ...rawValue] = chunk.trim().split('=');
|
||||
if (rawName === name) {
|
||||
return decodeURIComponent(rawValue.join('='));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function getBearerToken(req) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader?.startsWith('Bearer '))
|
||||
return null;
|
||||
const token = authHeader.slice(7).trim();
|
||||
return token.length > 0 ? token : null;
|
||||
}
|
||||
function getAuthToken(req, cookieName) {
|
||||
// Prefer HttpOnly cookies over bearer tokens so stale script-readable tokens
|
||||
// cannot shadow a valid server-managed session during migration.
|
||||
return (cookieName ? getCookie(req, cookieName) : null) ?? getBearerToken(req);
|
||||
}
|
||||
function sendForbidden(res, error, message, extra) {
|
||||
return res.status(403).json({ error, message, statusCode: 403, ...extra });
|
||||
}
|
||||
function sendPaymentRequired(res, error, message, extra) {
|
||||
return res.status(402).json({ error, message, statusCode: 402, ...extra });
|
||||
}
|
||||
//# sourceMappingURL=authHelpers.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"authHelpers.js","sourceRoot":"","sources":["../../src/middleware/authHelpers.ts"],"names":[],"mappings":";;AAOA,4CAEC;AAED,8BAYC;AAED,wCAKC;AAED,oCAIC;AAED,sCAEC;AAED,kDAEC;AA1CD;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,GAAa,EAAE,KAAa,EAAE,OAAe;IAC5E,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAA;AAClE,CAAC;AAED,SAAgB,SAAS,CAAC,GAAY,EAAE,IAAY;IAClD,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAA;IACvC,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAA;IAE9B,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACtD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAgB,cAAc,CAAC,GAAY;IACzC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAA;IAC5C,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAA;IACnD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACxC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;AACxC,CAAC;AAED,SAAgB,YAAY,CAAC,GAAY,EAAE,UAAmB;IAC5D,6EAA6E;IAC7E,iEAAiE;IACjE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,CAAA;AAChF,CAAC;AAED,SAAgB,aAAa,CAAC,GAAa,EAAE,KAAa,EAAE,OAAe,EAAE,KAA+B;IAC1G,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAA;AAC5E,CAAC;AAED,SAAgB,mBAAmB,CAAC,GAAa,EAAE,KAAa,EAAE,OAAe,EAAE,KAA+B;IAChH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAA;AAC5E,CAAC"}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export declare const authLimiter: import("express-rate-limit").RateLimitRequestHandler;
|
||||
export declare const apiLimiter: import("express-rate-limit").RateLimitRequestHandler;
|
||||
export declare const publicLimiter: import("express-rate-limit").RateLimitRequestHandler;
|
||||
export declare const adminLimiter: import("express-rate-limit").RateLimitRequestHandler;
|
||||
export declare const actorLimiter: import("express-rate-limit").RateLimitRequestHandler;
|
||||
//# sourceMappingURL=rateLimiter.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimiter.ts"],"names":[],"mappings":"AA0DA,eAAO,MAAM,WAAW,sDAQtB,CAAA;AAGF,eAAO,MAAM,UAAU,sDAarB,CAAA;AAGF,eAAO,MAAM,aAAa,sDAOxB,CAAA;AAGF,eAAO,MAAM,YAAY,sDAUvB,CAAA;AAKF,eAAO,MAAM,YAAY,sDAevB,CAAA"}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.actorLimiter = exports.adminLimiter = exports.publicLimiter = exports.apiLimiter = exports.authLimiter = void 0;
|
||||
const express_rate_limit_1 = __importStar(require("express-rate-limit"));
|
||||
const tokens_1 = require("../security/tokens");
|
||||
const sessionCookies_1 = require("../security/sessionCookies");
|
||||
const SESSION_COOKIE_NAMES = [
|
||||
(0, sessionCookies_1.getSessionCookieName)('admin'),
|
||||
(0, sessionCookies_1.getSessionCookieName)('employee'),
|
||||
(0, sessionCookies_1.getSessionCookieName)('renter'),
|
||||
];
|
||||
function readCookie(cookieHeader, name) {
|
||||
if (!cookieHeader)
|
||||
return null;
|
||||
for (const chunk of cookieHeader.split(';')) {
|
||||
const [rawName, ...rawValue] = chunk.trim().split('=');
|
||||
if (rawName === name)
|
||||
return decodeURIComponent(rawValue.join('='));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function getBearerToken(req) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader?.startsWith('Bearer '))
|
||||
return null;
|
||||
const token = authHeader.slice(7).trim();
|
||||
return token || null;
|
||||
}
|
||||
function getRequestToken(req) {
|
||||
const cookieHeader = req.headers.cookie;
|
||||
for (const name of SESSION_COOKIE_NAMES) {
|
||||
const token = readCookie(cookieHeader, name);
|
||||
if (token)
|
||||
return token;
|
||||
}
|
||||
return getBearerToken(req);
|
||||
}
|
||||
function getAuthenticatedActorKey(req) {
|
||||
const token = getRequestToken(req);
|
||||
if (!token)
|
||||
return null;
|
||||
try {
|
||||
const payload = (0, tokens_1.verifyAnyActorToken)(token);
|
||||
return `${payload.type}:${payload.sub}`;
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// req.ip is already the real client IP when app.set('trust proxy', 1) is configured
|
||||
const getClientIpKey = (req) => (0, express_rate_limit_1.ipKeyGenerator)(req.ip ?? '');
|
||||
// Strict limiter for auth endpoints — prevents brute-force and credential stuffing.
|
||||
// Successful requests (e.g. GET /me profile reads) are skipped so only failed
|
||||
// attempts count toward the cap.
|
||||
exports.authLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 20,
|
||||
standardHeaders: 'draft-7',
|
||||
legacyHeaders: false,
|
||||
skipSuccessfulRequests: true,
|
||||
keyGenerator: (req) => getClientIpKey(req),
|
||||
message: { error: 'too_many_requests', message: 'Too many attempts, please try again later', statusCode: 429 },
|
||||
});
|
||||
// Standard limiter for general authenticated API endpoints
|
||||
exports.apiLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 120,
|
||||
standardHeaders: 'draft-7',
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req) => {
|
||||
const ip = getClientIpKey(req);
|
||||
const actorKey = getAuthenticatedActorKey(req);
|
||||
const companyId = req.companyId ?? '';
|
||||
const renterId = req.renterId ?? '';
|
||||
return `${ip}:${actorKey || companyId || renterId || 'anonymous'}`;
|
||||
},
|
||||
message: { error: 'too_many_requests', message: 'Rate limit exceeded', statusCode: 429 },
|
||||
});
|
||||
// Limiter for public marketplace and site endpoints (no auth)
|
||||
exports.publicLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 60 * 1000,
|
||||
max: 60,
|
||||
standardHeaders: 'draft-7',
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req) => getClientIpKey(req),
|
||||
message: { error: 'too_many_requests', message: 'Rate limit exceeded', statusCode: 429 },
|
||||
});
|
||||
// Tight limiter for admin endpoints
|
||||
exports.adminLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 100,
|
||||
standardHeaders: 'draft-7',
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req) => {
|
||||
const ip = getClientIpKey(req);
|
||||
return `${ip}:${getAuthenticatedActorKey(req) || 'anonymous'}`;
|
||||
},
|
||||
message: { error: 'too_many_requests', message: 'Too many admin requests', statusCode: 429 },
|
||||
});
|
||||
// Applied after authentication so limits can include actor identity rather than
|
||||
// pretending every employee behind the same NAT is the same organism.
|
||||
exports.actorLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 60 * 1000,
|
||||
max: 240,
|
||||
standardHeaders: 'draft-7',
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req) => {
|
||||
const ip = getClientIpKey(req);
|
||||
const actorKey = getAuthenticatedActorKey(req);
|
||||
const companyId = req.companyId ?? '';
|
||||
const employeeId = req.employee?.id ?? '';
|
||||
const renterId = req.renterId ?? '';
|
||||
const adminId = req.admin?.id ?? '';
|
||||
return `${ip}:${companyId}:${actorKey || employeeId || renterId || adminId || 'anonymous'}`;
|
||||
},
|
||||
message: { error: 'too_many_requests', message: 'Authenticated rate limit exceeded', statusCode: 429 },
|
||||
});
|
||||
//# sourceMappingURL=rateLimiter.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../src/middleware/rateLimiter.ts"],"names":[],"mappings":"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"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { Request, Response, NextFunction } from 'express';
|
||||
export declare function requestIdMiddleware(req: Request, res: Response, next: NextFunction): void;
|
||||
//# sourceMappingURL=requestId.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requestId.d.ts","sourceRoot":"","sources":["../../src/middleware/requestId.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE9D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAMlF"}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requestIdMiddleware = requestIdMiddleware;
|
||||
const crypto_1 = __importDefault(require("crypto"));
|
||||
function requestIdMiddleware(req, res, next) {
|
||||
const incoming = req.headers['x-request-id'];
|
||||
const requestId = Array.isArray(incoming) ? incoming[0] : incoming;
|
||||
req.requestId = requestId && requestId.length <= 128 ? requestId : `req_${crypto_1.default.randomUUID()}`;
|
||||
res.setHeader('X-Request-Id', req.requestId);
|
||||
next();
|
||||
}
|
||||
//# sourceMappingURL=requestId.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requestId.js","sourceRoot":"","sources":["../../src/middleware/requestId.ts"],"names":[],"mappings":";;;;;AAGA,kDAMC;AATD,oDAA2B;AAG3B,SAAgB,mBAAmB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACjF,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;IAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;IAClE,GAAG,CAAC,SAAS,GAAG,SAAS,IAAI,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,gBAAM,CAAC,UAAU,EAAE,EAAE,CAAA;IAC/F,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,SAAS,CAAC,CAAA;IAC5C,IAAI,EAAE,CAAA;AACR,CAAC"}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { AdminRole } from '@rentaldrivego/database';
|
||||
/**
|
||||
* Requires a valid admin session token.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.admin — the full AdminUser record
|
||||
*/
|
||||
export declare function requireAdminAuth(req: Request, res: Response, next: NextFunction): Promise<Response<any, Record<string, any>> | undefined>;
|
||||
/**
|
||||
* Requires the authenticated admin to have at least `minimumRole`.
|
||||
* Must be applied after `requireAdminAuth`.
|
||||
*/
|
||||
export declare function requireAdminRole(minimumRole: AdminRole): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
|
||||
export declare function requireFreshAdmin2FA(req: Request, res: Response, next: NextFunction): Response<any, Record<string, any>> | undefined;
|
||||
//# sourceMappingURL=requireAdminAuth.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireAdminAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/requireAdminAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AA0BnD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,2DAwBrF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,SAAS,IAC7C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,oDAaxD;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,kDAanF"}
|
||||
+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
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireAdminAuth.js","sourceRoot":"","sources":["../../src/middleware/requireAdminAuth.ts"],"names":[],"mappings":"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"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
export declare function requireApiKey(req: Request, res: Response, next: NextFunction): Promise<Response<any, Record<string, any>> | undefined>;
|
||||
//# sourceMappingURL=requireApiKey.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireApiKey.d.ts","sourceRoot":"","sources":["../../src/middleware/requireApiKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAIzD,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,2DA8BlF"}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireApiKey = requireApiKey;
|
||||
const prisma_1 = require("../lib/prisma");
|
||||
const apiKeys_1 = require("../security/apiKeys");
|
||||
async function requireApiKey(req, res, next) {
|
||||
const apiKey = req.headers['x-api-key'];
|
||||
if (!apiKey) {
|
||||
return res.status(401).json({ error: 'missing_api_key', message: 'API key required in x-api-key header', statusCode: 401 });
|
||||
}
|
||||
const prefix = (0, apiKeys_1.getApiKeyPrefix)(apiKey);
|
||||
if (!prefix) {
|
||||
return res.status(401).json({ error: 'invalid_api_key', message: 'Invalid API key', statusCode: 401 });
|
||||
}
|
||||
const keyRecord = await prisma_1.prisma.companyApiKey.findUnique({
|
||||
where: { prefix },
|
||||
include: { company: true },
|
||||
});
|
||||
const hashed = (0, apiKeys_1.hashApiKey)(apiKey);
|
||||
if (!keyRecord || keyRecord.revokedAt || !(0, apiKeys_1.timingSafeEqualHex)(hashed, keyRecord.keyHash)) {
|
||||
return res.status(401).json({ error: 'invalid_api_key', message: 'Invalid API key', statusCode: 401 });
|
||||
}
|
||||
await prisma_1.prisma.companyApiKey.update({
|
||||
where: { id: keyRecord.id },
|
||||
data: { lastUsedAt: new Date() },
|
||||
});
|
||||
req.company = keyRecord.company;
|
||||
req.companyId = keyRecord.companyId;
|
||||
next();
|
||||
}
|
||||
//# sourceMappingURL=requireApiKey.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireApiKey.js","sourceRoot":"","sources":["../../src/middleware/requireApiKey.ts"],"names":[],"mappings":";;AAIA,sCA8BC;AAjCD,0CAAsC;AACtC,iDAAqF;AAE9E,KAAK,UAAU,aAAa,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACjF,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAuB,CAAA;IAE7D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,sCAAsC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAA;IAC7H,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,yBAAe,EAAC,MAAM,CAAC,CAAA;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAA;IACxG,CAAC;IAED,MAAM,SAAS,GAAG,MAAO,eAAc,CAAC,aAAa,CAAC,UAAU,CAAC;QAC/D,KAAK,EAAE,EAAE,MAAM,EAAE;QACjB,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,IAAA,oBAAU,EAAC,MAAM,CAAC,CAAA;IACjC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,SAAS,IAAI,CAAC,IAAA,4BAAkB,EAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QACxF,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAA;IACxG,CAAC;IAED,MAAO,eAAc,CAAC,aAAa,CAAC,MAAM,CAAC;QACzC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE;QAC3B,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE;KACjC,CAAC,CAAA;IAEF,GAAG,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAA;IAC/B,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS,CAAA;IACnC,IAAI,EAAE,CAAA;AACR,CAAC"}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
export declare function requireCompanyAuth(req: Request, res: Response, next: NextFunction): Promise<void | Response<any, Record<string, any>>>;
|
||||
export declare function requireCompanyDocumentAuth(req: Request, res: Response, next: NextFunction): Promise<void | Response<any, Record<string, any>>>;
|
||||
//# sourceMappingURL=requireCompanyAuth.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireCompanyAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/requireCompanyAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AA0CzD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,sDAEvF;AAED,wBAAsB,0BAA0B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,sDAE/F"}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireCompanyAuth = requireCompanyAuth;
|
||||
exports.requireCompanyDocumentAuth = requireCompanyDocumentAuth;
|
||||
const prisma_1 = require("../lib/prisma");
|
||||
const authHelpers_1 = require("./authHelpers");
|
||||
const tokens_1 = require("../security/tokens");
|
||||
const sessionCookies_1 = require("../security/sessionCookies");
|
||||
const rateLimiter_1 = require("./rateLimiter");
|
||||
/**
|
||||
* Validates a company-scoped Bearer token and loads the employee + company onto `req`.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.employee — full employee record (with company relation)
|
||||
* req.company — the employee's company
|
||||
* req.companyId — string shorthand for req.company.id
|
||||
*/
|
||||
async function authenticateCompanyRequest(req, res, next) {
|
||||
const token = (0, authHelpers_1.getAuthToken)(req, (0, sessionCookies_1.getSessionCookieName)('employee'));
|
||||
if (!token)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Authentication required');
|
||||
let payload;
|
||||
try {
|
||||
payload = (0, tokens_1.verifyActorToken)(token, 'employee');
|
||||
}
|
||||
catch {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'invalid_token', 'Invalid or expired session token');
|
||||
}
|
||||
const employee = await prisma_1.prisma.employee.findUnique({
|
||||
where: { id: payload.sub },
|
||||
include: { company: true },
|
||||
});
|
||||
if (!employee || !employee.isActive) {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Employee account not found or inactive');
|
||||
}
|
||||
req.employee = employee;
|
||||
req.company = employee.company;
|
||||
req.companyId = employee.companyId;
|
||||
return (0, rateLimiter_1.actorLimiter)(req, res, next);
|
||||
}
|
||||
async function requireCompanyAuth(req, res, next) {
|
||||
return authenticateCompanyRequest(req, res, next);
|
||||
}
|
||||
async function requireCompanyDocumentAuth(req, res, next) {
|
||||
return authenticateCompanyRequest(req, res, next);
|
||||
}
|
||||
//# sourceMappingURL=requireCompanyAuth.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireCompanyAuth.js","sourceRoot":"","sources":["../../src/middleware/requireCompanyAuth.ts"],"names":[],"mappings":";;AA0CA,gDAEC;AAED,gEAEC;AA/CD,0CAAsC;AACtC,+CAA8D;AAC9D,+CAAqD;AACrD,+DAAiE;AACjE,+CAA4C;AAE5C;;;;;;;GAOG;AACH,KAAK,UAAU,0BAA0B,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACvF,MAAM,KAAK,GAAG,IAAA,0BAAY,EAAC,GAAG,EAAE,IAAA,qCAAoB,EAAC,UAAU,CAAC,CAAC,CAAA;IAEjE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,yBAAyB,CAAC,CAAA;IAEtF,IAAI,OAAsC,CAAA;IAC1C,IAAI,CAAC;QACH,OAAO,GAAG,IAAA,yBAAgB,EAAC,KAAK,EAAE,UAAU,CAAC,CAAA;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,eAAe,EAAE,kCAAkC,CAAC,CAAA;IACnF,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,eAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAChD,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE;QAC1B,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACpC,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,wCAAwC,CAAC,CAAA;IAC3F,CAAC;IAED,GAAG,CAAC,QAAQ,GAAI,QAAQ,CAAA;IACxB,GAAG,CAAC,OAAO,GAAK,QAAQ,CAAC,OAAO,CAAA;IAChC,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAA;IAClC,OAAO,IAAA,0BAAY,EAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;AACrC,CAAC;AAEM,KAAK,UAAU,kBAAkB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACtF,OAAO,0BAA0B,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;AACnD,CAAC;AAEM,KAAK,UAAU,0BAA0B,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC9F,OAAO,0BAA0B,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;AACnD,CAAC"}
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { Request, Response, NextFunction } from 'express';
|
||||
import { type CompanyPolicyAction } from '../security/policies/companyPolicy';
|
||||
export declare function requireCompanyPolicy(action: CompanyPolicyAction): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
|
||||
//# sourceMappingURL=requireCompanyPolicy.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireCompanyPolicy.d.ts","sourceRoot":"","sources":["../../src/middleware/requireCompanyPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAG9D,OAAO,EAAiB,KAAK,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAE5F,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,IACtD,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,oDAexD"}
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireCompanyPolicy = requireCompanyPolicy;
|
||||
const authHelpers_1 = require("./authHelpers");
|
||||
const companyPolicy_1 = require("../security/policies/companyPolicy");
|
||||
function requireCompanyPolicy(action) {
|
||||
return (req, res, next) => {
|
||||
const employee = req.employee;
|
||||
if (!employee)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Authentication required');
|
||||
const allowedRoles = companyPolicy_1.CompanyPolicy[action];
|
||||
if (!allowedRoles.includes(employee.role)) {
|
||||
return (0, authHelpers_1.sendForbidden)(res, 'forbidden', 'You do not have permission to perform this action', {
|
||||
action,
|
||||
allowedRoles,
|
||||
yourRole: employee.role,
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=requireCompanyPolicy.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireCompanyPolicy.js","sourceRoot":"","sources":["../../src/middleware/requireCompanyPolicy.ts"],"names":[],"mappings":";;AAKA,oDAgBC;AApBD,+CAA+D;AAE/D,sEAA4F;AAE5F,SAAgB,oBAAoB,CAAC,MAA2B;IAC9D,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;QAC7B,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,yBAAyB,CAAC,CAAA;QAEzF,MAAM,YAAY,GAA4B,6BAAa,CAAC,MAAM,CAAC,CAAA;QACnE,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,OAAO,IAAA,2BAAa,EAAC,GAAG,EAAE,WAAW,EAAE,mDAAmD,EAAE;gBAC1F,MAAM;gBACN,YAAY;gBACZ,QAAQ,EAAE,QAAQ,CAAC,IAAI;aACxB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,EAAE,CAAA;IACR,CAAC,CAAA;AACH,CAAC"}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
/**
|
||||
* Requires a valid renter Bearer token.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.renterId — the authenticated renter's id
|
||||
*/
|
||||
export declare function requireRenterAuth(req: Request, res: Response, next: NextFunction): Promise<void | Response<any, Record<string, any>>>;
|
||||
/**
|
||||
* Optionally extracts renter identity from a Bearer token.
|
||||
* Never blocks the request — invalid or absent tokens are silently ignored.
|
||||
*
|
||||
* Sets on success:
|
||||
* req.renterId — the authenticated renter's id (if token is valid)
|
||||
*/
|
||||
export declare function optionalRenterAuth(req: Request, _res: Response, next: NextFunction): Promise<void>;
|
||||
//# sourceMappingURL=requireRenterAuth.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireRenterAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/requireRenterAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAOzD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,sDAmBtF;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,iBAYxF"}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireRenterAuth = requireRenterAuth;
|
||||
exports.optionalRenterAuth = optionalRenterAuth;
|
||||
const prisma_1 = require("../lib/prisma");
|
||||
const authHelpers_1 = require("./authHelpers");
|
||||
const tokens_1 = require("../security/tokens");
|
||||
const sessionCookies_1 = require("../security/sessionCookies");
|
||||
const rateLimiter_1 = require("./rateLimiter");
|
||||
/**
|
||||
* Requires a valid renter Bearer token.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.renterId — the authenticated renter's id
|
||||
*/
|
||||
async function requireRenterAuth(req, res, next) {
|
||||
const token = (0, authHelpers_1.getAuthToken)(req, (0, sessionCookies_1.getSessionCookieName)('renter'));
|
||||
if (!token)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Renter authentication required');
|
||||
let payload;
|
||||
try {
|
||||
payload = (0, tokens_1.verifyActorToken)(token, 'renter');
|
||||
}
|
||||
catch {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'invalid_token', 'Invalid or expired token');
|
||||
}
|
||||
const renter = await prisma_1.prisma.renter.findUnique({ where: { id: payload.sub } });
|
||||
if (!renter || !renter.isActive) {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Renter account not found or inactive');
|
||||
}
|
||||
req.renterId = renter.id;
|
||||
return (0, rateLimiter_1.actorLimiter)(req, res, next);
|
||||
}
|
||||
/**
|
||||
* Optionally extracts renter identity from a Bearer token.
|
||||
* Never blocks the request — invalid or absent tokens are silently ignored.
|
||||
*
|
||||
* Sets on success:
|
||||
* req.renterId — the authenticated renter's id (if token is valid)
|
||||
*/
|
||||
async function optionalRenterAuth(req, _res, next) {
|
||||
const token = (0, authHelpers_1.getAuthToken)(req, (0, sessionCookies_1.getSessionCookieName)('renter'));
|
||||
if (!token)
|
||||
return next();
|
||||
try {
|
||||
const payload = (0, tokens_1.verifyActorToken)(token, 'renter');
|
||||
req.renterId = payload.sub;
|
||||
}
|
||||
catch {
|
||||
// Optional — silently ignore invalid tokens
|
||||
}
|
||||
next();
|
||||
}
|
||||
//# sourceMappingURL=requireRenterAuth.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireRenterAuth.js","sourceRoot":"","sources":["../../src/middleware/requireRenterAuth.ts"],"names":[],"mappings":";;AAaA,8CAmBC;AASD,gDAYC;AApDD,0CAAsC;AACtC,+CAA8D;AAC9D,+CAAqD;AACrD,+DAAiE;AACjE,+CAA4C;AAE5C;;;;;GAKG;AACI,KAAK,UAAU,iBAAiB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACrF,MAAM,KAAK,GAAG,IAAA,0BAAY,EAAC,GAAG,EAAE,IAAA,qCAAoB,EAAC,QAAQ,CAAC,CAAC,CAAA;IAE/D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,gCAAgC,CAAC,CAAA;IAE7F,IAAI,OAAsC,CAAA;IAC1C,IAAI,CAAC;QACH,OAAO,GAAG,IAAA,yBAAgB,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,eAAe,EAAE,0BAA0B,CAAC,CAAA;IAC3E,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC7E,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,sCAAsC,CAAC,CAAA;IACzF,CAAC;IAED,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAA;IACxB,OAAO,IAAA,0BAAY,EAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;AACrC,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,kBAAkB,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IACvF,MAAM,KAAK,GAAG,IAAA,0BAAY,EAAC,GAAG,EAAE,IAAA,qCAAoB,EAAC,QAAQ,CAAC,CAAC,CAAA;IAC/D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,EAAE,CAAA;IAEzB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,yBAAgB,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjD,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,4CAA4C;IAC9C,CAAC;IAED,IAAI,EAAE,CAAA;AACR,CAAC"}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { EmployeeRole } from '@rentaldrivego/database';
|
||||
/**
|
||||
* Requires the authenticated employee to have at least `minimumRole`.
|
||||
* Must be applied after `requireCompanyAuth`.
|
||||
*/
|
||||
export declare function requireRole(minimumRole: EmployeeRole): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
|
||||
//# sourceMappingURL=requireRole.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireRole.d.ts","sourceRoot":"","sources":["../../src/middleware/requireRole.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAStD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,YAAY,IAC3C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,oDAgBxD"}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireRole = requireRole;
|
||||
const authHelpers_1 = require("./authHelpers");
|
||||
const ROLE_RANK = {
|
||||
OWNER: 3,
|
||||
MANAGER: 2,
|
||||
AGENT: 1,
|
||||
};
|
||||
/**
|
||||
* Requires the authenticated employee to have at least `minimumRole`.
|
||||
* Must be applied after `requireCompanyAuth`.
|
||||
*/
|
||||
function requireRole(minimumRole) {
|
||||
return (req, res, next) => {
|
||||
const employee = req.employee;
|
||||
if (!employee)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Authentication required');
|
||||
const employeeRank = ROLE_RANK[employee.role] ?? 0;
|
||||
const requiredRank = ROLE_RANK[minimumRole] ?? 99;
|
||||
if (employeeRank < requiredRank) {
|
||||
return (0, authHelpers_1.sendForbidden)(res, 'forbidden', `This action requires the ${minimumRole} role or higher`, {
|
||||
requiredRole: minimumRole,
|
||||
yourRole: employee.role,
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=requireRole.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireRole.js","sourceRoot":"","sources":["../../src/middleware/requireRole.ts"],"names":[],"mappings":";;AAcA,kCAiBC;AA7BD,+CAA+D;AAE/D,MAAM,SAAS,GAAiC;IAC9C,KAAK,EAAI,CAAC;IACV,OAAO,EAAE,CAAC;IACV,KAAK,EAAI,CAAC;CACX,CAAA;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,WAAyB;IACnD,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;QAC7B,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,yBAAyB,CAAC,CAAA;QAEzF,MAAM,YAAY,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClD,MAAM,YAAY,GAAG,SAAS,CAAC,WAAW,CAAC,IAAM,EAAE,CAAA;QAEnD,IAAI,YAAY,GAAG,YAAY,EAAE,CAAC;YAChC,OAAO,IAAA,2BAAa,EAAC,GAAG,EAAE,WAAW,EAAE,4BAA4B,WAAW,iBAAiB,EAAE;gBAC/F,YAAY,EAAE,WAAW;gBACzB,QAAQ,EAAE,QAAQ,CAAC,IAAI;aACxB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,EAAE,CAAA;IACR,CAAC,CAAA;AACH,CAAC"}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
/**
|
||||
* Blocks requests for companies with lapsed or unactivated subscriptions.
|
||||
* Must be applied after `requireTenant`.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.company.status is not SUSPENDED or PENDING
|
||||
*/
|
||||
export declare function requireSubscription(req: Request, res: Response, next: NextFunction): Response<any, Record<string, any>> | undefined;
|
||||
//# sourceMappingURL=requireSubscription.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireSubscription.d.ts","sourceRoot":"","sources":["../../src/middleware/requireSubscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAKzD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,kDAgBlF"}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireSubscription = requireSubscription;
|
||||
const authHelpers_1 = require("./authHelpers");
|
||||
const BLOCKED_STATUSES = ['SUSPENDED', 'PENDING'];
|
||||
/**
|
||||
* Blocks requests for companies with lapsed or unactivated subscriptions.
|
||||
* Must be applied after `requireTenant`.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.company.status is not SUSPENDED or PENDING
|
||||
*/
|
||||
function requireSubscription(req, res, next) {
|
||||
const company = req.company;
|
||||
if (!company)
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'No company context');
|
||||
if (BLOCKED_STATUSES.includes(company.status)) {
|
||||
return (0, authHelpers_1.sendPaymentRequired)(res, `subscription_${company.status.toLowerCase()}`, company.status === 'SUSPENDED'
|
||||
? 'Your account has been suspended. Please contact support or renew your subscription.'
|
||||
: 'Your account is pending activation. Please complete your subscription setup.', { billingUrl: `${process.env.NEXT_PUBLIC_DASHBOARD_URL}/billing` });
|
||||
}
|
||||
next();
|
||||
}
|
||||
//# sourceMappingURL=requireSubscription.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireSubscription.js","sourceRoot":"","sources":["../../src/middleware/requireSubscription.ts"],"names":[],"mappings":";;AAYA,kDAgBC;AA3BD,+CAAqE;AAErE,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;AAEjD;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACjF,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;IAC3B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,oBAAoB,CAAC,CAAA;IAEnF,IAAI,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9C,OAAO,IAAA,iCAAmB,EACxB,GAAG,EACH,gBAAgB,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,EAC9C,OAAO,CAAC,MAAM,KAAK,WAAW;YAC5B,CAAC,CAAC,qFAAqF;YACvF,CAAC,CAAC,8EAA8E,EAClF,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,UAAU,EAAE,CACnE,CAAA;IACH,CAAC;IAED,IAAI,EAAE,CAAA;AACR,CAAC"}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
/**
|
||||
* Loads the company record and validates it exists.
|
||||
* Must be applied after `requireCompanyAuth`.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.company — full Company record
|
||||
* req.companyId — string id (already set by requireCompanyAuth)
|
||||
*/
|
||||
export declare function requireTenant(req: Request, res: Response, next: NextFunction): Promise<Response<any, Record<string, any>> | undefined>;
|
||||
//# sourceMappingURL=requireTenant.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireTenant.d.ts","sourceRoot":"","sources":["../../src/middleware/requireTenant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAIzD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,2DAYlF"}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireTenant = requireTenant;
|
||||
const prisma_1 = require("../lib/prisma");
|
||||
const authHelpers_1 = require("./authHelpers");
|
||||
/**
|
||||
* Loads the company record and validates it exists.
|
||||
* Must be applied after `requireCompanyAuth`.
|
||||
*
|
||||
* Guarantees on success:
|
||||
* req.company — full Company record
|
||||
* req.companyId — string id (already set by requireCompanyAuth)
|
||||
*/
|
||||
async function requireTenant(req, res, next) {
|
||||
if (!req.companyId) {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'unauthenticated', 'Tenant context missing — requireCompanyAuth must run first');
|
||||
}
|
||||
const company = await prisma_1.prisma.company.findUnique({ where: { id: req.companyId } });
|
||||
if (!company) {
|
||||
return (0, authHelpers_1.sendUnauthorized)(res, 'company_not_found', 'Company not found');
|
||||
}
|
||||
req.company = company;
|
||||
next();
|
||||
}
|
||||
//# sourceMappingURL=requireTenant.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"requireTenant.js","sourceRoot":"","sources":["../../src/middleware/requireTenant.ts"],"names":[],"mappings":";;AAYA,sCAYC;AAvBD,0CAAsC;AACtC,+CAAgD;AAEhD;;;;;;;GAOG;AACI,KAAK,UAAU,aAAa,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACjF,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACnB,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,iBAAiB,EAAE,4DAA4D,CAAC,CAAA;IAC/G,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,eAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IACjF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,mBAAmB,EAAE,mBAAmB,CAAC,CAAA;IACxE,CAAC;IAED,GAAG,CAAC,OAAO,GAAG,OAAO,CAAA;IACrB,IAAI,EAAE,CAAA;AACR,CAAC"}
|
||||
Reference in New Issue
Block a user