diff --git a/src/mailer/mail-templates/verify.ejs b/src/mailer/mail-templates/verify.ejs index e384dae43ff76ded6f735fe586b80598c6f8fb14..2782ec1376354879a3875a0ebf3bc8e5352fe4ad 100644 --- a/src/mailer/mail-templates/verify.ejs +++ b/src/mailer/mail-templates/verify.ejs @@ -2,7 +2,7 @@ Bonjour<br /> <br /> Afin de pouvoir vous connecter sur la plateforme, merci de cliquer sur <a - href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/users/verify/<%= token %>" + href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/users/verify/<%= userId %>?token=<%= token %>" >ce lien</a > afin de valider votre inscription<br /> diff --git a/src/users/user.interface.ts b/src/users/user.interface.ts new file mode 100644 index 0000000000000000000000000000000000000000..0fe05c6f856caafb64c23ff4dfbaaa778d6b5773 --- /dev/null +++ b/src/users/user.interface.ts @@ -0,0 +1,10 @@ +import { Document } from 'mongoose'; + +export interface IUser extends Document { + readonly _id: string; + email: string; + password: string; + emailVerified: boolean; + validationToken: string; + role: number; +} diff --git a/src/users/user.schema.ts b/src/users/user.schema.ts index daa58ed0c7f9123ad1b04af3cf8ad9f475bf9f1a..abb11e91aaac1bac06d07ab6c513567b89a6791c 100644 --- a/src/users/user.schema.ts +++ b/src/users/user.schema.ts @@ -1,9 +1,5 @@ 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({ required: true }) diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index d7ebf5198487b29ebd157508033cbf2ee4cf508f..8d80697be368cc3c180d8a1c6894c2e1be5d9353 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -1,4 +1,5 @@ -import { Body, Controller, Get, Post, Request, UseGuards } from '@nestjs/common'; +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 { CreateUserDto } from './create-user.dto'; import { UsersService } from './users.service'; @@ -8,13 +9,25 @@ export class UsersController { constructor(private usersService: UsersService) {} @UseGuards(JwtAuthGuard) + @ApiOperation({ description: 'Get user profile' }) + @ApiResponse({ status: 200, description: 'Return user profil' }) + @ApiResponse({ status: 401, description: 'User does not have sufficient rights' }) @Get('profile') public getProfile(@Request() req) { return req.user; } @Post() + @ApiResponse({ status: 201, description: 'User created' }) public async create(@Body() createUserDto: CreateUserDto) { return this.usersService.create(createUserDto); } + + @Post('verify/:id') + @ApiParam({ name: 'id', type: String, required: true }) + @ApiResponse({ status: 201, description: 'User verified' }) + @ApiResponse({ status: 401, description: "This token does'nt exist or is not associate to this user." }) + public async validateUser(@Param() params, @Query('token') token: string) { + return this.usersService.validateUser(params.id, token); + } } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index c2f9e59ae4f494f107318700842abb9da111222f..4ea11e51fc03c9026ee7712013df0ae79bc06a11 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -6,13 +6,18 @@ import * as crypto from 'crypto'; import { Model } from 'mongoose'; import { LoginDto } from '../auth/login-dto'; import { CreateUserDto } from './create-user.dto'; -import { User, UserDocument } from './user.schema'; +import { User } from './user.schema'; import { MailerService } from '../mailer/mailer.service'; +import { IUser } from './user.interface'; @Injectable() export class UsersService { - constructor(@InjectModel(User.name) private userModel: Model<UserDocument>, private mailerService: MailerService) {} + constructor(@InjectModel(User.name) private userModel: Model<IUser>, private mailerService: MailerService) {} + /** + * Create a user account + * @param createUserDto CreateUserDto + */ public async create(createUserDto: CreateUserDto): Promise<User> { const userInDb = await this.findOne(createUserDto.email); if (userInDb) { @@ -54,13 +59,22 @@ export class UsersService { return await bcrypt.hash(password, process.env.SALT); } - public async findOne(mail: string, passwordQuery?: boolean): Promise<User | undefined> { + public async findOne(mail: string, passwordQuery?: boolean): Promise<IUser | undefined> { if (passwordQuery) { return this.userModel.findOne({ email: mail }).exec(); } return this.userModel.findOne({ email: mail }).select('-password').exec(); } + public async findById(id: string): Promise<IUser | undefined> { + return this.userModel.findById(id).select('-password').exec(); + } + + /** + * Return a user after credential checking. + * Use for login action + * @param param LoginDto + */ public async findByLogin({ email, password }: LoginDto): Promise<User> { const user = await this.findOne(email, true); @@ -83,16 +97,16 @@ export class UsersService { * a new account. * @param user User */ - private async verifyUserMail(user: User): Promise<any> { + private async verifyUserMail(user: IUser): Promise<any> { const config = this.mailerService.config; const ejsPath = this.mailerService.getTemplateLocation(config.templates.verify.ejs); const jsonConfig = this.mailerService.loadJsonConfig(config.templates.verify.json); const token = crypto.randomBytes(64).toString('hex'); - const html = await ejs.renderFile(ejsPath, { config, token: token, + userId: user._id, }); this.mailerService.send(user.email, jsonConfig.subject, html); @@ -100,4 +114,20 @@ export class UsersService { user.validationToken = token; return user; } + + /** + * Check that the given token is associated to userId. If it's true, validate user account. + * @param userId string + * @param token string + */ + public async validateUser(userId: string, token: string): Promise<any> { + const user = await this.findById(userId); + if (user && user.validationToken === token) { + user.validationToken = null; + user.emailVerified = true; + user.save(); + } else { + throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED); + } + } }