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