skailan-core
Version:
Servicio de autenticación y multitenancy para Skailan.
327 lines • 12.3 kB
JavaScript
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