UNPKG

skailan-core

Version:

Servicio de autenticación y multitenancy para Skailan.

269 lines 9.14 kB
import { PermissionService } from "./PermissionService"; export class RBACService { prisma; permissionService; cache = {}; config; constructor(prisma, config) { this.prisma = prisma; this.permissionService = new PermissionService(prisma); this.config = { enableCache: true, cacheTTL: 300, // 5 minutos enableAudit: true, defaultPermissions: [], roleHierarchy: { OWNER: ["ADMIN", "MEMBER", "GUEST"], ADMIN: ["MEMBER", "GUEST"], MEMBER: ["GUEST"], GUEST: [], }, ...config, }; } /** * Verificar si un usuario tiene un permiso específico */ async checkPermission(userId, organizationId, resource, action) { const permissionString = `${resource}:${action}`; // Verificar cache si está habilitado if (this.config.enableCache) { const cached = this.getCachedPermissions(userId, organizationId); if (cached) { const hasPermission = cached.some((p) => p.resource === resource && p.action === action); return { hasPermission, requiredPermission: permissionString, userPermissions: cached, role: await this.getUserRole(userId, organizationId), }; } } // Verificar permisos en base de datos const validation = await this.permissionService.checkUserPermission(userId, organizationId, permissionString); // Cachear resultado si está habilitado if (this.config.enableCache && validation.userPermissions.length > 0) { this.cachePermissions(userId, organizationId, validation.userPermissions); } return validation; } /** * Verificar múltiples permisos de una vez */ async checkMultiplePermissions(userId, organizationId, permissions) { const results = await Promise.all(permissions.map(async ({ resource, action }) => { const validation = await this.checkPermission(userId, organizationId, resource, action); return { permission: `${resource}:${action}`, hasPermission: validation.hasPermission, }; })); const hasAllPermissions = results.every((r) => r.hasPermission); return { hasAllPermissions, results, }; } /** * Obtener contexto RBAC completo de un usuario */ async getRBACContext(userId, organizationId) { const membership = await this.prisma.membership.findFirst({ where: { userId, organizationId, status: "ACTIVE", }, }); if (!membership) { return null; } const permissions = await this.permissionService.getUserPermissions(userId, organizationId); return { userId, organizationId, role: membership.role, permissions, }; } /** * Verificar si un usuario tiene un rol específico o superior */ async hasRoleOrHigher(userId, organizationId, requiredRole) { const membership = await this.prisma.membership.findFirst({ where: { userId, organizationId, status: "ACTIVE", }, }); if (!membership) { return false; } const roleHierarchy = this.config.roleHierarchy[membership.role] || []; return (membership.role === requiredRole || roleHierarchy.includes(requiredRole)); } /** * Obtener todos los permisos de un usuario con cache */ async getUserPermissions(userId, organizationId) { // Verificar cache if (this.config.enableCache) { const cached = this.getCachedPermissions(userId, organizationId); if (cached) { return cached; } } // Obtener de base de datos const permissions = await this.permissionService.getUserPermissions(userId, organizationId); // Cachear resultado if (this.config.enableCache) { this.cachePermissions(userId, organizationId, permissions); } return permissions; } /** * Limpiar cache de permisos */ clearCache(userId, organizationId) { if (userId && organizationId) { const key = `${userId}:${organizationId}`; delete this.cache[key]; } else { this.cache = {}; } } /** * Obtener estadísticas de permisos */ async getPermissionStats(organizationId) { const [permissions, rolePermissions] = await Promise.all([ this.prisma.permission.count({ where: { OR: [{ organizationId }, { organizationId: null }], }, }), this.prisma.rolePermission.count({ where: { OR: [{ organizationId }, { organizationId: null }], }, }), ]); const permissionsByResource = await this.prisma.permission.groupBy({ by: ["resource"], where: { OR: [{ organizationId }, { organizationId: null }], }, _count: { resource: true, }, }); const permissionsByRole = await this.prisma.rolePermission.groupBy({ by: ["role"], where: { OR: [{ organizationId }, { organizationId: null }], }, _count: { role: true, }, }); return { totalPermissions: permissions, totalRoleAssignments: rolePermissions, permissionsByResource: permissionsByResource.reduce((acc, item) => { acc[item.resource] = item._count; return acc; }, {}), permissionsByRole: permissionsByRole.reduce((acc, item) => { acc[item.role] = item._count; return acc; }, {}), }; } /** * Validar configuración de permisos */ async validatePermissionConfiguration(organizationId) { const issues = []; // Verificar que todos los roles tengan al menos un permiso const roles = ["OWNER", "ADMIN", "MEMBER", "GUEST"]; for (const role of roles) { const rolePermissions = await this.permissionService.getRolePermissions(role, organizationId); if (rolePermissions.length === 0) { issues.push(`El rol ${role} no tiene permisos asignados`); } } // Verificar permisos huérfanos (sin asignar a ningún rol) const allPermissions = await this.permissionService.getPermissions(organizationId); const assignedPermissions = await this.prisma.rolePermission.findMany({ where: { OR: [{ organizationId }, { organizationId: null }], }, select: { permissionId: true, }, }); const assignedPermissionIds = new Set(assignedPermissions.map((p) => p.permissionId)); const orphanedPermissions = allPermissions.filter((p) => !assignedPermissionIds.has(p.id)); if (orphanedPermissions.length > 0) { issues.push(`${orphanedPermissions.length} permisos no están asignados a ningún rol`); } return { isValid: issues.length === 0, issues, }; } /** * Obtener el rol de un usuario */ async getUserRole(userId, organizationId) { const membership = await this.prisma.membership.findFirst({ where: { userId, organizationId, status: "ACTIVE", }, }); return membership?.role || "MEMBER"; } /** * Obtener permisos del cache */ getCachedPermissions(userId, organizationId) { const key = `${userId}:${organizationId}`; const cached = this.cache[key]; if (cached && Date.now() < cached.expiresAt) { return cached.permissions; } // Limpiar cache expirado if (cached) { delete this.cache[key]; } return null; } /** * Cachear permisos */ cachePermissions(userId, organizationId, permissions) { const key = `${userId}:${organizationId}`; this.cache[key] = { permissions, expiresAt: Date.now() + this.config.cacheTTL * 1000, }; } /** * Obtener el servicio de permisos */ getPermissionService() { return this.permissionService; } /** * Actualizar configuración */ updateConfig(config) { this.config = { ...this.config, ...config }; } } //# sourceMappingURL=RBACService.js.map