import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../infra/prisma.service'; export interface UserAccessContext { roles: string[]; permissions: string[]; canAccessAdmin: boolean; canManageRoles: boolean; canViewOAuth: boolean; canManageOAuth: boolean; canManageAllOAuth: boolean; canManageUsers: boolean; canManageAllUsers: boolean; canViewUsers: boolean; canManageSettings: boolean; canViewUserDocuments: boolean; canVerifyUsers: boolean; canManageBots: boolean; } function hasAnyPermission(permissions: string[], ...slugs: string[]) { return slugs.some((slug) => permissions.includes(slug)); } @Injectable() export class AccessService { constructor(private readonly prisma: PrismaService) {} async getUserAccess(userId: string, isSuperAdmin: boolean): Promise { if (isSuperAdmin) { return { roles: ['super-admin'], permissions: [ 'admin.access', 'oauth.view', 'oauth.view.all', 'oauth.manage', 'oauth.manage.all', 'users.view', 'users.view.all', 'users.manage', 'users.manage.all', 'settings.manage', 'rbac.manage', 'documents.view_others', 'users.verify', 'bots.manage.all' ], canAccessAdmin: true, canManageRoles: true, canViewOAuth: true, canManageOAuth: true, canManageAllOAuth: true, canManageUsers: true, canManageAllUsers: true, canViewUsers: true, canManageSettings: true, canViewUserDocuments: true, canVerifyUsers: true, canManageBots: true }; } const [roleLinks, directLinks] = await Promise.all([ this.prisma.userRole.findMany({ where: { userId }, include: { role: { include: { permissions: { include: { permission: true } } } } } }), this.prisma.userPermission.findMany({ where: { userId }, include: { permission: true } }) ]); const roles = roleLinks.map((link) => link.role.slug); const rolePermissions = roleLinks.flatMap((link) => link.role.permissions.map((item) => item.permission.slug)); const directPermissions = directLinks.map((link) => link.permission.slug); const permissions = [...new Set([...rolePermissions, ...directPermissions])]; const canViewOAuth = hasAnyPermission(permissions, 'oauth.view', 'oauth.view.all', 'oauth.manage', 'oauth.manage.all'); const canManageOAuth = hasAnyPermission(permissions, 'oauth.manage', 'oauth.manage.all'); const canManageAllOAuth = permissions.includes('oauth.manage.all'); const canViewUsers = hasAnyPermission(permissions, 'users.view', 'users.view.all', 'users.manage', 'users.manage.all', 'users.read'); const canManageUsers = hasAnyPermission(permissions, 'users.manage', 'users.manage.all'); const canManageAllUsers = permissions.includes('users.manage.all'); return { roles, permissions, canAccessAdmin: permissions.includes('admin.access'), canManageRoles: permissions.includes('rbac.manage'), canViewOAuth, canManageOAuth, canManageAllOAuth, canManageUsers, canManageAllUsers, canViewUsers, canManageSettings: permissions.includes('settings.manage'), canViewUserDocuments: permissions.includes('documents.view_others'), canVerifyUsers: permissions.includes('users.verify'), canManageBots: permissions.includes('bots.manage.all') }; } canManageOAuthClient( access: UserAccessContext, actorUserId: string, client: { createdById: string | null } ) { if (access.canManageAllOAuth) return true; if (access.canManageOAuth && client.createdById === actorUserId) return true; return false; } async assertRbacManage(actorUserId: string) { const actor = await this.prisma.user.findUnique({ where: { id: actorUserId } }); if (!actor) { throw new NotFoundException('Пользователь не найден'); } if (actor.isSuperAdmin) return; const access = await this.getUserAccess(actorUserId, false); if (!access.canManageRoles) { throw new ForbiddenException('Недостаточно прав для управления ролями и правами'); } } async assertCanVerifyUsers(actorUserId: string) { const actor = await this.prisma.user.findUnique({ where: { id: actorUserId } }); if (!actor) { throw new NotFoundException('Пользователь не найден'); } if (actor.isSuperAdmin) return; const access = await this.getUserAccess(actorUserId, false); if (!access.canVerifyUsers) { throw new ForbiddenException('Недостаточно прав для верификации пользователей'); } } async assertSuperAdmin(actorUserId: string) { const actor = await this.prisma.user.findUnique({ where: { id: actorUserId } }); if (!actor?.isSuperAdmin) { throw new ForbiddenException('Только супер-администратор может выполнять это действие'); } } async assertPermission(userId: string, isSuperAdmin: boolean, permission: string) { const access = await this.getUserAccess(userId, isSuperAdmin); if (!access.permissions.includes(permission) && !isSuperAdmin) { throw new Error('FORBIDDEN_PERMISSION'); } return access; } async assertCanViewUserDocuments(requesterId: string, targetUserId: string) { if (requesterId === targetUserId) return; const requester = await this.prisma.user.findUnique({ where: { id: requesterId } }); if (!requester) { throw new ForbiddenException('Пользователь не найден'); } const access = await this.getUserAccess(requesterId, requester.isSuperAdmin); if (!access.canViewUserDocuments) { throw new ForbiddenException('Нет доступа к документам пользователя'); } } }