From acb79f25c7a4c7ee447e4409be25682152b3f739 Mon Sep 17 00:00:00 2001
From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com>
Date: Tue, 12 Jan 2021 10:36:41 +0100
Subject: [PATCH] feat: add role guard

---
 src/users/decorators/roles.decorator.ts |  3 +++
 src/users/dto/create-user.dto.ts        |  2 +-
 src/users/guards/roles.guard.ts         | 33 +++++++++++++++++++++++++
 src/users/users.controller.ts           | 15 ++++++-----
 src/users/users.service.ts              |  6 ++---
 5 files changed, 49 insertions(+), 10 deletions(-)
 create mode 100644 src/users/decorators/roles.decorator.ts
 create mode 100644 src/users/guards/roles.guard.ts

diff --git a/src/users/decorators/roles.decorator.ts b/src/users/decorators/roles.decorator.ts
new file mode 100644
index 000000000..b0376727c
--- /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 acb92be7f..34297e1ca 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 000000000..2ad887357
--- /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 b5029bbec..d78059dc5 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 b4d54e274..e5e6b9230 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 = [];
 
-- 
GitLab