diff --git a/src/users/decorators/roles.decorator.ts b/src/users/decorators/roles.decorator.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0376727cc30742bda0e26d1e498c4f36fd4be02 --- /dev/null +++ b/src/users/decorators/roles.decorator.ts @@ -0,0 +1,3 @@ +import { SetMetadata } from '@nestjs/common'; + +export const Roles = (...roles: string[]) => SetMetadata('roles', roles); diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts index acb92be7f354d6a6afc05b729a4e6c0da247f6f3..34297e1cae9c8ac7634be3311347a8fa316abb12 100644 --- a/src/users/dto/create-user.dto.ts +++ b/src/users/dto/create-user.dto.ts @@ -14,5 +14,5 @@ export class CreateUserDto { @IsArray() @IsOptional() - structuresLink?: Array<number>; + pendingStructuresLink?: Array<number>; } diff --git a/src/users/guards/roles.guard.ts b/src/users/guards/roles.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ad8873575a0458ca5f15ffa6568f6af8767cbdd --- /dev/null +++ b/src/users/guards/roles.guard.ts @@ -0,0 +1,33 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { UserRole } from '../enum/user-role.enum'; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private reflector: Reflector) {} + + canActivate(context: ExecutionContext): boolean { + const roles = this.reflector.get<string[]>('roles', context.getHandler()); + if (!roles) { + return true; + } + const request = context.switchToHttp().getRequest(); + const user = request.user; + return this.matchRoles(user.role, roles[0]); + } + + /** + * Return true if user is admin or if the requested role match the user role. + * @param userRole user role from request + * @param expectedRole role requested by the endpoint + */ + private matchRoles(userRole: number, expectedRole: string): boolean { + if (userRole === UserRole.admin) { + return true; + } else if (userRole === UserRole[expectedRole]) { + return true; + } else { + return false; + } + } +} diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index b5029bbec5cd901b6d18a8f909f2ea98a6330c59..d78059dc5688afb2b2291e872847d613dc045df5 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -7,6 +7,8 @@ import { CreateUserDto } from './dto/create-user.dto'; import { PasswordResetApplyDto } from './dto/reset-password-apply.dto'; import { PasswordResetDto } from './dto/reset-password.dto'; import { UsersService } from './users.service'; +import { RolesGuard } from './guards/roles.guard'; +import { Roles } from './decorators/roles.decorator'; @Controller('users') export class UsersController { @@ -26,9 +28,9 @@ export class UsersController { public async create(@Body() createUserDto: CreateUserDto) { // remove structureId for creation and add structure after let structureId = null; - if (createUserDto.structuresLink.length > 0) { - structureId = createUserDto.structuresLink[0]; - delete createUserDto.structuresLink; + if (createUserDto.pendingStructuresLink.length > 0) { + structureId = createUserDto.pendingStructuresLink[0]; + delete createUserDto.pendingStructuresLink; } const user = await this.usersService.create(createUserDto); if (structureId) { @@ -86,9 +88,10 @@ export class UsersController { return this.usersService.validatePasswordResetToken(passwordResetApplyDto.password, passwordResetApplyDto.token); } - @UseGuards(JwtAuthGuard) - @Get('pendingAttachments') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles('admin') + @Get('pendingStructures') public getPendingAttachments() { - return this.usersService.getPendingAttachments(); + return this.usersService.getPendingStructures(); } } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index b4d54e274771fa48ed9cb8d4d3568644ef0b0dab..e5e6b923077ea9ccc842cdc7ef577f80b11549e1 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -244,9 +244,9 @@ export class UsersService { public async updateStructureLinked(userEmail: string, idStructure: number): Promise<any> { const user = await this.findOne(userEmail, true); if (user) { - user.structuresLink.push(idStructure); + user.pendingStructuresLink.push(idStructure); user.save(); - return user.structuresLink; + return user.pendingStructuresLink; } throw new HttpException('Invalid user', HttpStatus.NOT_FOUND); } @@ -254,7 +254,7 @@ export class UsersService { /** * Return all pending attachments of all profiles */ - public async getPendingAttachments(): Promise<{ userEmail: string; structureId: number }[]> { + public async getPendingStructures(): Promise<{ userEmail: string; structureId: number }[]> { const users = await this.userModel.find(); const structuresPending = [];