172 lines
4.8 KiB
JavaScript
172 lines
4.8 KiB
JavaScript
// middleware/coarseAuthorize.js
|
|
/**
|
|
* Coarse-Grained Authorization Middleware (Route Level)
|
|
*
|
|
* Blocks access if user's role doesn't match required roles for the route
|
|
* Returns 403 Forbidden if unauthorized
|
|
*
|
|
* Route-to-role mapping:
|
|
* /admin/* → ADMIN
|
|
* /users/* → USER, ADMIN
|
|
* /orders/* → USER, ADMIN
|
|
* etc.
|
|
*/
|
|
|
|
/**
|
|
* Route-to-Role Mapping Configuration
|
|
* Maps route patterns to allowed roles
|
|
*/
|
|
const ROUTE_ROLE_MAP = {
|
|
// Admin routes require ADMIN role
|
|
'/admin': ['ADMIN'],
|
|
'/admin/*': ['ADMIN'],
|
|
|
|
// User routes allow USER and ADMIN
|
|
'/users': ['USER', 'ADMIN'],
|
|
'/users/*': ['USER', 'ADMIN'],
|
|
|
|
// Order routes allow USER and ADMIN
|
|
'/orders': ['USER', 'ADMIN'],
|
|
'/orders/*': ['USER', 'ADMIN'],
|
|
|
|
// Listing routes allow USER and ADMIN
|
|
'/listings': ['USER', 'ADMIN'],
|
|
'/listings/*': ['USER', 'ADMIN'],
|
|
|
|
// Location routes allow USER and ADMIN
|
|
'/locations': ['USER', 'ADMIN'],
|
|
'/locations/*': ['USER', 'ADMIN'],
|
|
|
|
// Chat routes allow USER and ADMIN
|
|
'/chat': ['USER', 'ADMIN'],
|
|
'/chat/*': ['USER', 'ADMIN'],
|
|
|
|
// Default: Allow all authenticated users
|
|
'*': ['USER', 'ADMIN'],
|
|
};
|
|
|
|
/**
|
|
* Get required roles for a route
|
|
*/
|
|
function getRequiredRoles(path) {
|
|
// Check exact matches first
|
|
if (ROUTE_ROLE_MAP[path]) {
|
|
return ROUTE_ROLE_MAP[path];
|
|
}
|
|
|
|
// Check wildcard patterns
|
|
for (const [pattern, roles] of Object.entries(ROUTE_ROLE_MAP)) {
|
|
if (pattern.includes('*')) {
|
|
const regexPattern = pattern.replace(/\*/g, '.*');
|
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
if (regex.test(path)) {
|
|
return roles;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default: allow all authenticated users
|
|
return ROUTE_ROLE_MAP['*'] || ['USER', 'ADMIN'];
|
|
}
|
|
|
|
/**
|
|
* Normalize role to standard format
|
|
*/
|
|
function normalizeRole(role) {
|
|
if (!role) return null;
|
|
|
|
// Map common role variations to standard roles
|
|
const roleMap = {
|
|
'user': 'USER',
|
|
'admin': 'ADMIN',
|
|
'USER': 'USER',
|
|
'ADMIN': 'ADMIN',
|
|
'security_admin': 'ADMIN',
|
|
'moderator': 'ADMIN',
|
|
};
|
|
|
|
return roleMap[role.toUpperCase()] || role.toUpperCase();
|
|
}
|
|
|
|
/**
|
|
* Coarse-Grained Authorization Middleware Factory
|
|
*
|
|
* @param {string[]} allowedRoles - Roles allowed to access this route (optional, uses route mapping if not provided)
|
|
* @returns {Function} Express middleware
|
|
*/
|
|
function createCoarseAuthorize(allowedRoles = null) {
|
|
return function coarseAuthorize(req, res, next) {
|
|
try {
|
|
console.log(`[Coarse Auth] Starting authorization check for ${req.method} ${req.path}`);
|
|
|
|
// User should be set by jwtAuthenticate middleware
|
|
if (!req.user || !req.user.userId) {
|
|
console.log(`[Coarse Auth] ❌ FAILED: No user found in request. req.user:`, req.user);
|
|
return res.status(401).json({
|
|
error: 'Unauthorized',
|
|
message: 'Authentication required',
|
|
});
|
|
}
|
|
|
|
console.log(`[Coarse Auth] User found:`, {
|
|
userId: req.user.userId,
|
|
role: req.user.role,
|
|
userType: req.user.userType,
|
|
});
|
|
|
|
const userRole = normalizeRole(req.user.role);
|
|
|
|
// Determine required roles for this route
|
|
const requiredRoles = allowedRoles || getRequiredRoles(req.path);
|
|
const normalizedRequiredRoles = requiredRoles.map(normalizeRole);
|
|
|
|
console.log(`[Coarse Auth] Role check:`, {
|
|
userRole,
|
|
requiredRoles: normalizedRequiredRoles,
|
|
routePath: req.path,
|
|
});
|
|
|
|
// Check if user's role is in the allowed roles list
|
|
if (!normalizedRequiredRoles.includes(userRole)) {
|
|
console.log(`[Coarse Auth] ❌ FAILED: User role "${userRole}" not in required roles:`, normalizedRequiredRoles);
|
|
|
|
// Log authorization failure
|
|
if (req.auditLogger) {
|
|
req.auditLogger.log({
|
|
userId: req.user.userId,
|
|
action: 'authorization_failed',
|
|
route: req.path,
|
|
status: 'forbidden',
|
|
meta: {
|
|
userRole,
|
|
requiredRoles: normalizedRequiredRoles,
|
|
},
|
|
});
|
|
}
|
|
|
|
return res.status(403).json({
|
|
error: 'Forbidden',
|
|
message: `Access denied. Required roles: ${normalizedRequiredRoles.join(', ')}. Your role: ${userRole}`,
|
|
});
|
|
}
|
|
|
|
console.log(`[Coarse Auth] ✅ Authorization passed: User role "${userRole}" is allowed`);
|
|
next();
|
|
} catch (err) {
|
|
console.error('[Coarse Auth] ❌ Authorization error:', err);
|
|
return res.status(500).json({
|
|
error: 'Internal server error',
|
|
message: 'Authorization check failed',
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Pre-configured authorization middlewares for common cases
|
|
*/
|
|
export const requireUser = createCoarseAuthorize(['USER', 'ADMIN']);
|
|
export const requireAdmin = createCoarseAuthorize(['ADMIN']);
|
|
|
|
export default createCoarseAuthorize;
|