How To: Utilizar PrincipalPermission para bloquear Funcionalidades

Introducción

Hace unos días hablando con Antiocol, comentábamos sobre como llevar a cabo el bloqueo de funcionalidades / pantallas / vistas ante la existencia de distintos usuarios propios de la aplicación, es decir, no son usuarios de Windows.

Me comento la existencia del atributo PrincipalPermission, y decidí investigar un poco acerca de este tema.

El problema

Dada una aplicación multiusuario, en el que encontramos los perfiles: Encargado, Dependiente y Mozo, tenemos una serie de controles de Usuario(WPF) o formularios(Winforms) que no van a ser accesibles por todos ellos.

Dentro de nuestra Base de Datos, no voy a entrar a su definición, tenemos una serie de usuarios y la asociación con los distintos perfiles, por ejemplo: Juan puede ser Encargado, dependiente y Mozo, y Pedro solo es mozo.

Tenemos dos controles:

– Facturación que va a ser solo accesible por el perfil Encargado.

– Almacén: que va a ser accesible por Encargado, Dependiente y Mozo

Para resolver, esta situación vamos a ver una posible solución con WPF, y PrincipalPermission.

 

Solución

Una posible aproximación para alcanzar el objetivo puede ser la siguiente:

En primer lugar, tendremos que establecer el usuario y los roles del mismo para asignárselos como Identidad a la hora de ejecutar la aplicación.

 

   1: string NombreUsuario = ObtenerNombreUsuario();

   2: GenericIdentity identidad = new GenericIdentity(NombreUsuario);

   3: String[] roles = ObtenerRoles(NombreUsuario);

   4: GenericPrincipal MyPrincipal =

   5: new GenericPrincipal(identidad, roles);

   6: Thread.CurrentPrincipal = MyPrincipal;

Una vez realizada la asignación de los permisos, va a llegar el momento de verificar si un control dado puede ser visualizado por la Identidad actual:

 

   1: try

   2:            {

   3:                

   4:                PrincipalPermission MyPermission =

   5:                new PrincipalPermission(ObtenerUsuario(), "Mozo");

   6:                // Demand this permission.

   7:                MyPermission.Demand();

   8:                this.DataContext = new ViewModel.Control2();

   9:                

  10:            }

  11:            catch (SecurityException ex)

  12:            {

  13:  

  14:                this.Visibility = Visibility.Hidden; 

  15:            }

El método Demand se del objeto PrincipalPermission es el encargado de comprobar si el usuario tiene el rol indicado. Si el usuario dispone de dicho rol podrá visualizar el control sin ningún problema, en caso contrario, se provocará una excepción del tipo SecurityException indicándolo.

Para el correcto funcionamiento de esta solución es necesario que se incluyan los siguientes using:

– System.Security.Principal

– System.Security.Permissions

– System.Threading

– System.Security

 

Espero que os sea de utilidad, y que comenteis otras formas que conozcais.

 

Un saludo

7 comentarios en “How To: Utilizar PrincipalPermission para bloquear Funcionalidades”

  1. Hola Javier,

    Esta aproximación que comentas a mí personalmente no me gusta mucho y te voy a exponer el porque:

    Primero, porque vas a meter un montón de código repetivio en tu lógica de negocio, método trás método metiendo el mismo código (Rompe el principio DRY)

    Yo en mis aplicaciones el tema de seguridad lo trato como Cross-Cutting, es decir, algo que sea transversal a nuestra aplicación.

    Para una apróximación más simple podrías hacer uso de un patrón Decorator, aunque no evitas el DRY, porque por ejemplo si decoras una clase repositorio para meter seguridad, tendrás que meter el mismo código en cada método Save, Update, Delete…

    Con AOP también puedes hacerlo, pero tienes que estar con atributos por los métodos o clases (con PostSharp) con la desventaja que los atributos irán en tu código compilados y que solo pueden ser aplicados a clase, métodos, propiedades… pero no sirven si necesitas reglas dinámicas.

    La mejor aproximación desde mi punto de vista para implementar esto, es la intercepción dinámica con contenedores de dependencias, Unity, CastleWindsor… lo implementan y queda de maravilla

    Mis 2 centimos 😉

  2. Interesantísimo artículo. La idea de Luis de usar intercepción dinámica con contenedores me parece interesante, aunque no tengo nada de información de como hacerlo. Si alguien se anima… 😀

    Un saludo.

  3. Hola,
    @Luis le daré un vistazo a ver que tal 😀 muchas gracias por el comment!
    Aqui en principio creo que es suficiente definirlo, digamos, a nivel de vista, por lo que todo lo de la misma vista es o no accesible.

    Muchas gracias @Code2011! A ver si Luis se anima a poner un ejemplo :p

  4. Pues a mi nunca me ha convencido este método. Y no me convence porque me parece sospechosamente a usar las excepciones para controlar el flujo de tu aplicación.
    Sólo lo veo como un recurso válido en aquellos casos en que alguien desarrolla un framework interno que no sabe quien llamará y debe evitar ciertas funciones en función de roles. Pero en una aplicación, no lo termino de ver.

    Aunque de todos modos coincido plenamente con lo que ha dicho Luis: intercepción es el mejor mecanismo para lidiar con todo esto…

    @Jonathan
    Hace tiempo publiqué en mi blog un artículo sobre intercepción (usando Unity, pero lo mismo aplica a cualquier contenedor IoC). Lo puedes leer en http://geeks.ms/blogs/etomas/archive/2009/09/24/unity-proxies-aop-y-un-poco-de-todo-eso.aspx
    Es introductorio pero te puede servir como primer paso.

    Un saludo!

  5. @Eduard totalmente de acuerdo con respecto al tema de controlar el flujo a través de excepciones. Creo que lo han hecho así para hacerlo de la misma manera que se hace con el atributo que se puede utilizar a nivel de clase o método, que ahí, creo que no tienes otra opción.

  6. Interesante artículo e interesante tema el del control de permisos, que según mi percepción está lejos de una solución satisfactoria y estándar.

    Aunque AOP ofrece mucha potencia, no sé si es la mejor solución para esto. Sí si se quiere una solución implícita/declarativa, pero puede alcanzar una complejidad considerable por la cantidad de escenarios distintos donde hay que realizar distintas comprobaciones (pienso en vistas y controles como plantea Javier, pero también en menús, comandos, informes, datos escalares, listados configurables, filtros como almacén, empresa…). Y yo a día de hoy no conozco una librería que cubra un desarrollo de este tipo. Me refiero a una solución específica para permisos, no a una genérica de AOP o de IoC.

    De ahí que una solución imperativa me parezca más directamente aplicable, sobre todo a proyectos de menor envergadura. Basta con inyectar el comprobador apropiado y aplicarlo en los momentos donde sea necesario (muchas veces podrá ser de forma genérica, evitando repetición de código).

    Respecto al uso de Thread.Principal, nunca lo había visto, pero tal como se plantea no me gusta: yo no preguntaría “¿El usuario tiene el rol R?” sino “¿El usuario puede ver el control C?”. Así la definición de permisos es más flexible, y el código de comprobación que se distribuye por la aplicación tiene mucho menos peso.

    Todo esto da para un interesante debate. Gracias Javi.

  7. Buenas!!

    Yo es que estoy viendo toda esa lógica de seguridad en la vista y no me gusta 🙁
    Creo que las vistas deben ser simplemente eso: vistas, yo protegería las acciones dentro de la viewmodel, mediante el método que más te guste jeje, no la vista, así mantengo la idea de MVVM de independizar logica de presentación y vista.
    (lo se, lo se… soy un taliban de MVVM :P)

    Un saludote!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *