Upgrade system finished

This commit is contained in:
2026-03-22 21:47:09 -06:00
parent a71ba20176
commit bb1935027d
299 changed files with 8067 additions and 2758 deletions

View File

@@ -1 +1 @@
{"version":3,"file":"auth.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAY1D,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAe5E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAiB5E"}
{"version":3,"file":"auth.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAa1D,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAoB5E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAsB5E"}

View File

@@ -15,8 +15,13 @@ function authenticate(req, _res, next) {
}
const token = header.slice(7);
try {
const payload = jsonwebtoken_1.default.verify(token, env_1.env.JWT_ACCESS_SECRET);
req.user = { id: payload.id, email: payload.email, role: payload.role };
const payload = jsonwebtoken_1.default.verify(token, env_1.env.JWT_ACCESS_SECRET, { algorithms: ['HS256'] });
req.user = {
id: payload.id,
email: payload.email,
role: payload.role,
roles: payload.roles || [payload.role], // Backwards compat: old JWTs without roles
};
next();
}
catch {
@@ -31,8 +36,13 @@ function optionalAuth(req, _res, next) {
}
const token = header.slice(7);
try {
const payload = jsonwebtoken_1.default.verify(token, env_1.env.JWT_ACCESS_SECRET);
req.user = { id: payload.id, email: payload.email, role: payload.role };
const payload = jsonwebtoken_1.default.verify(token, env_1.env.JWT_ACCESS_SECRET, { algorithms: ['HS256'] });
req.user = {
id: payload.id,
email: payload.email,
role: payload.role,
roles: payload.roles || [payload.role],
};
}
catch {
// Token invalid — continue without user

View File

@@ -1 +1 @@
{"version":3,"file":"auth.middleware.js","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":";;;;;AAYA,oCAeC;AAED,oCAiBC;AA7CD,gEAA+B;AAE/B,uCAAoC;AACpC,mDAA2C;AAQ3C,SAAgB,YAAY,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,SAAG,CAAC,iBAAiB,CAAiB,CAAC;QACzE,GAAG,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QACxE,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,0BAA0B,EAAE,eAAe,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,SAAgB,YAAY,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,SAAG,CAAC,iBAAiB,CAAiB,CAAC;QACzE,GAAG,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;IAED,IAAI,EAAE,CAAC;AACT,CAAC"}
{"version":3,"file":"auth.middleware.js","sourceRoot":"","sources":["../../src/middleware/auth.middleware.ts"],"names":[],"mappings":";;;;;AAaA,oCAoBC;AAED,oCAsBC;AAxDD,gEAA+B;AAE/B,uCAAoC;AACpC,mDAA2C;AAS3C,SAAgB,YAAY,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,SAAG,CAAC,iBAAiB,EAAE,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAiB,CAAC;QACpG,GAAG,CAAC,IAAI,GAAG;YACT,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,2CAA2C;SACpF,CAAC;QACF,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,0BAA0B,EAAE,eAAe,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,SAAgB,YAAY,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,SAAG,CAAC,iBAAiB,EAAE,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAiB,CAAC;QACpG,GAAG,CAAC,IAAI,GAAG;YACT,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;SACvC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;IAED,IAAI,EAAE,CAAC;AACT,CAAC"}

View File

@@ -6,7 +6,19 @@ export declare const canvassVisitRateLimit: import("express-rate-limit").RateLim
export declare const canvassBulkVisitRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const gpsTrackingRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const canvassGeocodeRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const adTrackingRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const quickJoinRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const authRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const observabilityRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const docsAnalyticsRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const docsCommentAnonRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const docsCommentAuthRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const docsCommentFetchRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const profileViewRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const profileEditRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const profilePhotoRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const profilePasswordRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const eventSubmissionRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const errorReportRateLimit: import("express-rate-limit").RateLimitRequestHandler;
export declare const healthMetricsRateLimit: import("express-rate-limit").RateLimitRequestHandler;
//# sourceMappingURL=rate-limit.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe,sDAe1B,CAAC;AAEH,eAAO,MAAM,cAAc,sDAezB,CAAC;AAEH,eAAO,MAAM,iBAAiB,sDAe5B,CAAC;AAEH,eAAO,MAAM,oBAAoB,sDAe/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,sDAehC,CAAC;AAEH,eAAO,MAAM,yBAAyB,sDAepC,CAAC;AAEH,eAAO,MAAM,oBAAoB,sDAe/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,sDAelC,CAAC;AAEH,eAAO,MAAM,aAAa,sDAexB,CAAC;AAEH,eAAO,MAAM,sBAAsB,sDAejC,CAAC;AAEH,eAAO,MAAM,sBAAsB,sDAejC,CAAC"}
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe,sDAe1B,CAAC;AAEH,eAAO,MAAM,cAAc,sDAezB,CAAC;AAEH,eAAO,MAAM,iBAAiB,sDAe5B,CAAC;AAEH,eAAO,MAAM,oBAAoB,sDAe/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,sDAehC,CAAC;AAEH,eAAO,MAAM,yBAAyB,sDAepC,CAAC;AAEH,eAAO,MAAM,oBAAoB,sDAe/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,sDAelC,CAAC;AAEH,eAAO,MAAM,mBAAmB,sDAe9B,CAAC;AAEH,eAAO,MAAM,kBAAkB,sDAe7B,CAAC;AAEH,eAAO,MAAM,aAAa,sDAexB,CAAC;AAEH,eAAO,MAAM,sBAAsB,sDAejC,CAAC;AAEH,eAAO,MAAM,sBAAsB,sDAejC,CAAC;AAEH,eAAO,MAAM,wBAAwB,sDAenC,CAAC;AAEH,eAAO,MAAM,wBAAwB,sDAenC,CAAC;AAEH,eAAO,MAAM,yBAAyB,sDAepC,CAAC;AAEH,eAAO,MAAM,oBAAoB,sDAe/B,CAAC;AAEH,eAAO,MAAM,oBAAoB,sDAe/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,sDAehC,CAAC;AAEH,eAAO,MAAM,wBAAwB,sDAenC,CAAC;AAEH,eAAO,MAAM,wBAAwB,sDAenC,CAAC;AAEH,eAAO,MAAM,oBAAoB,sDAe/B,CAAC;AAEH,eAAO,MAAM,sBAAsB,sDAejC,CAAC"}

View File

@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.healthMetricsRateLimit = exports.observabilityRateLimit = exports.authRateLimit = exports.canvassGeocodeRateLimit = exports.gpsTrackingRateLimit = exports.canvassBulkVisitRateLimit = exports.canvassVisitRateLimit = exports.shiftSignupRateLimit = exports.responseRateLimit = exports.emailRateLimit = exports.globalRateLimit = void 0;
exports.healthMetricsRateLimit = exports.errorReportRateLimit = exports.eventSubmissionRateLimit = exports.profilePasswordRateLimit = exports.profilePhotoRateLimit = exports.profileEditRateLimit = exports.profileViewRateLimit = exports.docsCommentFetchRateLimit = exports.docsCommentAuthRateLimit = exports.docsCommentAnonRateLimit = exports.docsAnalyticsRateLimit = exports.observabilityRateLimit = exports.authRateLimit = exports.quickJoinRateLimit = exports.adTrackingRateLimit = exports.canvassGeocodeRateLimit = exports.gpsTrackingRateLimit = exports.canvassBulkVisitRateLimit = exports.canvassVisitRateLimit = exports.shiftSignupRateLimit = exports.responseRateLimit = exports.emailRateLimit = exports.globalRateLimit = void 0;
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
const rate_limit_redis_1 = __importDefault(require("rate-limit-redis"));
const redis_1 = require("../config/redis");
@@ -136,6 +136,38 @@ exports.canvassGeocodeRateLimit = (0, express_rate_limit_1.default)({
},
},
});
exports.adTrackingRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 1000, // 1 minute
max: 60, // 60 events/min per IP (generous for scroll-heavy gallery pages)
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:ad-track:',
}),
message: {
error: {
message: 'Too many tracking requests',
code: 'RATE_LIMIT_EXCEEDED',
},
},
});
exports.quickJoinRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 10,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:quick-join:',
}),
message: {
error: {
message: 'Too many join attempts, please try again later',
code: 'QUICK_JOIN_RATE_LIMIT_EXCEEDED',
},
},
});
exports.authRateLimit = (0, express_rate_limit_1.default)({
windowMs: 15 * 60 * 1000,
max: 10, // Reduced from 20 to prevent brute force attacks
@@ -168,6 +200,166 @@ exports.observabilityRateLimit = (0, express_rate_limit_1.default)({
},
},
});
exports.docsAnalyticsRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 1000, // 1 minute
max: 60, // 60 requests/min per IP
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:docs-analytics:',
}),
message: {
error: {
message: 'Too many tracking requests, please slow down',
code: 'DOCS_ANALYTICS_RATE_LIMIT_EXCEEDED',
},
},
});
exports.docsCommentAnonRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:docs-comment-anon:',
}),
message: {
error: {
message: 'Too many anonymous comments, please try again later',
code: 'DOCS_COMMENT_ANON_RATE_LIMIT_EXCEEDED',
},
},
});
exports.docsCommentAuthRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 30,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:docs-comment-auth:',
}),
message: {
error: {
message: 'Too many comments, please try again later',
code: 'DOCS_COMMENT_AUTH_RATE_LIMIT_EXCEEDED',
},
},
});
exports.docsCommentFetchRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 1000, // 1 minute
max: 60,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:docs-comment-fetch:',
}),
message: {
error: {
message: 'Too many comment fetch requests, please slow down',
code: 'DOCS_COMMENT_FETCH_RATE_LIMIT_EXCEEDED',
},
},
});
exports.profileViewRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 1000, // 1 minute
max: 60, // 60 requests per minute (shared across profile, photo, activity endpoints)
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:profile-view:',
}),
message: {
error: {
message: 'Too many profile requests, please try again later',
code: 'PROFILE_VIEW_RATE_LIMIT_EXCEEDED',
},
},
});
exports.profileEditRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 20,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:profile-edit:',
}),
message: {
error: {
message: 'Too many profile edit requests, please try again later',
code: 'PROFILE_EDIT_RATE_LIMIT_EXCEEDED',
},
},
});
exports.profilePhotoRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:profile-photo:',
}),
message: {
error: {
message: 'Too many photo uploads, please try again later',
code: 'PROFILE_PHOTO_RATE_LIMIT_EXCEEDED',
},
},
});
exports.profilePasswordRateLimit = (0, express_rate_limit_1.default)({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // Stricter than auth (10/15min) — profile passwords may be simpler
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:profile-password:',
}),
message: {
error: {
message: 'Too many password attempts, please try again later',
code: 'PROFILE_PASSWORD_RATE_LIMIT_EXCEEDED',
},
},
});
exports.eventSubmissionRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:event-submit:',
}),
message: {
error: {
message: 'Too many event submissions, please try again later',
code: 'EVENT_SUBMISSION_RATE_LIMIT_EXCEEDED',
},
},
});
exports.errorReportRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5,
standardHeaders: true,
legacyHeaders: false,
store: new rate_limit_redis_1.default({
sendCommand: (command, ...args) => redis_1.redis.call(command, ...args),
prefix: 'rl:error-report:',
}),
message: {
error: {
message: 'Too many error reports, please try again later',
code: 'ERROR_REPORT_RATE_LIMIT_EXCEEDED',
},
},
});
exports.healthMetricsRateLimit = (0, express_rate_limit_1.default)({
windowMs: 60 * 1000, // 1 minute
max: 30, // 30 requests per minute

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"rbac.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/rbac.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,QAAQ,EAAE,IACtC,KAAK,OAAO,EAAE,MAAM,QAAQ,EAAE,MAAM,YAAY,UAWzD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAU9E"}
{"version":3,"file":"rbac.middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/rbac.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,QAAQ,EAAE,IACtC,KAAK,OAAO,EAAE,MAAM,QAAQ,EAAE,MAAM,YAAY,UAqBzD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAY9E"}

View File

@@ -9,7 +9,14 @@ function requireRole(...roles) {
if (!req.user) {
throw new error_handler_1.AppError(401, 'Authentication required', 'AUTH_REQUIRED');
}
if (!roles.includes(req.user.role)) {
// Check multi-role array (falls back to single role via auth middleware)
const userRoles = req.user.roles || [req.user.role];
// SUPER_ADMIN bypasses all role checks
if (userRoles.includes(client_1.UserRole.SUPER_ADMIN)) {
return next();
}
const hasRole = userRoles.some(r => roles.includes(r));
if (!hasRole) {
throw new error_handler_1.AppError(403, 'Insufficient permissions', 'FORBIDDEN');
}
next();
@@ -19,7 +26,9 @@ function requireNonTemp(req, _res, next) {
if (!req.user) {
throw new error_handler_1.AppError(401, 'Authentication required', 'AUTH_REQUIRED');
}
if (req.user.role === client_1.UserRole.TEMP) {
const userRoles = req.user.roles || [req.user.role];
// User is "temp only" if their only role is TEMP
if (userRoles.length === 1 && userRoles[0] === client_1.UserRole.TEMP) {
throw new error_handler_1.AppError(403, 'Temporary accounts cannot access this resource', 'TEMP_FORBIDDEN');
}
next();

View File

@@ -1 +1 @@
{"version":3,"file":"rbac.middleware.js","sourceRoot":"","sources":["../../src/middleware/rbac.middleware.ts"],"names":[],"mappings":";;AAIA,kCAYC;AAED,wCAUC;AA3BD,2CAA0C;AAC1C,mDAA2C;AAE3C,SAAgB,WAAW,CAAC,GAAG,KAAiB;IAC9C,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAE,EAAE;QAC1D,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,0BAA0B,EAAE,WAAW,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IAC7E,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,iBAAQ,CAAC,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,gDAAgD,EAAE,gBAAgB,CAAC,CAAC;IAC9F,CAAC;IAED,IAAI,EAAE,CAAC;AACT,CAAC"}
{"version":3,"file":"rbac.middleware.js","sourceRoot":"","sources":["../../src/middleware/rbac.middleware.ts"],"names":[],"mappings":";;AAIA,kCAsBC;AAED,wCAYC;AAvCD,2CAA0C;AAC1C,mDAA2C;AAE3C,SAAgB,WAAW,CAAC,GAAG,KAAiB;IAC9C,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAE,EAAE;QAC1D,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC;QACtE,CAAC;QAED,yEAAyE;QACzE,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEpD,uCAAuC;QACvC,IAAI,SAAS,CAAC,QAAQ,CAAC,iBAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7C,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAEvD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,0BAA0B,EAAE,WAAW,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IAC7E,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,iDAAiD;IACjD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,iBAAQ,CAAC,IAAI,EAAE,CAAC;QAC7D,MAAM,IAAI,wBAAQ,CAAC,GAAG,EAAE,gDAAgD,EAAE,gBAAgB,CAAC,CAAC;IAC9F,CAAC;IAED,IAAI,EAAE,CAAC;AACT,CAAC"}