/
tm-project
/
rbac-react-system
Обзор
Документация
Войти
/
tm-project
/
rbac-react-system
Код
Запросы
0
Пакеты
0
Релизы
1
CI/CD
Аналитика
Безопасность
gitverse
src/application/use-cases/CheckRoleUseCase.ts
167 строк
8 KB
Artem
chore: release v1.0.4
30 ноя 2025, 22:51
30 ноя 2025, 22:51
f6f509c
Код
Авторство
О чём код?
/** * rbac-react-system v1.0.4 * Copyright (c) 2025 TM Project * Licensed under the MIT License */ import { IRoleRepository, IUserRepository } from '../../domain/interfaces' import { CircularRoleInheritanceException } from '../../domain/exceptions' /** * Use case для проверки наличия у пользователя определённой роли. * Реализует проверку ролей с поддержкой иерархии наследования. */ export class CheckRoleUseCase { /** * Создаёт экземпляр use case для проверки ролей. * * @param {IUserRepository} userRepository - Репозиторий для доступа к данным пользователей. * @param {IRoleRepository} roleRepository - Репозиторий для доступа к данным ролей. */ constructor( private readonly userRepository: IUserRepository, private readonly roleRepository: IRoleRepository ) {} /** * Выполняет проверку, имеет ли пользователь указанную роль (напрямую или по наследованию). * * @param {string} userId - Идентификатор пользователя, для которого проверяется наличие роли. * @param {string} roleId - Идентификатор роли, которую необходимо проверить. * @returns {Promise<boolean>} Промис, разрешающийся в `true`, если пользователь имеет указанную роль, иначе — `false`. */ async execute(userId: string, roleId: string): Promise<boolean> { try { // Шаг 1: Получение прямых ролей пользователя const userRoles = await this.userRepository.getUserRoles(userId) // Шаг 2: Проверка, назначена ли роль напрямую if (userRoles.includes(roleId)) { return true } // Шаг 3: Проверка, наследуется ли роль через иерархию return await this.checkInheritedRoles(userRoles, roleId) } catch (error) { // При возникновении ошибки возвращаем false (fail-safe поведение) console.error('Ошибка при проверке роли:', error) return false } } /** * Возвращает все эффективные роли пользователя, включая: * - роли, назначенные напрямую, * - роли, полученные по наследованию. * * @param {string} userId - Идентификатор пользователя, для которого запрашиваются роли. * @returns {Promise<string[]>} Промис, разрешающийся массивом всех идентификаторов ролей. */ async getAllEffectiveRoles(userId: string): Promise<string[]> { try { const userRoles = await this.userRepository.getUserRoles(userId) const allRoles = new Set<string>(userRoles) for (const roleId of userRoles) { const inheritedRoles = await this.collectInheritedRoles(roleId, []) inheritedRoles.forEach(r => allRoles.add(r)) } return Array.from(allRoles) } catch (error) { console.error('Не удалось получить список эффективных ролей:', error) return [] } } /** * Проверяет, наследует ли пользователь целевую роль через иерархию ролей. * * @param {string[]} userRoles - Массив идентификаторов ролей, назначенных пользователю напрямую. * @param {string} targetRoleId - Идентификатор целевой роли, которую нужно найти в иерархии. * @returns {Promise<boolean>} Промис, разрешающийся в `true`, если целевая роль найдена в иерархии, иначе — `false`. */ private async checkInheritedRoles(userRoles: string[], targetRoleId: string): Promise<boolean> { for (const roleId of userRoles) { const hasRole = await this.traverseRoleHierarchy(roleId, targetRoleId, []) if (hasRole) { return true } } return false } /** * Рекурсивно обходит иерархию ролей в поиске целевой роли. * Реализует защиту от циклического наследования, выбрасывая исключение при его обнаружении. * * @param {string} currentRoleId - Идентификатор текущей роли, которая проверяется. * @param {string} targetRoleId - Идентификатор роли, которую необходимо найти. * @param {string[]} visitedRoles - Список уже посещённых ролей для отслеживания циклов. * @returns {Promise<boolean>} Промис, разрешающийся в `true`, если целевая роль найдена, иначе — `false`. */ private async traverseRoleHierarchy( currentRoleId: string, targetRoleId: string, visitedRoles: string[] ): Promise<boolean> { // Обнаружение циклического наследования if (visitedRoles.includes(currentRoleId)) { throw new CircularRoleInheritanceException([...visitedRoles, currentRoleId]) } // Добавляем текущую роль в список посещённых const newVisitedRoles = [...visitedRoles, currentRoleId] // Получаем данные текущей роли const role = await this.roleRepository.findRoleById(currentRoleId) if (!role || !role.inheritedRoles || role.inheritedRoles.length === 0) { return false } // Проверяем, является ли целевая роль непосредственным наследником if (role.inheritedRoles.includes(targetRoleId)) { return true } // Рекурсивно проверяем все наследуемые роли for (const inheritedRoleId of role.inheritedRoles) { const found = await this.traverseRoleHierarchy(inheritedRoleId, targetRoleId, newVisitedRoles) if (found) { return true } } return false } /** * Рекурсивно собирает все наследуемые роли для указанной роли. * Используется для построения полной иерархии прав. * * @param {string} roleId - Идентификатор роли, для которой собираются наследуемые роли. * @param {string[]} visitedRoles - Список уже посещённых ролей для предотвращения циклов. * @returns {Promise<string[]>} Промис, разрешающийся массивом всех идентификаторов наследуемых ролей. */ private async collectInheritedRoles(roleId: string, visitedRoles: string[]): Promise<string[]> { if (visitedRoles.includes(roleId)) { return [] // Избегаем зацикливания } const newVisitedRoles = [...visitedRoles, roleId] const role = await this.roleRepository.findRoleById(roleId) if (!role || !role.inheritedRoles || role.inheritedRoles.length === 0) { return [] } const inherited: string[] = [...role.inheritedRoles] for (const inheritedRoleId of role.inheritedRoles) { const nestedInherited = await this.collectInheritedRoles(inheritedRoleId, newVisitedRoles) inherited.push(...nestedInherited) } return inherited } }