import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common'; import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; import { map } from 'rxjs'; import { CoreGrpcService } from '../core-grpc.service'; import { CurrentAdmin } from '../decorators/current-admin.decorator'; import { AssignUserPermissionDto, AssignUserRoleDto, CreateOAuthClientDto, CreateRoleDto, UpdateOAuthClientDto, UpdateRoleDto } from '../dto/rbac.dto'; import { AdminGuard, AdminRequestUser, assertAdminAnyPermission, assertAdminPermission, RbacManageGuard, SuperAdminGuard } from '../guards/admin.guard'; @ApiTags('RBAC и OAuth') @ApiBearerAuth() @UseGuards(AdminGuard) @Controller('admin/rbac') export class RbacController { constructor(private readonly core: CoreGrpcService) {} @Get('roles') @ApiOperation({ summary: 'Список ролей', description: 'Возвращает роли вместе с назначенными правами.' }) listRoles() { return this.core.rbac.ListRoles({}).pipe( map((response) => { const payload = response as { roles?: Array<{ permissions?: unknown[] }> }; return { roles: (payload.roles ?? []).map((role) => ({ ...role, permissions: role.permissions ?? [] })) }; }) ); } @Get('permissions') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Список прав', description: 'Возвращает все доступные permissions.' }) listPermissions() { return this.core.rbac.ListPermissions({}); } @Get('oauth-scopes') @ApiOperation({ summary: 'OAuth scopes', description: 'Возвращает доступные scopes для OAuth-приложений.' }) listOAuthScopes(@CurrentAdmin() admin: AdminRequestUser) { assertAdminAnyPermission(admin, 'canViewOAuth', 'canManageOAuth'); return this.core.rbac.ListOAuthScopes({}); } @Get('oauth-clients') @ApiOperation({ summary: 'OAuth приложения', description: 'Возвращает OAuth-клиенты и доступные scopes.' }) listOAuthClients(@CurrentAdmin() admin: AdminRequestUser) { assertAdminPermission(admin, 'canViewOAuth'); return this.core.rbac.ListOAuthClients({ actorUserId: admin.id }); } @Post('roles') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Создать роль', description: 'Создаёт новую роль с набором прав.' }) @ApiBody({ type: CreateRoleDto }) createRole(@Body() dto: CreateRoleDto, @CurrentAdmin() admin: AdminRequestUser) { return this.core.rbac.CreateRole({ actorUserId: admin.id, ...dto }); } @Patch('roles/:roleSlug') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Обновить роль', description: 'Изменяет название, описание и права роли.' }) @ApiParam({ name: 'roleSlug', description: 'Slug роли' }) @ApiBody({ type: UpdateRoleDto }) updateRole(@Param('roleSlug') roleSlug: string, @Body() dto: UpdateRoleDto, @CurrentAdmin() admin: AdminRequestUser) { return this.core.rbac.UpdateRole({ actorUserId: admin.id, roleSlug, ...dto }); } @Delete('roles/:roleSlug') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Удалить роль', description: 'Удаляет пользовательскую роль. Системные роли удалить нельзя.' }) @ApiParam({ name: 'roleSlug', description: 'Slug роли' }) deleteRole(@Param('roleSlug') roleSlug: string, @CurrentAdmin() admin: AdminRequestUser) { return this.core.rbac.DeleteRole({ actorUserId: admin.id, roleSlug }); } @Post('users/:userId/roles') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Назначить роль пользователю' }) @ApiParam({ name: 'userId', description: 'ID пользователя' }) @ApiBody({ type: AssignUserRoleDto }) assignRole(@Param('userId') userId: string, @Body() dto: AssignUserRoleDto, @CurrentAdmin() admin: AdminRequestUser) { return this.core.rbac.AssignUserRole({ actorUserId: admin.id, userId, roleSlug: dto.roleSlug }); } @Delete('users/:userId/roles/:roleSlug') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Снять роль с пользователя' }) @ApiParam({ name: 'userId', description: 'ID пользователя' }) @ApiParam({ name: 'roleSlug', description: 'Slug роли' }) removeRole(@Param('userId') userId: string, @Param('roleSlug') roleSlug: string, @CurrentAdmin() admin: AdminRequestUser) { return this.core.rbac.RemoveUserRole({ actorUserId: admin.id, userId, roleSlug }); } @Post('users/:userId/permissions') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Назначить право пользователю', description: 'Прямое назначение permission без смены роли.' }) @ApiParam({ name: 'userId', description: 'ID пользователя' }) @ApiBody({ type: AssignUserPermissionDto }) assignPermission(@Param('userId') userId: string, @Body() dto: AssignUserPermissionDto, @CurrentAdmin() admin: AdminRequestUser) { return this.core.rbac.AssignUserPermission({ actorUserId: admin.id, userId, permissionSlug: dto.permissionSlug }); } @Delete('users/:userId/permissions/:permissionSlug') @UseGuards(RbacManageGuard) @ApiOperation({ summary: 'Снять право с пользователя' }) @ApiParam({ name: 'userId', description: 'ID пользователя' }) @ApiParam({ name: 'permissionSlug', description: 'Slug права' }) removePermission( @Param('userId') userId: string, @Param('permissionSlug') permissionSlug: string, @CurrentAdmin() admin: AdminRequestUser ) { return this.core.rbac.RemoveUserPermission({ actorUserId: admin.id, userId, permissionSlug }); } @Post('oauth-clients') @ApiOperation({ summary: 'Создать OAuth-приложение', description: 'Создаёт OAuth2-клиент и возвращает client secret один раз.' }) @ApiBody({ type: CreateOAuthClientDto }) createOAuthClient(@Body() dto: CreateOAuthClientDto, @CurrentAdmin() admin: AdminRequestUser) { assertAdminPermission(admin, 'canManageOAuth'); return this.core.rbac.CreateOAuthClient({ actorUserId: admin.id, ...dto }); } @Patch('oauth-clients/:clientId') @ApiOperation({ summary: 'Обновить OAuth-приложение', description: 'Изменяет redirect URI, scopes и статус приложения.' }) @ApiParam({ name: 'clientId', description: 'Client ID приложения' }) @ApiBody({ type: UpdateOAuthClientDto }) updateOAuthClient(@Param('clientId') clientId: string, @Body() dto: UpdateOAuthClientDto, @CurrentAdmin() admin: AdminRequestUser) { assertAdminPermission(admin, 'canManageOAuth'); return this.core.rbac.UpdateOAuthClient({ actorUserId: admin.id, clientId, ...dto }); } @Delete('oauth-clients/:clientId') @ApiOperation({ summary: 'Удалить OAuth-приложение', description: 'Полностью удаляет OAuth2-клиент и связанные данные.' }) @ApiParam({ name: 'clientId', description: 'Client ID приложения' }) deleteOAuthClient(@Param('clientId') clientId: string, @CurrentAdmin() admin: AdminRequestUser) { assertAdminPermission(admin, 'canManageOAuth'); return this.core.rbac.DeleteOAuthClient({ actorUserId: admin.id, clientId }); } @Post('oauth-clients/:clientId/rotate-secret') @ApiOperation({ summary: 'Перевыпустить client secret', description: 'Генерирует новый secret для confidential-клиента.' }) @ApiParam({ name: 'clientId', description: 'Client ID приложения' }) rotateSecret(@Param('clientId') clientId: string, @CurrentAdmin() admin: AdminRequestUser) { assertAdminPermission(admin, 'canManageOAuth'); return this.core.rbac.RotateOAuthSecret({ actorUserId: admin.id, clientId }); } }