Actualización módulo auth, implementación del método de cambio de contraseña
This commit is contained in:
@ -31,6 +31,7 @@ export interface IAuditLog {
|
||||
| "REJECT"
|
||||
| "LOGIN"
|
||||
| "LOGOUT"
|
||||
| "CHANGE_PASSWORD"
|
||||
| "CLOSE";
|
||||
|
||||
recordId?: ObjectId;
|
||||
|
||||
@ -70,6 +70,8 @@ const auditLogSchema = new mongoose.Schema(
|
||||
|
||||
"LOGOUT",
|
||||
|
||||
"CHANGE_PASSWORD",
|
||||
|
||||
"CLOSE"
|
||||
|
||||
]
|
||||
|
||||
@ -34,7 +34,7 @@ export class AuthController {
|
||||
|
||||
} catch(error:any){
|
||||
|
||||
res.status(401).json({
|
||||
res.status(500).json({
|
||||
message:error.message
|
||||
});
|
||||
|
||||
@ -58,4 +58,32 @@ export class AuthController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async changePassword(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const user = (req as any).user;
|
||||
|
||||
const result = await authService.changePassword(
|
||||
|
||||
user.id,
|
||||
req.body
|
||||
|
||||
);
|
||||
|
||||
res.status(200).json(result);
|
||||
|
||||
} catch (error: any) {
|
||||
|
||||
res.status(500).json({
|
||||
|
||||
message: error.message,
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,50 +1,74 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
export const authMiddleware = (
|
||||
export const permissionMiddleware = (
|
||||
module: string,
|
||||
action: string
|
||||
) => {
|
||||
|
||||
return (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if(!authHeader){
|
||||
return res.status(401).json({
|
||||
message:"Token requerido"
|
||||
});
|
||||
}
|
||||
|
||||
//Verificar formato Bearer
|
||||
|
||||
if(!authHeader.startsWith("Bearer ")) {
|
||||
return res.status(401).json({
|
||||
message:"Formato de token inválido"
|
||||
});
|
||||
}
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
if (!token)
|
||||
{
|
||||
return res.status(401).json({
|
||||
message:"Token inválido"
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const decoded = jwt.verify (
|
||||
token,
|
||||
process.env.JWT_SECRET as string,
|
||||
const user = (req as any).user;
|
||||
|
||||
|
||||
if (!user) {
|
||||
|
||||
return res.status(401).json({
|
||||
message: "Usuario no autenticado."
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (!user.permissions) {
|
||||
|
||||
return res.status(403).json({
|
||||
message: "El usuario no tiene permisos asignados."
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
const hasPermission = user.permissions.some(
|
||||
|
||||
(permission: any) =>
|
||||
|
||||
permission.module === module &&
|
||||
permission.action === action
|
||||
|
||||
);
|
||||
|
||||
(req as any).user =
|
||||
decoded;
|
||||
|
||||
if (!hasPermission) {
|
||||
|
||||
return res.status(403).json({
|
||||
|
||||
message: "No tiene permisos para realizar esta acción."
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
next();
|
||||
} catch{
|
||||
return res.status(401).json({
|
||||
message:"Token inválido"
|
||||
|
||||
} catch (error) {
|
||||
|
||||
return res.status(500).json({
|
||||
|
||||
message: "Error al validar permisos.",
|
||||
|
||||
error
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
@ -3,13 +3,20 @@ import jwt from "jsonwebtoken";
|
||||
import userModel from "../user/user.model";
|
||||
import auditLogModel from "../auditLogs/auditLog.model";
|
||||
|
||||
import { IChangePassword } from "./changePassword.interface";
|
||||
|
||||
import { RolePermissionService } from '../role-permissions/rolePermission.service';
|
||||
|
||||
const service = new RolePermissionService();
|
||||
|
||||
export class AuthService {
|
||||
|
||||
async login (
|
||||
userName:string,
|
||||
password:string
|
||||
){
|
||||
const user = await userModel.findOne({
|
||||
const user =
|
||||
await userModel.findOne({
|
||||
userName
|
||||
}).populate("roleId");
|
||||
|
||||
@ -49,12 +56,27 @@ export class AuthService {
|
||||
|
||||
const role = user.roleId as any;
|
||||
|
||||
const rolePermissions = await service.getByRole(
|
||||
role._id.toString()
|
||||
);
|
||||
|
||||
const permissions = rolePermissions ?
|
||||
(rolePermissions.permissions as any []).map(
|
||||
permission => ({
|
||||
name:permission.name,
|
||||
module:permission.module,
|
||||
action:permission.action
|
||||
})
|
||||
)
|
||||
:[]
|
||||
|
||||
const token = jwt.sign(
|
||||
{
|
||||
|
||||
id:user._id,
|
||||
roleId: user.roleId,
|
||||
roleName: role.roleName
|
||||
roleId: role._id,
|
||||
roleName: role.roleName,
|
||||
permissions
|
||||
},
|
||||
|
||||
process.env.JWT_SECRET as string,
|
||||
@ -76,6 +98,8 @@ export class AuthService {
|
||||
|
||||
roleName: role.roleName,
|
||||
|
||||
permissions,
|
||||
|
||||
active: user.active
|
||||
}
|
||||
};
|
||||
@ -84,6 +108,12 @@ export class AuthService {
|
||||
async logout (
|
||||
userId:string
|
||||
){
|
||||
|
||||
if(!userId){
|
||||
|
||||
throw new Error("Usuario no autenticado.");
|
||||
|
||||
}
|
||||
await auditLogModel.create({
|
||||
userId,
|
||||
module:"Authentication",
|
||||
@ -96,4 +126,71 @@ export class AuthService {
|
||||
message:"Sesión cerrada exitosamente"
|
||||
};
|
||||
}
|
||||
|
||||
async changePassword(
|
||||
userId: string,
|
||||
data: IChangePassword
|
||||
) {
|
||||
|
||||
// Buscar usuario
|
||||
const user = await userModel.findById(userId);
|
||||
|
||||
if (!user) {
|
||||
throw new Error("Usuario no encontrado.");
|
||||
}
|
||||
|
||||
// Validar contraseña actual
|
||||
const validPassword = await bcrypt.compare(
|
||||
data.currentPassword,
|
||||
user.passwordHash
|
||||
);
|
||||
|
||||
if (!validPassword) {
|
||||
throw new Error("La contraseña actual es incorrecta.");
|
||||
}
|
||||
|
||||
// Validar que la nueva contraseña sea diferente
|
||||
if (data.currentPassword === data.newPassword) {
|
||||
throw new Error(
|
||||
"La nueva contraseña debe ser diferente a la actual."
|
||||
);
|
||||
}
|
||||
|
||||
// Validar longitud mínima
|
||||
if (data.newPassword.length < 8) {
|
||||
throw new Error(
|
||||
"La nueva contraseña debe tener al menos 8 caracteres."
|
||||
);
|
||||
}
|
||||
|
||||
// Encriptar nueva contraseña
|
||||
user.passwordHash = await bcrypt.hash(
|
||||
data.newPassword,
|
||||
10
|
||||
);
|
||||
|
||||
await user.save();
|
||||
|
||||
// Registrar en auditoría
|
||||
await auditLogModel.create({
|
||||
|
||||
userId: user._id,
|
||||
|
||||
module: "Authentication",
|
||||
|
||||
action: "CHANGE_PASSWORD",
|
||||
|
||||
description: "El usuario cambió su contraseña.",
|
||||
|
||||
status: "EXITOSO"
|
||||
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
message: "Contraseña actualizada correctamente."
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
7
backend/src/modules/auth/changePassword.interface.ts
Normal file
7
backend/src/modules/auth/changePassword.interface.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface IChangePassword {
|
||||
|
||||
currentPassword: string;
|
||||
|
||||
newPassword: string;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user