From cead66ea129de7d7b38e026e6ee89d6fa2892f5e Mon Sep 17 00:00:00 2001
From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com>
Date: Fri, 11 Dec 2020 15:50:22 +0100
Subject: [PATCH] feat: add password cahnge endpoint

---
 src/users/change-password.dto.ts |  6 ++++++
 src/users/users.controller.ts    | 14 ++++++++++++++
 src/users/users.service.ts       | 21 ++++++++++++++++++++-
 3 files changed, 40 insertions(+), 1 deletion(-)
 create mode 100644 src/users/change-password.dto.ts

diff --git a/src/users/change-password.dto.ts b/src/users/change-password.dto.ts
new file mode 100644
index 000000000..25ff3cc07
--- /dev/null
+++ b/src/users/change-password.dto.ts
@@ -0,0 +1,6 @@
+import { IsNotEmpty } from 'class-validator';
+
+export class PasswordChangeDto {
+  @IsNotEmpty() readonly newPassword: string;
+  @IsNotEmpty() readonly oldPassword: string;
+}
diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts
index 8d80697be..18efa8f93 100644
--- a/src/users/users.controller.ts
+++ b/src/users/users.controller.ts
@@ -1,6 +1,7 @@
 import { Body, Controller, Get, Param, Post, Query, Req, Request, UseGuards } from '@nestjs/common';
 import { ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger';
 import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { PasswordChangeDto } from './change-password.dto';
 import { CreateUserDto } from './create-user.dto';
 import { UsersService } from './users.service';
 
@@ -30,4 +31,17 @@ export class UsersController {
   public async validateUser(@Param() params, @Query('token') token: string) {
     return this.usersService.validateUser(params.id, token);
   }
+
+  @UseGuards(JwtAuthGuard)
+  @Post('change-password')
+  @ApiResponse({ status: 201, description: 'Password changed' })
+  @ApiResponse({ status: 401, description: 'Invalid password' })
+  @ApiResponse({ status: 422, description: 'Weak password' })
+  public async changePassword(@Request() req, @Body() passwordChangeDto: PasswordChangeDto) {
+    return this.usersService.changeUserPassword(
+      req.user._id,
+      passwordChangeDto.oldPassword,
+      passwordChangeDto.newPassword
+    );
+  }
 }
diff --git a/src/users/users.service.ts b/src/users/users.service.ts
index 4ea11e51f..c4db445d0 100644
--- a/src/users/users.service.ts
+++ b/src/users/users.service.ts
@@ -66,7 +66,10 @@ export class UsersService {
     return this.userModel.findOne({ email: mail }).select('-password').exec();
   }
 
-  public async findById(id: string): Promise<IUser | undefined> {
+  public async findById(id: string, passwordQuery?: boolean): Promise<IUser | undefined> {
+    if (passwordQuery) {
+      return this.userModel.findById(id).exec();
+    }
     return this.userModel.findById(id).select('-password').exec();
   }
 
@@ -130,4 +133,20 @@ export class UsersService {
       throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
     }
   }
+
+  public async changeUserPassword(userId: string, oldPassword: string, newPassword: string) {
+    const user = await this.findById(userId, true);
+    const arePasswordEqual = await this.comparePassword(oldPassword, user.password);
+    if (!arePasswordEqual) {
+      throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    }
+    if (!this.isStrongPassword(newPassword)) {
+      throw new HttpException(
+        'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+        HttpStatus.UNPROCESSABLE_ENTITY
+      );
+    }
+    user.password = await this.hashPassword(newPassword);
+    user.save();
+  }
 }
-- 
GitLab