UNPKG

@brandazm/dynamic-permissions

Version:

A flexible and powerful permissions management system for NestJS applications with built-in security features

191 lines 8.09 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RoleHierarchyService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const permission_entity_1 = require("../models/permission.entity"); const role_hierarchy_exception_1 = require("../exceptions/role-hierarchy.exception"); require("reflect-metadata"); const config_service_1 = require("./config.service"); let RoleHierarchyService = class RoleHierarchyService { constructor(permissionRepository, configService) { this.permissionRepository = permissionRepository; this.configService = configService; this.roleTree = null; this.rolePermissions = new Map(); } async buildRoleTree(hierarchy) { const roles = Object.keys(hierarchy); const childRoles = new Set(roles.flatMap((role) => hierarchy[role].inherits || [])); const rootRoles = roles.filter((role) => !childRoles.has(role)); this.roleTree = await this.buildRoleNode(rootRoles[0], hierarchy); return this.roleTree; } async buildRoleNode(role, hierarchy) { const { level, inherits = [] } = hierarchy[role]; const children = []; for (const childRole of inherits) { const childNode = await this.buildRoleNode(childRole, hierarchy); children.push(childNode); } const permissions = await this.getRolePermissions(role); return { role, level, children, permissions: new Set(permissions.map((p) => String(p.id))), }; } async getInheritedPermissions(role) { if (!this.roleTree) { throw new Error('Role hierarchy not initialized'); } const roleNode = this.findRoleNode(this.roleTree, role); if (!roleNode) { throw new Error(`Role not found: ${role}`); } const permissionIds = new Set(); this.collectPermissions(roleNode, permissionIds); const permissions = await this.permissionRepository.findByIds([...permissionIds]); return permissions.map(this.mapPermissionEntity); } findRoleNode(node, role) { if (node.role === role) { return node; } for (const child of node.children) { const found = this.findRoleNode(child, role); if (found) { return found; } } return null; } collectPermissions(node, collected) { node.permissions.forEach((p) => collected.add(p)); for (const child of node.children) { this.collectPermissions(child, collected); } } async getRolePermissions(role) { const cachedPermissions = this.rolePermissions.get(role); if (cachedPermissions) { const permissions = await this.permissionRepository.findByIds([...cachedPermissions]); return permissions.map(this.mapPermissionEntity); } const permissions = await this.permissionRepository.find({ where: { name: role, is_active: true }, }); this.rolePermissions.set(role, new Set(permissions.map((p) => String(p.id)))); return permissions.map(this.mapPermissionEntity); } validateRoleHierarchy(hierarchy) { const visited = new Set(); const recursionStack = new Set(); const hasCycle = (currentRole) => { var _a; if (recursionStack.has(currentRole)) { return true; } if (visited.has(currentRole)) { return false; } visited.add(currentRole); recursionStack.add(currentRole); const inherits = ((_a = hierarchy[currentRole]) === null || _a === void 0 ? void 0 : _a.inherits) || []; for (const child of inherits) { if (hasCycle(child)) { return true; } } recursionStack.delete(currentRole); return false; }; for (const role of Object.keys(hierarchy)) { if (hasCycle(role)) { return false; } } for (const [parentRole, config] of Object.entries(hierarchy)) { const inherits = config.inherits || []; for (const childRole of inherits) { if (!hierarchy[childRole]) { return false; } if (hierarchy[childRole].level >= config.level) { throw new role_hierarchy_exception_1.RoleHierarchyException(`Invalid role hierarchy: Child role '${childRole}' has higher or equal level than parent role '${parentRole}'`); } } } return true; } validateHierarchy(hierarchy) { const visited = new Set(); const recursionStack = new Set(); const checkCircular = (currentRole) => { var _a; visited.add(currentRole); recursionStack.add(currentRole); const inherits = ((_a = hierarchy[currentRole]) === null || _a === void 0 ? void 0 : _a.inherits) || []; for (const inheritedRole of inherits) { if (!visited.has(inheritedRole)) { checkCircular(inheritedRole); } else if (recursionStack.has(inheritedRole)) { throw new role_hierarchy_exception_1.RoleHierarchyException(`Circular dependency detected in role hierarchy: ${currentRole} -> ${inheritedRole}`); } } recursionStack.delete(currentRole); }; Object.keys(hierarchy).forEach(checkCircular); } clearCache() { this.rolePermissions.clear(); this.roleTree = null; } async getPermissionsByRole(role) { return this.permissionRepository .createQueryBuilder('p') .where('p.name = :role AND p.is_active = :isActive', { role, isActive: true }) .getMany(); } async getRoleHierarchy(role) { const permissions = await this.permissionRepository .createQueryBuilder('p') .where('p.name = :role AND p.is_active = :isActive', { role, isActive: true }) .getMany(); return permissions; } mapPermissionEntity(permission) { return { id: permission.id, name: permission.name, description: permission.description, level: permission.level, is_active: permission.is_active, created_at: permission.created_at, updated_at: permission.updated_at, }; } }; exports.RoleHierarchyService = RoleHierarchyService; exports.RoleHierarchyService = RoleHierarchyService = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(permission_entity_1.PermissionEntity)), __metadata("design:paramtypes", [typeorm_2.Repository, config_service_1.ConfigService]) ], RoleHierarchyService); //# sourceMappingURL=role-hierarchy.service.js.map