Ir al contenido principal

Gestión de perfiles y permisos en aplicaciones Web

Cuando se desarrollan aplicaciones web (o de escritorio si es del caso) que requieren manejo de usuarios, una de las características más comunes a incluir es la de asignar permisos a cada usuario dependiendo de su rol o perfil dentro de la aplicación. A continuación se describen algunas sugerencias para una correcta administración y asignación de estos permisos.

Comencemos por establecer el comportamiento de estos llamados “permisos”. Básicamente, se trata de permitir a un usuario el poder acceder a una característica como por ejemplo el editar registros de una tabla particular, por ejemplo “inventario”. Así, un usuario que tenga en su perfil asignado el acceso a “inventario/editar” podrá hacerlo, en tanto aquel que no lo tenga, no podrá realizar esta actividad. Así las cosas, podemos relacionar el permiso con un menú asociado, en este caso “inventario/editar”, igual puede hacerse con “inventario/listar”, “inventario/adicionar”, “inventario/asignar-responsable” u otras acciones similares para otras tablas o servicios de la aplicación. Esto significa que debemos definir en algún lugar todos los posibles menús que serán usados. Téngase presente que estos menús pueden estar o no directamente asociados a un script particular, como por ejemplo “/control/inventario/editar.php” (veremos un poco más sobre esto más adelante).

Ahora bien, si has prestado atención (y estoy seguro que lo has hecho) habrás notado que al declarar el menú he usado “inventario/editar” y no “editar/inventario”. Esto para que de esta forma todos los menús relacionados con el manejo de la tabla “inventario” sean del tipo “inventario/xxxx”, donde “xxxx” representa la acción de interés, así se hace más fácil el encontrar los menús que corresponden a acciones sobre una tabla o servicio en particular. Por supuesto, pueden declararse tantos niveles como se quieran, por ejemplo “inventario/elemento/retirar” o similares, y aunque puede verse como una forma de organizar una sección de menú en la interfaz de usuario de la aplicación, esto debería ser una consecuencia y no la razón de ser de la misma, porque recordemos que los menús mostrados en pantalla pueden organizarse de muchas diferentes formas según la interfaz de usuario que se esté usando. Así que mi recomendación es siempre agrupar los menús teniendo presente su funcionalidad primero que todo.

Pasemos entonces a resolver la inquietud de “¿dónde debo almacenar mi listado de menús?”. Si estamos usando bases de datos para registro de usuarios, seguro el primer pensamiento será el de incluir una tabla con ese listado base de menús. Y aunque puede ser una aproximación válida en muchos escenarios, es una que no comparto por la siguiente razón: imagina que tu aplicación está instalada en varios clientes y necesitas instalar una actualización en la que se adicionan nuevos menús. Si tu listado de menús base (todavía no hemos entrado a la parte donde se configuran para cada usuario, paciencia) se encuentra en una tabla de la base de datos, tendrás que incluir un script o secuencia de comandos adicional solamente para adicionar ese nuevo listado. Ahora bien, si el listado es tomado de un archivo incluido con los otros archivos o scripts que serán copiados durante la actualización, no requerirás ningún trabajo adicional, simplificando y reduciendo puntos de falla en dicho proceso de actualización. Así pues, la recomendación es usar un archivo dentro del repositorio de scripts, ya sea en formato texto plano, CSV, archivos .ini, JSON u otro formato de tu preferencia (como usuario Windows que soy y por su fácil manejo, me inclino usualmente por archivos .ini). Por ejemplo:

inventory/edit = /control/inventory/actions/edit.php
inventory/item/remove = /control/inventory/remove.php
inventory/item/detach = /control/inventory/remove.php?deta=1

Sugerencia para desarrolladores: Asociar el path del menú con la ubicación del script puede ser una forma rápida de invocar dicho script, aunque también puede usarse el archivo de definiciones para indicar a qué script corresponde cada menú y evitar así las limitaciones de verse forzado a crear un script para cada menú, tal como se muestra en el ejemplo de arriba.

Una vez se haya leído la lista de menús disponibles, en tu sistema de Administración de usuarios puedes ya proceder a vincularlos a cada usuario, usando el método que mejor se acomode a tu sistema (ahora si, lo ideal sería mediante vínculos entre tablas de una base de datos). Si tienes varios usuarios que tendrán acceso a los mismos menús, quizás prefieras no realizar la asociación usuario→menú, sino algo intermedio, crear primero un listado de (adivinas?) perfiles. Pero, ¿a qué hace referencia “perfiles” en este contexto? Pues a una agrupación de menús disponibles designados con nombres característicos como “Administrador”, “visitante” o “usuario básico”, entre otros. De esta forma, la asociación a realizar será usuario→perfil→menú.

