Las tres piezas: usuarios, roles, permisos
Un usuario es una persona o una máquina con identidad. Un permiso es una sola cosa con nombre que se puede hacer: leer este tipo de registro, aprobar esta transición, exportar este reporte. Un rol es un paquete de permisos con nombre. Los usuarios reciben roles, los roles reciben permisos, y el sistema nunca pregunta si esta persona es el jefe. Pregunta si tiene el permiso.
Esa indirección es todo el punto. Cuando un cargo cambia, cambias un rol y se mueve con él todo el que lo tenga. Cuando aparece un permiso nuevo, se lo otorgas a los roles que deben tenerlo en vez de buscar cada lugar del código donde se nombró un cargo. Los roles y sus permisos viven en la base de datos, editables desde un panel, no compilados en un despliegue.
Dónde ocurre el chequeo de verdad
Una petición atraviesa varias capas. Una capa de sesión adjunta a quién entró. Una validación en la ruta decide si la página se renderiza. La interfaz esconde los botones que ese rol no puede usar. Las tres sirven y ninguna es la frontera, porque cada una cuida un camino hacia los datos en lugar de cuidar los datos mismos.
El punto débil es el calce de rutas. Las validaciones se escriben contra patrones, y el ruteo real tiene grupos, reescrituras, prefijos de idioma y comodines; los dos se separan la primera vez que alguien agrega una ruta. Una validación que dejó de calzar en silencio falla abierta: la página se renderiza, la consulta corre y nada en el sistema reclama.
El chequeo que sí sostiene corre adentro de la función que lee o escribe los datos. Vuelve a derivar la identidad desde la petición, busca el rol, pregunta si ese rol tiene el permiso que esta operación exige, y cuando no lo tiene se niega. Como todos los que llaman pasan por esa función — la página, un celular, una tarea programada, una integración — no hay camino que la rodee.
Acceso por fila: cuáles registros, no solo cuáles pantallas
Tener un permiso responde qué tipo de cosa puedes hacer. No responde cuáles filas. Una cuenta de cliente y una de personal pueden tener las dos permiso de leer pedidos, y el cliente solo puede ver los suyos, nunca los de otro. Ese es un chequeo aparte, y va en el mismo lugar: la consulta, filtrada por la identidad que preguntó, antes de devolver nada.
El atajo tentador es traer todo y filtrarlo en la interfaz. En pantalla se ve idéntico y no es el mismo sistema, porque los datos ya salieron de la frontera. Cualquier cosa que vea la respuesta — la consola del navegador, un proxy, un registro de red guardado — ve las filas que ese usuario nunca debió recibir. El filtro va adentro de la consulta.
Así funciona también un canal con terceros. Un proveedor o un contratista recibe un rol cuyas lecturas están limitadas a los casos en los que está y a los campos que tú listaste, y a nada de al lado: ni comentarios internos, ni otros clientes, ni el hilo del caso vecino. Mismo mecanismo, otro alcance: un rol que puede leer todas las filas y otro que solo lee las propias.
Cómo se ve una implementación que funciona
En la práctica son cuatro cosas: un catálogo de permisos con nombre, una tabla de roles, una tabla que las cruza y una tabla que mapea cada ruta al permiso que exige. Las cuatro son datos, así que un administrador las edita desde una pantalla y el cambio aplica sin despliegue. La navegación lee esos mismos permisos, y a nadie le muestran una puerta que no puede abrir.
Dos cosas lo vuelven auditable. Cada cambio de rol o de permiso queda registrado con quién lo hizo y cuándo, así que un permiso que apareció se puede rastrear. Y quitar el acceso nunca borra la historia: suspendes una cuenta, sus sesiones terminan, y lo que hizo queda en el registro. El acceso es presente; la bitácora es pasado.