diff --git a/src/auth/decorator/roles.decorator.ts b/src/auth/decorator/roles.decorator.ts new file mode 100644 index 0000000..b69b373 --- /dev/null +++ b/src/auth/decorator/roles.decorator.ts @@ -0,0 +1,3 @@ +import { Reflector } from '@nestjs/core'; + +export const Roles = Reflector.createDecorator(); diff --git a/src/auth/guard/roles.guard.ts b/src/auth/guard/roles.guard.ts new file mode 100644 index 0000000..e38da81 --- /dev/null +++ b/src/auth/guard/roles.guard.ts @@ -0,0 +1,43 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Roles } from '../decorator/roles.decorator'; +import { InjectRepository } from '@nestjs/typeorm'; +import { UserEntity } from '../../user/entity/user.entity'; +import { Repository } from 'typeorm'; +import { UserRoleEntity } from '../../user/entity/user-role.entity'; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor( + private reflector: Reflector, + @InjectRepository(UserEntity) + private readonly userRepository: Repository, + ) {} + + async canActivate(context: ExecutionContext) { + const roles = this.reflector.get(Roles, context.getHandler()); + if (!roles) { + return true; + } + const request = context.switchToHttp().getRequest(); + const userId = request.user; + if (!userId) { + return false; + } + const user = await this.userRepository.findOne({ + where: { id: userId }, + relations: { + roles: true, + }, + }); + return this.matchRoles(roles, user.roles); + } + + private matchRoles(roles: string[], userRoles: UserRoleEntity[]): boolean { + return ( + userRoles + .map((userRole) => userRole.name) + .filter((userRole) => roles.includes(userRole)).length > 0 + ); + } +} diff --git a/src/canvas/canvas-tag.controller.ts b/src/canvas/canvas-tag.controller.ts index 87a80f6..9262fda 100644 --- a/src/canvas/canvas-tag.controller.ts +++ b/src/canvas/canvas-tag.controller.ts @@ -13,14 +13,17 @@ import { CanvasTagService } from './canvas-tag.service'; import { CanvasTagCreateOrUpdateDTO, CanvasTagDTO } from './canvas.interface'; import { AuthenticatedGuard } from '../auth/guard/authenticated.guard'; import { Uuid } from '../common/common.interface'; -import {ListFilter, PagedResult} from '../common/pageable.utils'; +import { ListFilter, PagedResult } from '../common/pageable.utils'; +import { Roles } from '../auth/decorator/roles.decorator'; +import { RolesGuard } from '../auth/guard/roles.guard'; @Controller('/canvas-tag') export class CanvasTagController { constructor(private readonly canvasTagService: CanvasTagService) {} @Post() - @UseGuards(AuthenticatedGuard) + @Roles(['ADMIN']) + @UseGuards(AuthenticatedGuard, RolesGuard) public async create( @Body() dto: CanvasTagCreateOrUpdateDTO, ): Promise { @@ -47,7 +50,8 @@ export class CanvasTagController { } @Put('/:id') - @UseGuards(AuthenticatedGuard) + @Roles(['ADMIN']) + @UseGuards(AuthenticatedGuard, RolesGuard) public async update( @Param('id') id: Uuid, @Body() dto: CanvasTagCreateOrUpdateDTO, @@ -59,7 +63,8 @@ export class CanvasTagController { } @Delete('/:id') - @UseGuards(AuthenticatedGuard) + @Roles(['ADMIN']) + @UseGuards(AuthenticatedGuard, RolesGuard) public async delete(@Param('id') id: Uuid) { await this.canvasTagService.delete({ id }); } diff --git a/src/canvas/canvas.interface.ts b/src/canvas/canvas.interface.ts index 554e1b9..6c815d3 100644 --- a/src/canvas/canvas.interface.ts +++ b/src/canvas/canvas.interface.ts @@ -41,12 +41,12 @@ export interface CancelAccessCommand { export interface CanvasAddTagCommand { canvasId: Uuid; - tagIds: [Uuid]; + tagIds: Uuid[]; } export interface CanvasRemoveTagCommand { canvasId: Uuid; - tagIds: [Uuid]; + tagIds: Uuid[]; } export class CanvasMetadataUpdateDTO { @@ -121,5 +121,5 @@ export class CanvasModifyTagDTO { @IsUUID('all', { each: true }) @IsNotEmpty({ each: true }) @IsNotEmpty() - tagIds: [Uuid]; + tagIds: Uuid[]; } diff --git a/src/canvas/guard/canvas.guard.ts b/src/canvas/guard/canvas.guard.ts index 925563f..89e2477 100644 --- a/src/canvas/guard/canvas.guard.ts +++ b/src/canvas/guard/canvas.guard.ts @@ -14,7 +14,13 @@ export class CanvasGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const canvasId = request.params.id; - const userId = request.user.toString(); + if (!canvasId) { + return true; + } + const userId = request.user; + if (!userId) { + return false; + } const canvasAccess = await this.canvasAccessRepository.findOne({ where: { canvas: { id: canvasId }, user: { id: userId } },