import { Request, Response, NextFunction } from 'express'; import { RBACService } from '../services/rbac.service'; export interface AuthorizedRequest extends Request { user?: { id: string; email: string; subscription: string; roles: string[]; [key: string]: any; }; } /** * Middleware to check if user has required permission */ export function requirePermission(permissionId: string, projectIdParam?: string) { return (req: AuthorizedRequest, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ error: 'Authentication required' }); } const userId = req.user.id; let projectId: string | undefined; // Extract project ID from request parameters if specified if (projectIdParam) { projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string; } // Check if user has the required permission if (!RBACService.hasPermission(userId, permissionId, projectId)) { return res.status(403).json({ error: 'Insufficient permissions', required: permissionId, projectId: projectId || 'global' }); } next(); }; } /** * Middleware to check if user has any of the required permissions */ export function requireAnyPermission(permissionIds: string[], projectIdParam?: string) { return (req: AuthorizedRequest, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ error: 'Authentication required' }); } const userId = req.user.id; let projectId: string | undefined; // Extract project ID from request parameters if specified if (projectIdParam) { projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string; } // Check if user has any of the required permissions if (!RBACService.hasAnyPermission(userId, permissionIds, projectId)) { return res.status(403).json({ error: 'Insufficient permissions', required: `Any of: ${permissionIds.join(', ')}`, projectId: projectId || 'global' }); } next(); }; } /** * Middleware to check if user has all of the required permissions */ export function requireAllPermissions(permissionIds: string[], projectIdParam?: string) { return (req: AuthorizedRequest, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ error: 'Authentication required' }); } const userId = req.user.id; let projectId: string | undefined; // Extract project ID from request parameters if specified if (projectIdParam) { projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string; } // Check if user has all of the required permissions if (!RBACService.hasAllPermissions(userId, permissionIds, projectId)) { return res.status(403).json({ error: 'Insufficient permissions', required: `All of: ${permissionIds.join(', ')}`, projectId: projectId || 'global' }); } next(); }; } /** * Middleware to check if user is admin */ export function requireAdmin() { return requirePermission('admin:system'); } /** * Middleware to check project access */ export function requireProjectAccess(action: 'read' | 'update' | 'delete' | 'share' = 'read', projectIdParam: string = 'projectId') { return (req: AuthorizedRequest, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ error: 'Authentication required' }); } const userId = req.user.id; const projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string; if (!projectId) { return res.status(400).json({ error: 'Project ID required' }); } // Check if user can access the project if (!RBACService.canAccessProject(userId, projectId, action)) { return res.status(403).json({ error: 'Project access denied', action, projectId }); } next(); }; } /** * Middleware to check if user owns the resource or is admin */ export function requireOwnershipOrAdmin(ownerIdParam: string = 'userId') { return (req: AuthorizedRequest, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ error: 'Authentication required' }); } const userId = req.user.id; const resourceOwnerId = req.params[ownerIdParam] || req.body[ownerIdParam] || req.query[ownerIdParam] as string; // Allow if user is the owner or is admin if (userId === resourceOwnerId || RBACService.isAdmin(userId)) { return next(); } return res.status(403).json({ error: 'Access denied - ownership or admin privileges required' }); }; } /** * Utility function to get user permissions for response */ export function getUserPermissionsForResponse(userId: string, projectId?: string) { return { permissions: RBACService.getUserPermissions(userId, projectId).map(p => ({ id: p.id, name: p.name, resource: p.resource, action: p.action })), roles: RBACService.getUserRoles(userId, projectId).map(ur => ({ roleId: ur.roleId, projectId: ur.projectId, grantedAt: ur.grantedAt })), isAdmin: RBACService.isAdmin(userId) }; } /** * Middleware to add user permissions to response */ export function addUserPermissions(projectIdParam?: string) { return (req: AuthorizedRequest, res: Response, next: NextFunction) => { if (req.user) { const userId = req.user.id; let projectId: string | undefined; if (projectIdParam) { projectId = req.params[projectIdParam] || req.body[projectIdParam] || req.query[projectIdParam] as string; } // Add permissions to request for use in route handlers (req as any).userPermissions = getUserPermissionsForResponse(userId, projectId); } next(); }; }