From 06b4ab716665bb154d3b6886ffc734179c658ad2 Mon Sep 17 00:00:00 2001 From: karol Date: Fri, 10 Jul 2026 14:56:18 +0000 Subject: [PATCH] =?UTF-8?q?Actualizaci=C3=B3n=20m=C3=B3dulo=20de=20roles?= =?UTF-8?q?=20e=20implementaci=C3=B3n=20de=20permission=20middleware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/modules/auth/auth.service.ts | 3 +- .../src/modules/auth/permission.middleware.ts | 74 +++++++++++++++++++ backend/src/modules/roles/role.controller.ts | 5 +- backend/src/modules/roles/role.middleware.ts | 29 -------- backend/src/modules/roles/role.model.ts | 6 +- backend/src/modules/roles/role.routes.ts | 13 ++-- backend/src/modules/roles/role.service.ts | 57 ++++++++++++++ 7 files changed, 146 insertions(+), 41 deletions(-) create mode 100644 backend/src/modules/auth/permission.middleware.ts delete mode 100644 backend/src/modules/roles/role.middleware.ts diff --git a/backend/src/modules/auth/auth.service.ts b/backend/src/modules/auth/auth.service.ts index 75dc2e9..8caf111 100644 --- a/backend/src/modules/auth/auth.service.ts +++ b/backend/src/modules/auth/auth.service.ts @@ -9,8 +9,7 @@ export class AuthService { userName:string, password:string ){ - const user = - await userModel.findOne({ + const user = await userModel.findOne({ userName }).populate("roleId"); diff --git a/backend/src/modules/auth/permission.middleware.ts b/backend/src/modules/auth/permission.middleware.ts new file mode 100644 index 0000000..88f3525 --- /dev/null +++ b/backend/src/modules/auth/permission.middleware.ts @@ -0,0 +1,74 @@ +import { Request, Response, NextFunction } from "express"; + +export const permissionMiddleware = ( + module: string, + action: string +) => { + + return ( + req: Request, + res: Response, + next: NextFunction + ) => { + + try { + + 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 + + ); + + + if (!hasPermission) { + + return res.status(403).json({ + + message: "No tiene permisos para realizar esta acción." + + }); + + } + + + next(); + + } catch (error) { + + return res.status(500).json({ + + message: "Error al validar permisos.", + + error + + }); + + } + + }; + +}; \ No newline at end of file diff --git a/backend/src/modules/roles/role.controller.ts b/backend/src/modules/roles/role.controller.ts index 2dab603..ad2296e 100644 --- a/backend/src/modules/roles/role.controller.ts +++ b/backend/src/modules/roles/role.controller.ts @@ -57,9 +57,10 @@ export class RoleController { res.status(201).json(role); - } catch(error){ + } catch(error:any){ res.status(500).json({ - message:"Error al crear role" + message:error.message, + error }); } diff --git a/backend/src/modules/roles/role.middleware.ts b/backend/src/modules/roles/role.middleware.ts deleted file mode 100644 index 0037e82..0000000 --- a/backend/src/modules/roles/role.middleware.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Request, Response, NextFunction } from "express"; - -export const roleMiddleware = -(allowedRoles: string[]) => { - - return ( - req: any, - res: Response, - next: NextFunction - ) => { - - const userRole = - req.user.roleName; - - if ( - !allowedRoles.includes( - userRole - ) - ) { - - return res.status(403).json({ - message: - "No tiene permisos para realizar esta acción" - }); - } - - next(); - }; -}; \ No newline at end of file diff --git a/backend/src/modules/roles/role.model.ts b/backend/src/modules/roles/role.model.ts index dceb07c..3c05bf3 100644 --- a/backend/src/modules/roles/role.model.ts +++ b/backend/src/modules/roles/role.model.ts @@ -5,11 +5,13 @@ const roleSchema = new mongoose.Schema({ roleName:{ type:String, required:true, - unique:true + unique:true, + trim:true }, description:{ - type:String + type:String, + trim:true }, active:{ diff --git a/backend/src/modules/roles/role.routes.ts b/backend/src/modules/roles/role.routes.ts index 56ad24d..3e08ce5 100644 --- a/backend/src/modules/roles/role.routes.ts +++ b/backend/src/modules/roles/role.routes.ts @@ -1,19 +1,20 @@ import { Router } from "express"; import { RoleController } from "./role.controller"; import { authMiddleware } from "../auth/auth.middleware"; +import { permissionMiddleware } from "../auth/permission.middleware"; const roleRouter = Router(); const controller = new RoleController(); -roleRouter.get("/", authMiddleware, controller.getAll); +roleRouter.get("/", authMiddleware, permissionMiddleware("roles", "read"), controller.getAll); -roleRouter.get("/:id", authMiddleware, controller.getById); +roleRouter.get("/:id", authMiddleware, permissionMiddleware("roles", "read"), controller.getById); -roleRouter.post("/", authMiddleware, controller.create); +roleRouter.post("/", authMiddleware, permissionMiddleware("roles", "create"), controller.create); -roleRouter.put("/:id", authMiddleware, controller.update); +roleRouter.put("/:id", authMiddleware, permissionMiddleware("roles", "update"), controller.update); -roleRouter.delete("/:id", authMiddleware, controller.delete); +roleRouter.delete("/:id", authMiddleware, permissionMiddleware("roles", "delete"), controller.delete); -export default roleRouter; \ No newline at end of file +export default roleRouter ; \ No newline at end of file diff --git a/backend/src/modules/roles/role.service.ts b/backend/src/modules/roles/role.service.ts index b8e0df2..21c5933 100644 --- a/backend/src/modules/roles/role.service.ts +++ b/backend/src/modules/roles/role.service.ts @@ -1,7 +1,10 @@ import roleModel from "./role.model"; +import userModel from "../user/user.model"; + import { IRole } from "./role.interface"; export class RoleService { + async getAll() { return await roleModel.find(); @@ -16,6 +19,18 @@ export class RoleService { async create( data:IRole ) { + + const exists = await roleModel.findOne({ + + roleName:data.roleName + + }); + + if(exists){ + + throw new Error("Ya existe un rol con ese nombre."); + } + return await roleModel.create(data); } @@ -23,6 +38,23 @@ export class RoleService { id:string, data:Partial ){ + if (data.roleName) { + + const exists = await roleModel.findOne({ + + roleName: data.roleName, + + _id: { + $ne: id + } + + }); + + if (exists) { + throw new Error("Ya existe un rol con ese nombre."); + } + } + return await roleModel.findByIdAndUpdate( id, data, @@ -35,6 +67,31 @@ export class RoleService { async delete( id:string ){ + // Verificar que el rol exista + const role = await roleModel.findById(id); + + if (!role) { + return null; + } + + // Verificar si el rol está asignado a usuarios + const usersWithRole = await userModel.countDocuments({ + roleId: id + }); + + if (usersWithRole > 0) { + throw new Error( + "No se puede eliminar el rol porque está asignado a uno o más usuarios." + ); + } + + if(role.roleName === "ADMINISTRADOR"){ + + throw new Error( + + "No se puede eliminar el rol Administrador."); + } + return await roleModel.findByIdAndDelete(id) } } \ No newline at end of file