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