From 8cf88c0eb37975172de43a869e30ae125edcacf3 Mon Sep 17 00:00:00 2001
From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com>
Date: Tue, 1 Dec 2020 11:58:41 +0100
Subject: [PATCH] feat(auth): update password strength verification, increase
 security with salt in env variable

---
 src/users/enum/user-role.enum.ts |  4 ++++
 src/users/user.schema.ts         | 11 ++++++-----
 src/users/users.service.ts       | 28 ++++++++++++++++++++++------
 template.env                     |  1 +
 4 files changed, 33 insertions(+), 11 deletions(-)
 create mode 100644 src/users/enum/user-role.enum.ts

diff --git a/src/users/enum/user-role.enum.ts b/src/users/enum/user-role.enum.ts
new file mode 100644
index 000000000..b9d591450
--- /dev/null
+++ b/src/users/enum/user-role.enum.ts
@@ -0,0 +1,4 @@
+export enum UserRole {
+  user,
+  admin,
+}
diff --git a/src/users/user.schema.ts b/src/users/user.schema.ts
index a8eb4ee71..127dcb460 100644
--- a/src/users/user.schema.ts
+++ b/src/users/user.schema.ts
@@ -1,21 +1,22 @@
 import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
 import { Document } from 'mongoose';
+import { UserRole } from './enum/user-role.enum';
 
 export type UserDocument = User & Document;
 
 @Schema()
 export class User {
-  @Prop()
+  @Prop({ required: true })
   email: string;
 
-  @Prop()
+  @Prop({ required: true })
   password: string;
 
-  @Prop()
-  uuid: string;
-
   @Prop({ default: false })
   emailVerified: boolean;
+
+  @Prop({ enum: [UserRole.admin, UserRole.user], default: UserRole.user })
+  role: number;
 }
 
 export const UserSchema = SchemaFactory.createForClass(User);
diff --git a/src/users/users.service.ts b/src/users/users.service.ts
index 404913230..bf6f7f9bf 100644
--- a/src/users/users.service.ts
+++ b/src/users/users.service.ts
@@ -6,9 +6,6 @@ import { CreateUserDto } from './create-user.dto';
 import { User, UserDocument } from './user.schema';
 import * as bcrypt from 'bcrypt';
 
-// This should be a real class/interface representing a user entity
-// export type User = any;
-
 @Injectable()
 export class UsersService {
   constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
@@ -18,11 +15,30 @@ export class UsersService {
     if (userInDb) {
       throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
     }
+    if (!this.isStrongPassword(createUserDto.password)) {
+      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
+      );
+    }
     const createUser = new this.userModel(createUserDto);
     createUser.password = await this.hashPassword(createUser.password);
-    //TODO: generate UUID for connexion
     createUser.save();
-    return this.findOne(createUserDto.email);
+    return await this.findOne(createUserDto.email);
+  }
+
+  /**
+   * Verify password strenth with the following rule:
+   * - The string must contain at least 1 lowercase alphabetical character
+   * - The string must contain at least 1 uppercase alphabetical character
+   * - The string must contain at least 1 numeric character
+   * - The string must contain at least one special character, reserved RegEx characters are escaped to avoid conflict
+   * - The string must be eight characters or longer
+   * @param password string
+   */
+  private isStrongPassword(password: string): boolean {
+    const strongRegex = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})');
+    return strongRegex.test(password);
   }
 
   private async comparePassword(attempt: string, password: string): Promise<boolean> {
@@ -30,7 +46,7 @@ export class UsersService {
   }
 
   private async hashPassword(password: string): Promise<string> {
-    return await bcrypt.hash(password, 10);
+    return await bcrypt.hash(password, process.env.SALT);
   }
 
   public async findOne(mail: string, passwordQuery?: boolean): Promise<User | undefined> {
diff --git a/template.env b/template.env
index 2352a2c8c..93bc175be 100644
--- a/template.env
+++ b/template.env
@@ -9,3 +9,4 @@ MONGO_DB_HOST_AND_PORT=<host:port used by the api to connect to mongo db>
 ME_CONFIG_BASICAUTH_USERNAME=<mongo express username>
 ME_CONFIG_BASICAUTH_PASSWORD=<mongo express password>
 ME_PORT=<mongo express port>
+SALT=<Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue>
-- 
GitLab