Ajout d'un endpoint pour modifier son mot de passe

This commit is contained in:
Emmy D'Anello 2024-12-07 13:52:49 +01:00
parent 45a1cebcf9
commit 1ae6b6634c
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
7 changed files with 48 additions and 8 deletions

View File

@ -16,7 +16,7 @@ export const JWT_SECRET = env.JWT_SECRET
PassportModule, PassportModule,
JwtModule.register({ JwtModule.register({
secret: JWT_SECRET, secret: JWT_SECRET,
signOptions: { expiresIn: '5m' }, signOptions: { expiresIn: '12h' },
}), }),
UsersModule, UsersModule,
], ],

View File

@ -13,9 +13,7 @@ export class AuthService {
if (!user) { if (!user) {
throw new NotFoundException(`Aucun⋅e utilisateur⋅rice avec pour nom ${name}`) throw new NotFoundException(`Aucun⋅e utilisateur⋅rice avec pour nom ${name}`)
} }
const isPasswordValid = await bcrypt.compare(password, user.password) const isPasswordValid = await bcrypt.compare(password, user.password)
if (!isPasswordValid) { if (!isPasswordValid) {
throw new UnauthorizedException('Mot de passe incorrect') throw new UnauthorizedException('Mot de passe incorrect')
} }
@ -24,4 +22,4 @@ export class AuthService {
accessToken: this.jwtService.sign({ userId: user.id }), accessToken: this.jwtService.sign({ userId: user.id }),
} }
} }
} }

View File

@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger' import { ApiProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsString, MinLength } from 'class-validator' import { IsNotEmpty, IsString } from 'class-validator'
export class LoginDto { export class LoginDto {
@IsString() @IsString()

View File

@ -3,6 +3,7 @@ import { PassportStrategy } from '@nestjs/passport'
import { ExtractJwt, Strategy } from 'passport-jwt' import { ExtractJwt, Strategy } from 'passport-jwt'
import { JWT_SECRET } from './auth.module' import { JWT_SECRET } from './auth.module'
import { UsersService } from 'src/users/users.service' import { UsersService } from 'src/users/users.service'
import { User } from '@prisma/client'
@Injectable() @Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
@ -13,7 +14,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
}) })
} }
async validate(payload: { userId: number }) { async validate(payload: { userId: number }): Promise<User> {
const user = await this.usersService.findOne(payload.userId) const user = await this.usersService.findOne(payload.userId)
if (!user) { if (!user) {
throw new UnauthorizedException() throw new UnauthorizedException()

View File

@ -0,0 +1,10 @@
import { ApiProperty } from '@nestjs/swagger'
import { IsNotEmpty, IsString, MinLength } from 'class-validator'
export class UpdatePasswordDto {
@IsString()
@IsNotEmpty()
@MinLength(8)
@ApiProperty()
password: string
}

View File

@ -1,8 +1,12 @@
import { Controller, Get, NotFoundException, Param, ParseIntPipe, UseGuards } from '@nestjs/common' import { Body, Controller, Get, HttpCode, NotFoundException, Param, ParseIntPipe, Patch, Post, Req, UseGuards } from '@nestjs/common'
import { UsersService } from './users.service' import { UsersService } from './users.service'
import { ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse } from '@nestjs/swagger' import { ApiBadRequestResponse, ApiBearerAuth, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
import { UserEntity } from './entities/user.entity' import { UserEntity } from './entities/user.entity'
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard' import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'
import { User } from '@prisma/client'
import { UpdatePasswordDto } from './dto/user_password.dto'
export type AuthenticatedRequest = Request & { user: User }
@Controller('users') @Controller('users')
export class UsersController { export class UsersController {
@ -12,6 +16,8 @@ export class UsersController {
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@ApiOkResponse({ type: UserEntity, isArray: true }) @ApiOkResponse({ type: UserEntity, isArray: true })
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
async findAll() { async findAll() {
const users = await this.usersService.findAll() const users = await this.usersService.findAll()
return users.map(user => new UserEntity(user)) return users.map(user => new UserEntity(user))
@ -21,6 +27,8 @@ export class UsersController {
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@ApiOkResponse({ type: UserEntity }) @ApiOkResponse({ type: UserEntity })
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
@ApiNotFoundResponse({ description: "Utilisateur⋅rice non trouvé⋅e" }) @ApiNotFoundResponse({ description: "Utilisateur⋅rice non trouvé⋅e" })
async findOne(@Param('id', ParseIntPipe) id: number) { async findOne(@Param('id', ParseIntPipe) id: number) {
const user = await this.usersService.findOne(id) const user = await this.usersService.findOne(id)
@ -28,4 +36,17 @@ export class UsersController {
throw new NotFoundException(`L'utilisateur⋅rice avec l'identifiant ${id} n'existe pas`) throw new NotFoundException(`L'utilisateur⋅rice avec l'identifiant ${id} n'existe pas`)
return new UserEntity(user) return new UserEntity(user)
} }
@Patch('/update-password')
@HttpCode(204)
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiNoContentResponse({description: "Le mot de passe a bien été modifié."})
@ApiBadRequestResponse({description: "Erreur dans la saisie du nouveau mot de passe."})
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
@ApiForbiddenResponse({ description: "Permission refusée" })
async updatePassword(@Req() request: AuthenticatedRequest, @Body() { password }: UpdatePasswordDto) {
const user = request.user
await this.usersService.updatePassword(user, password)
}
} }

View File

@ -1,5 +1,7 @@
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { User } from '@prisma/client'
import { PrismaService } from 'src/prisma/prisma.service' import { PrismaService } from 'src/prisma/prisma.service'
import * as bcrypt from 'bcrypt'
@Injectable() @Injectable()
export class UsersService { export class UsersService {
@ -12,4 +14,12 @@ export class UsersService {
async findOne(id: number) { async findOne(id: number) {
return await this.prisma.user.findUnique({ where: { id } }) return await this.prisma.user.findUnique({ where: { id } })
} }
async updatePassword(user: User, password: string) {
const hashedPassword = await bcrypt.hash(password, 10)
await this.prisma.user.update({
where: { id: user.id },
data: { password: hashedPassword }
})
}
} }