roles update
This commit is contained in:
@@ -3,6 +3,8 @@ import { randomBytes } from 'node:crypto';
|
||||
import { OAuthClientType } from '../generated/prisma/client';
|
||||
import { PrismaService } from '../infra/prisma.service';
|
||||
import { AccessService, UserAccessContext } from './access.service';
|
||||
import { NotificationsService } from './notifications.service';
|
||||
import { DEFAULT_USER_ROLE_SLUG } from './rbac.constants';
|
||||
|
||||
export interface OAuthClientListItem {
|
||||
id: string;
|
||||
@@ -21,11 +23,12 @@ export interface OAuthClientListItem {
|
||||
export class RbacService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly access: AccessService
|
||||
private readonly access: AccessService,
|
||||
private readonly notifications: NotificationsService
|
||||
) {}
|
||||
|
||||
async createRole(actorUserId: string, data: { slug: string; name: string; description?: string; permissionSlugs?: string[] }) {
|
||||
await this.access.assertSuperAdmin(actorUserId);
|
||||
await this.access.assertRbacManage(actorUserId);
|
||||
return this.prisma.role.create({
|
||||
data: {
|
||||
slug: data.slug,
|
||||
@@ -51,28 +54,105 @@ export class RbacService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateRole(
|
||||
actorUserId: string,
|
||||
roleSlug: string,
|
||||
data: { name?: string; description?: string; permissionSlugs?: string[] }
|
||||
) {
|
||||
await this.access.assertRbacManage(actorUserId);
|
||||
const role = await this.prisma.role.findUnique({ where: { slug: roleSlug } });
|
||||
if (!role) {
|
||||
throw new NotFoundException('Роль не найдена');
|
||||
}
|
||||
|
||||
if (data.permissionSlugs !== undefined) {
|
||||
await this.prisma.rolePermission.deleteMany({ where: { roleId: role.id } });
|
||||
for (const slug of data.permissionSlugs) {
|
||||
const permission = await this.prisma.permission.findUnique({ where: { slug } });
|
||||
if (!permission) continue;
|
||||
await this.prisma.rolePermission.create({
|
||||
data: { roleId: role.id, permissionId: permission.id }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this.prisma.role.update({
|
||||
where: { id: role.id },
|
||||
data: {
|
||||
name: data.name,
|
||||
description: data.description
|
||||
},
|
||||
include: { permissions: { include: { permission: true } } }
|
||||
});
|
||||
}
|
||||
|
||||
async deleteRole(actorUserId: string, roleSlug: string) {
|
||||
await this.access.assertRbacManage(actorUserId);
|
||||
const role = await this.prisma.role.findUnique({ where: { slug: roleSlug } });
|
||||
if (!role) {
|
||||
throw new NotFoundException('Роль не найдена');
|
||||
}
|
||||
if (role.isSystem) {
|
||||
throw new BadRequestException('Системную роль нельзя удалить');
|
||||
}
|
||||
|
||||
await this.prisma.userRole.deleteMany({ where: { roleId: role.id } });
|
||||
await this.prisma.rolePermission.deleteMany({ where: { roleId: role.id } });
|
||||
await this.prisma.role.delete({ where: { id: role.id } });
|
||||
return { slug: roleSlug };
|
||||
}
|
||||
|
||||
async assignRole(actorUserId: string, userId: string, roleSlug: string) {
|
||||
await this.access.assertSuperAdmin(actorUserId);
|
||||
await this.access.assertRbacManage(actorUserId);
|
||||
const role = await this.prisma.role.findUnique({ where: { slug: roleSlug } });
|
||||
if (!role) {
|
||||
throw new NotFoundException('Роль не найдена');
|
||||
}
|
||||
await this.ensureUserExists(userId);
|
||||
await this.prisma.userRole.upsert({
|
||||
where: { userId_roleId: { userId, roleId: role.id } },
|
||||
create: { userId, roleId: role.id },
|
||||
update: {}
|
||||
|
||||
const existingLink = await this.prisma.userRole.findUnique({
|
||||
where: { userId_roleId: { userId, roleId: role.id } }
|
||||
});
|
||||
if (existingLink) {
|
||||
return this.getUserRoleSlugs(userId);
|
||||
}
|
||||
|
||||
await this.prisma.userRole.create({
|
||||
data: { userId, roleId: role.id }
|
||||
});
|
||||
|
||||
await this.notifications.create(userId, 'role_assigned', 'Роли', `Вам назначена роль: ${role.name}`, {
|
||||
roleSlug: role.slug,
|
||||
roleName: role.name
|
||||
});
|
||||
|
||||
return this.getUserRoleSlugs(userId);
|
||||
}
|
||||
|
||||
async removeRole(actorUserId: string, userId: string, roleSlug: string) {
|
||||
await this.access.assertSuperAdmin(actorUserId);
|
||||
await this.access.assertRbacManage(actorUserId);
|
||||
const role = await this.prisma.role.findUnique({ where: { slug: roleSlug } });
|
||||
if (!role) {
|
||||
throw new NotFoundException('Роль не найдена');
|
||||
}
|
||||
if (role.isSystem || role.isDefault || role.slug === DEFAULT_USER_ROLE_SLUG) {
|
||||
throw new BadRequestException('Стандартную роль «Пользователь» нельзя снять');
|
||||
}
|
||||
|
||||
const existingLink = await this.prisma.userRole.findUnique({
|
||||
where: { userId_roleId: { userId, roleId: role.id } }
|
||||
});
|
||||
if (!existingLink) {
|
||||
return this.getUserRoleSlugs(userId);
|
||||
}
|
||||
|
||||
await this.prisma.userRole.deleteMany({ where: { userId, roleId: role.id } });
|
||||
|
||||
await this.notifications.create(userId, 'role_removed', 'Роли', `С вас снята роль: ${role.name}`, {
|
||||
roleSlug: role.slug,
|
||||
roleName: role.name
|
||||
});
|
||||
|
||||
return this.getUserRoleSlugs(userId);
|
||||
}
|
||||
|
||||
@@ -163,6 +243,54 @@ export class RbacService {
|
||||
return { client: updated, clientSecret: undefined as string | undefined };
|
||||
}
|
||||
|
||||
async deleteOAuthClient(actorUserId: string, clientId: string) {
|
||||
const existing = await this.prisma.oAuthClient.findUnique({ where: { clientId } });
|
||||
if (!existing) {
|
||||
throw new NotFoundException('OAuth-приложение не найдено');
|
||||
}
|
||||
await this.assertCanManageOAuthClient(actorUserId, existing);
|
||||
await this.prisma.oAuthClient.delete({ where: { id: existing.id } });
|
||||
return { clientId };
|
||||
}
|
||||
|
||||
async ensureDefaultUserRole(userId: string) {
|
||||
const role = await this.prisma.role.findUnique({ where: { slug: DEFAULT_USER_ROLE_SLUG } });
|
||||
if (!role) return;
|
||||
await this.prisma.userRole.upsert({
|
||||
where: { userId_roleId: { userId, roleId: role.id } },
|
||||
create: { userId, roleId: role.id },
|
||||
update: {}
|
||||
});
|
||||
}
|
||||
|
||||
async assignUserPermission(actorUserId: string, userId: string, permissionSlug: string) {
|
||||
await this.access.assertRbacManage(actorUserId);
|
||||
await this.ensureUserExists(userId);
|
||||
const permission = await this.prisma.permission.findUnique({ where: { slug: permissionSlug } });
|
||||
if (!permission) {
|
||||
throw new NotFoundException('Право не найдено');
|
||||
}
|
||||
|
||||
await this.prisma.userPermission.upsert({
|
||||
where: { userId_permissionId: { userId, permissionId: permission.id } },
|
||||
create: { userId, permissionId: permission.id },
|
||||
update: {}
|
||||
});
|
||||
|
||||
return this.getUserPermissionSlugs(userId);
|
||||
}
|
||||
|
||||
async removeUserPermission(actorUserId: string, userId: string, permissionSlug: string) {
|
||||
await this.access.assertRbacManage(actorUserId);
|
||||
const permission = await this.prisma.permission.findUnique({ where: { slug: permissionSlug } });
|
||||
if (!permission) {
|
||||
throw new NotFoundException('Право не найдено');
|
||||
}
|
||||
|
||||
await this.prisma.userPermission.deleteMany({ where: { userId, permissionId: permission.id } });
|
||||
return this.getUserPermissionSlugs(userId);
|
||||
}
|
||||
|
||||
async rotateOAuthSecret(actorUserId: string, clientId: string) {
|
||||
const existing = await this.prisma.oAuthClient.findUnique({ where: { clientId } });
|
||||
if (!existing) {
|
||||
@@ -256,6 +384,14 @@ export class RbacService {
|
||||
return links.map((link) => link.role.slug);
|
||||
}
|
||||
|
||||
private async getUserPermissionSlugs(userId: string) {
|
||||
const links = await this.prisma.userPermission.findMany({
|
||||
where: { userId },
|
||||
include: { permission: true }
|
||||
});
|
||||
return links.map((link) => link.permission.slug);
|
||||
}
|
||||
|
||||
private async ensureUserExists(userId: string) {
|
||||
const user = await this.prisma.user.findUnique({ where: { id: userId } });
|
||||
if (!user) {
|
||||
|
||||
Reference in New Issue
Block a user