Ya para terminar, hay que tener presente que pueden existir usuarios con privilegios o excepciones especiales y que a pesar de pertenecer a un determinado perfil, requieran que les sean asignados menús adicionales o por el contrario, bloquear alguno ya permitido. En este caso, la solución podría ser una de las siguientes:

  •  Crear un nuevo perfil. Sin embargo, es posible que la aplicación tenga algunas acciones que por seguridad (o por decisiones “creativas”) solamente se permitan a usuarios con un perfil especifico y al asignarle uno diferente dejaría al usuario sin posibilidad de usarlo aun cuando tenga el permiso asignado.
  • Asignarle al usuario menús adicionales o restringir el uso de ciertos menús, de forma que se use primero la relación usuario→perfil→menú y luego se complemente con los adicionales que encuentre y/o excluya las excepciones, cada una asignada en una relación directa del tipo usuario→menú.
  • Permitir asignar al usuario más de un perfil (como sucede con muchos Sistemas Operativos).

Resumiendo:

  • Crear un listado con todas las opciones de menú permitidas en la aplicación.
  • Registrar el listado de opciones validas en un archivo texto (CSV, INI, JSON u otro).
  • Crear una administración de perfiles donde se asocien los menús.
  • Incluir en la administración de usuario la asociación al perfil de interés, ya sea en una relación de uno a uno o de uno a varios, entre usuarios y perfiles.
  • Opcionalmente, incluir para cada usuario la opción de asignar menús extra o excluir menús incluidos en el perfil.

¡Y eso es todo, amigos! Con estas sugerencias ya solo queda a) sentarse y buscar en Internet el administrador de perfiles que mejor se acomode a tus necesidades o b) codificar uno propio, ¿qué prefieres?

Si te gustó este contenido y te ha resultado útil, si tienes alguna observación o consideras que requiere alguna corrección o si simplemente quieres dejar un saludo, no dudes en dejar un  comentario, será un gesto apreciado y de esta forma ayudas a mejorar estos contenidos.

¡Hasta una próxima!

Imagen de fondo cortesía de Pixabay.

Comentarios

Entradas populares de este blog

Manejo de recursos HTML para tus páginas web con PHP

Déjame saber si te resulta familiar esta situación: páginas web que descargan el mismo recurso (sean estilos CSS o código Javascript) más de una vez o incluyen recursos remotos que tardan una eternidad en cada descarga. Yo lo he visto en más de una ocasión y no es difícil imaginar el porqué ocurre. Un desarrollador incluye el recurso de estilos que necesita su segmento de código y otro hace lo mismo, sin reparar (o sin que siquiera importe) que comparten el mismo recurso. En otro escenario muy común, acostumbran incluir muchos recursos remotos, con lo que el rendimiento de la página depende de lo rápido que responda dicho recurso. ¿Puede hacerse algo al respecto? Claro que si. Vamos a crear una clase en PHP que se encargue de administrar estos recursos y que nos facilite su despliegue en la página sin repeticiones . ¿Y respecto a la demora en la carga de recursos remotos? Atendamos una cosa por vez, porque como dicen por ahí: «Vísteme despacio, que tengo prisa». Administrando ...

Manejo de clases globales únicas en PHP

¿Cómo acceder desde cualquier script en tu proyecto a Clases y/o funciones de uso común? Este puede ser una de las primeras directrices a establecer para cualquier proyecto porque siempre, siempre , sea en  PHP  u otro lenguaje, será necesario usar recursos comunes. En PHP existen diferentes alternativas para su manejo, ya sea por medio de variables globales o de clases/objetos estáticos. A continuación consideraremos una propuesta para este manejo. Creación de recursos globales Para ilustrar esta solución, partimos de la necesidad de implementar una librería para manejo de servicios relacionados con el servidor Web, que de forma amigable nos permita disponer de información como: Valores almacenados de la variable superglobal $_SERVER de PHP. Valores asociados a la consulta realizada por el usuario, por Ej. la dirección IP del usuario o la URL ingresada. Valores asociados al servidor web usado, por Ej. la dirección IP del servidor o la ubicación del script que ej...

¿Qué tan bueno es realmente el “foreach” en PHP?

Como toda buena historia, esta comienza hace algún tiempo. El que fuera mi jefe por allá en la primera década del 2000, realmente odiaba (y mucho) el uso del foreach en el código PHP . Prefería que usáramos alguna alternativa diferente, alguna combinación del  for o del while . ¿Por qué? Ve tú a saber, nunca fue abierto respecto a las razones de su aprensión hacia ese constructor propio del lenguaje. Pero antes de continuar, veamos qué es y para qué nos puede servir. Arreglos, tenían que ser arreglos ¿Qué es foreach ? De acuerdo al manual de PHP , su definición es la siguiente: El constructor foreach proporciona un modo sencillo de iterar sobre arrays . foreach funciona sólo sobre arrays y objetos , y emitirá un error al intentar usarlo con una variable de un tipo diferente de datos o una variable no inicializada. Para su uso correcto existen dos sintaxis validas, a saber: foreach (expresión_array as $value) { ... } foreach (expresión_array as $key => $value) { ....