UNPKG

skailan-core

Version:

Servicio de autenticación y multitenancy para Skailan.

327 lines 12.3 kB
import { RBACService } from "../services/RBACService"; // Instancia global del servicio RBAC let rbacService = null; /** * Inicializar el servicio RBAC */ export function initializeRBAC(prisma) { rbacService = new RBACService(prisma); } /** * Middleware para verificar un permiso específico */ export function requirePermission(resource, action) { return async (req, res, next) => { try { if (!rbacService) { console.error("RBAC Service no inicializado"); res.status(500).json({ error: "Error de configuración del sistema" }); return; } const membership = req.membership; if (!membership) { res.status(403).json({ error: "No tienes permisos para esta acción" }); return; } const organization = req.organization; if (!organization) { res.status(400).json({ error: "Organización no encontrada" }); return; } const validation = await rbacService.checkPermission(membership.userId, organization.id, resource, action); if (!validation.hasPermission) { console.log(`Acceso denegado: ${membership.userId} intentó acceder a ${resource}:${action}`); res.status(403).json({ error: "No tienes permisos para esta acción", requiredPermission: `${resource}:${action}`, userRole: validation.role }); return; } // Agregar información de permisos al request para uso posterior req.rbacContext = { userId: membership.userId, organizationId: organization.id, role: validation.role, permissions: validation.userPermissions }; next(); } catch (error) { console.error("Error en middleware RBAC:", error); res.status(500).json({ error: "Error interno del servidor" }); } }; } /** * Middleware para verificar múltiples permisos (todos requeridos) */ export function requireAllPermissions(permissions) { return async (req, res, next) => { try { if (!rbacService) { console.error("RBAC Service no inicializado"); res.status(500).json({ error: "Error de configuración del sistema" }); return; } const membership = req.membership; if (!membership) { res.status(403).json({ error: "No tienes permisos para esta acción" }); return; } const organization = req.organization; if (!organization) { res.status(400).json({ error: "Organización no encontrada" }); return; } const result = await rbacService.checkMultiplePermissions(membership.userId, organization.id, permissions); if (!result.hasAllPermissions) { const failedPermissions = result.results.filter(r => !r.hasPermission); console.log(`Acceso denegado: ${membership.userId} no tiene todos los permisos requeridos`); res.status(403).json({ error: "No tienes todos los permisos requeridos", failedPermissions: failedPermissions.map(p => p.permission), requiredPermissions: permissions.map(p => `${p.resource}:${p.action}`) }); return; } next(); } catch (error) { console.error("Error en middleware RBAC múltiple:", error); res.status(500).json({ error: "Error interno del servidor" }); } }; } /** * Middleware para verificar múltiples permisos (al menos uno requerido) */ export function requireAnyPermission(permissions) { return async (req, res, next) => { try { if (!rbacService) { console.error("RBAC Service no inicializado"); res.status(500).json({ error: "Error de configuración del sistema" }); return; } const membership = req.membership; if (!membership) { res.status(403).json({ error: "No tienes permisos para esta acción" }); return; } const organization = req.organization; if (!organization) { res.status(400).json({ error: "Organización no encontrada" }); return; } const result = await rbacService.checkMultiplePermissions(membership.userId, organization.id, permissions); if (!result.results.some(r => r.hasPermission)) { console.log(`Acceso denegado: ${membership.userId} no tiene ningún permiso requerido`); res.status(403).json({ error: "No tienes permisos para esta acción", requiredPermissions: permissions.map(p => `${p.resource}:${p.action}`) }); return; } next(); } catch (error) { console.error("Error en middleware RBAC múltiple:", error); res.status(500).json({ error: "Error interno del servidor" }); } }; } /** * Middleware para verificar rol específico o superior */ export function requireRole(allowedRoles) { return async (req, res, next) => { try { const membership = req.membership; if (!membership) { res.status(403).json({ error: "No tienes permisos para esta acción" }); return; } if (allowedRoles.includes(membership.role)) { next(); return; } res.status(403).json({ error: "No tienes el rol requerido para esta acción", requiredRoles: allowedRoles, userRole: membership.role }); } catch (error) { console.error("Error verificando roles:", error); res.status(500).json({ error: "Error interno del servidor" }); } }; } /** * Middleware para verificar rol específico o superior usando jerarquía */ export function requireRoleOrHigher(requiredRole) { return async (req, res, next) => { try { if (!rbacService) { console.error("RBAC Service no inicializado"); res.status(500).json({ error: "Error de configuración del sistema" }); return; } const membership = req.membership; if (!membership) { res.status(403).json({ error: "No tienes permisos para esta acción" }); return; } const organization = req.organization; if (!organization) { res.status(400).json({ error: "Organización no encontrada" }); return; } const hasRole = await rbacService.hasRoleOrHigher(membership.userId, organization.id, requiredRole); if (!hasRole) { res.status(403).json({ error: "No tienes el rol requerido para esta acción", requiredRole, userRole: membership.role }); return; } next(); } catch (error) { console.error("Error verificando jerarquía de roles:", error); res.status(500).json({ error: "Error interno del servidor" }); } }; } /** * Middleware para verificar permisos dinámicos basados en parámetros de ruta */ export function requireDynamicPermission(resourceExtractor, actionExtractor) { return async (req, res, next) => { try { if (!rbacService) { console.error("RBAC Service no inicializado"); res.status(500).json({ error: "Error de configuración del sistema" }); return; } const membership = req.membership; if (!membership) { res.status(403).json({ error: "No tienes permisos para esta acción" }); return; } const organization = req.organization; if (!organization) { res.status(400).json({ error: "Organización no encontrada" }); return; } const resource = resourceExtractor(req); const action = actionExtractor(req); const validation = await rbacService.checkPermission(membership.userId, organization.id, resource, action); if (!validation.hasPermission) { console.log(`Acceso denegado: ${membership.userId} intentó acceder a ${resource}:${action}`); res.status(403).json({ error: "No tienes permisos para esta acción", requiredPermission: `${resource}:${action}`, userRole: validation.role }); return; } next(); } catch (error) { console.error("Error en middleware RBAC dinámico:", error); res.status(500).json({ error: "Error interno del servidor" }); } }; } /** * Middleware para logging de auditoría de permisos */ export function auditPermission(resource, action) { return async (req, res, next) => { const startTime = Date.now(); // Interceptar la respuesta para logging const originalSend = res.send; res.send = function (data) { const duration = Date.now() - startTime; const membership = req.membership; console.log(`[AUDIT] ${membership?.userId || 'unknown'} - ${resource}:${action} - ${res.statusCode} - ${duration}ms`); return originalSend.call(this, data); }; next(); }; } /** * Middleware para obtener contexto RBAC completo */ export function withRBACContext(req, res, next) { try { if (!rbacService) { console.error("RBAC Service no inicializado"); res.status(500).json({ error: "Error de configuración del sistema" }); return; } const membership = req.membership; if (!membership) { res.status(403).json({ error: "No tienes permisos para esta acción" }); return; } const organization = req.organization; if (!organization) { res.status(400).json({ error: "Organización no encontrada" }); return; } // Obtener contexto RBAC completo rbacService.getRBACContext(membership.userId, organization.id) .then(context => { if (context) { req.rbacContext = context; } next(); }) .catch(error => { console.error("Error obteniendo contexto RBAC:", error); next(); }); } catch (error) { console.error("Error en middleware RBAC context:", error); next(); } } /** * Función helper para crear middleware con múltiples verificaciones */ export function createRBACMiddleware(options) { const middlewares = []; // Agregar middleware de auditoría si está habilitado if (options.audit && options.permissions) { for (const permission of options.permissions) { middlewares.push(auditPermission(permission.resource, permission.action)); } } // Agregar middleware de permisos if (options.permissions && options.permissions.length > 0) { if (options.requireAll) { middlewares.push(requireAllPermissions(options.permissions)); } else { middlewares.push(requireAnyPermission(options.permissions)); } } // Agregar middleware de roles if (options.roles && options.roles.length > 0) { middlewares.push(requireRole(options.roles)); } return middlewares; } /** * Obtener el servicio RBAC */ export function getRBACService() { return rbacService; } //# sourceMappingURL=rbacMiddleware.js.map