Files
IdP/apps/sso-core/src/domain/access.service.ts
2026-06-25 23:48:57 +03:00

174 lines
6.1 KiB
TypeScript

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<UserAccessContext> {
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('Нет доступа к документам пользователя');
}
}
}