From ebe682ca0aa1b6d1e87dbf27ec064a618eece825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ruiz?= Date: Tue, 20 May 2025 10:54:44 +0200 Subject: [PATCH] Added reset password endpoint --- src/common/custom-error/custom-error.model.ts | 1 + src/pods/user/user.api-model.ts | 6 ++++ src/pods/user/user.rest-api.ts | 36 ++++++++++++++++++- src/pods/user/validations/index.ts | 1 + .../user/validations/password-validation.ts | 24 +++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/pods/user/validations/password-validation.ts diff --git a/src/common/custom-error/custom-error.model.ts b/src/common/custom-error/custom-error.model.ts index 6fca633..52e5734 100644 --- a/src/common/custom-error/custom-error.model.ts +++ b/src/common/custom-error/custom-error.model.ts @@ -4,6 +4,7 @@ export enum CustomInternalCodes { UserNotFound = 'user-002', RolNotFound = 'user-003', UnidadNotFound = 'user-004', + InvalidPassword = 'user-005', } export interface Error { diff --git a/src/pods/user/user.api-model.ts b/src/pods/user/user.api-model.ts index 27d6432..fbf5279 100644 --- a/src/pods/user/user.api-model.ts +++ b/src/pods/user/user.api-model.ts @@ -27,3 +27,9 @@ export interface SaveUserParams { hashedPassword: string; isTemporalPassword: boolean; } + +export interface ChangePasswordParams { + id: string; + contraseñaActual: string; + nuevaContraseña: string; +} diff --git a/src/pods/user/user.rest-api.ts b/src/pods/user/user.rest-api.ts index 016c210..c3f05f0 100644 --- a/src/pods/user/user.rest-api.ts +++ b/src/pods/user/user.rest-api.ts @@ -10,7 +10,7 @@ import { mapUserFromApiToModel, mapUserUpdateFromApiToModel, } from './user.mappers.js'; -import { validationPostUser, validationUpdateUser } from './validations/index.js'; +import { validationPostUser, validationUpdateUser, validationChangePassword } from './validations/index.js'; import * as apiModel from './user.api-model.js'; import * as model from '#dals/user/user.model.js'; @@ -116,4 +116,38 @@ userApi } catch (error) { next(error); } + }) + .post('/change-password', async (req, res, next) => { + try { + const passwordData: apiModel.ChangePasswordParams = req.body; + const validationResult = await validationChangePassword(passwordData); + + if (validationResult.succeded) { + const hashedPassword = await hash(passwordData.nuevaContraseña); + const user = await userRepository.getUser(passwordData.id); + + await userRepository.saveUser({ + ...user, + contraseña: hashedPassword, + esContraseñaTemporal: false, + }); + + res.sendStatus(204); + } else { + const statusCode = (() => { + switch (validationResult.error?.error) { + case CustomInternalCodes.UserNotFound: + return 422; + case CustomInternalCodes.InvalidPassword: + return 401; + default: + return 400; + } + })(); + + res.status(statusCode).send(validationResult.error); + } + } catch (error) { + next(error); + } }); diff --git a/src/pods/user/validations/index.ts b/src/pods/user/validations/index.ts index 622dc4c..aa209cc 100644 --- a/src/pods/user/validations/index.ts +++ b/src/pods/user/validations/index.ts @@ -1,2 +1,3 @@ export * from './user-validations.js'; export * from './update-user-validation.js'; +export * from './password-validation.js'; diff --git a/src/pods/user/validations/password-validation.ts b/src/pods/user/validations/password-validation.ts new file mode 100644 index 0000000..f7bd9d3 --- /dev/null +++ b/src/pods/user/validations/password-validation.ts @@ -0,0 +1,24 @@ +import { CustomInternalCodes, ValidationInfo } from '#common/custom-error/index.js'; +import { verifyHash } from '#common/helpers/index.js'; +import { userRepository } from '#dals/user/user.repository.js'; +import * as apiModel from '../user.api-model.js'; + +export const validationChangePassword = async ( + passwordData: apiModel.ChangePasswordParams +): Promise => { + if (!passwordData.id || !passwordData.contraseñaActual || !passwordData.nuevaContraseña) { + return { succeded: false, error: { error: CustomInternalCodes.FieldNotInformed } }; + } + + const user = await userRepository.getUser(passwordData.id); + if (!user) { + return { succeded: false, error: { error: CustomInternalCodes.UserNotFound } }; + } + + const isValidPassword = await verifyHash(passwordData.contraseñaActual, user.contraseña); + if (!isValidPassword) { + return { succeded: false, error: { error: CustomInternalCodes.InvalidPassword } }; + } + + return { succeded: true }; +};