Manejando roles y permisos en Angular

Aprende cómo manejar roles y permisos de forma fácil usando ngx-permissions en Angular

Antes de empezar quiero mencionar que no hablaremos de cómo manejar esto a nivel de backend, tampoco ahondaremos en todo esto, pero sí veremos su implementación sencilla para permitirnos utilizar roles y permisos dentro de nuestra aplicación. Antes que nada, empezaremos con una JWT, si no conoces mucho del tema te dejo mis posts:

https://asfo.dev/c-mo-utilizar-jwt-en-angular-de-forma-f-cil-cc0f75e2e9c/
https://asfo.dev/autenticando-un-api-rest-con-nodejs-y-jwt-json-web-tokens-5f3674aba50e/

Para ello, entonces, en la JWT necesitaremos que dentro del scope vengan los roles y/o sus permisos. Esto lo puedes manejar de forma libre, sin embargo yo recomiendo un estilo:

roles: ['ADMIN'], permissions: ['canReadSomething', 'canWriteSomething', 'canUpdateSomething']

Donde los permisos sean lo más específicos posibles y los roles igual. Pero de alguna forma siempre intenten mantener la simpleza.

Ya que tenemos una JWT armada, bueno, hay otra cosa que tenemos que tener en cuenta y es que no manejaremos casos dinámicos, es decir, no habrá como un “quiero que X dato solamente pueda ser visto por Y rol” ni nada de ese tipo, estaremos hablando más acerca de acceder a rutas y elementos.

Ya tenemos todo preparado, ¿qué sigue?. Para este tutorial usaremos ngx-permissions como dependencia. Por lo que hay que instalarla:

npm install ngx-permissions

Una vez dentro hay que cargarla en el módulo que vamos a utilizarla, en este ejemplo pensaremos que está en el app.module por ello vamos a integrarlo llamando la siguiente línea en los imports y obviamente cargando la dependencia:

NgxPermissionsModule.forRoot()

Ahora bien, ¿qué sigue?. Para este tutorial también usaremos la dependencia de lectura de JWT. En realidad, no es algo requerido ya que podemos hacer nuestro propio código, pero vamos a usarla por si en algún momento quieren hacer algo más como validar tiempo y así y facilitarse la vida.

Para ello vamos a instalar la dependencia con:

npm install @auth0/angular-jwt

Ya que tenemos esto, ahora sí vamos a separar en 3 secciones esta situación:

La primera es, en el initializer, ya que antes de arrancar debemos verificar y setear los roles y permisos, para ello tengo también un post:

https://asfo.dev/usando-app-initializer-en-angular-e822f3af3fb5/

Para esto, vamos a integrar una sección de código de la siguiente forma:

// Primero generamos el import:
import {JwtHelperService} from '@auth0/angular-jwt';
import {NgxPermissionsService, NgxRolesService} from 'ngx-permissions';
// ...
// Después en nuestro constructor haremos la inyección de dependencia de ngx-permissions
constructor(private rolesService: NgxRolesService,
            private permissionsService: NgxPermissionsService) {
}
...
// Por último en nuestro load() haremos la asignación:
this.rolesService.addRole(decodedToken.roles[0], decodedToken.permissions);
this.permissionsService.addPermission(decodedToken.permissions);

Lo que hacemos aquí, cabe destacar, es que en nuestro load() vamos a detectar “si la token existe” y “si la token tiene tiempo de vida”, así como extra, pueden integrar si la token es válida.

Lo que hace esta sección es usar el servicio de roles de ngx-permissions y el de permisos, así nos asigna un rol y a su vez, sus permisos.

Ahora vamos a la segunda sección, las rutas, en nuestro app-routing.module vamos a cargar la dependencia:

import {NgxPermissionsGuard} from 'ngx-permissions';

Y a utilizarla, tomaremos como ejemplo la siguiente ruta:

{
  path: 'admin',
  component: AdminComponent,
  canActivate: [NgxPermissionsGuard],
  data: {
    permissions: {
      only: ['ADMIN'],
      redirectTo: '/forbidden'
    }
  }
},

Lo que hace es que en la ruta admin carga el componente AdminComponent y después haciendo uso de los Guards, protegerá esta ruta dependiendo de las condicionales que agreguemos que en este caso dependen de data y que podemos ver que, únicamente se le dará acceso al rol ADMIN y en caso de que no tenga este rol, lo redirigiremos a forbidden que sería una ruta para mostrar que no tienen acceso.

Cabe destacar que una JWT es posible que se modifique para acceder a esta ruta, sin embargo el backend deberá re-validar siempre.

Y ya por último, la parte del componente.

Dentro del constructor cargaremos el servicio de permisos en este caso, como habremos visto hay dos tipos, roles y permisos, pero como en este caso los roles nos sirven solamente para proteger rutas, vamos a proteger un elemento en nuestro componente, para ello lo primero es inyectar la dependencia del servicio de permisos:

constructor(private permissionsService: NgxPermissionsService)

Dado que ya están cargados los servicios desde nuestro APP_INITIALIZER podemos evitar la re-carga de permisos, en todo caso que lo quisieran, en el mismo constructor pueden llamar la función de carga de estos, con:

this.permissionsService.loadPermissions(['array_de_permisos']);

Ya teniendo todo lo anterior, es meramente llamar los permisos requeridos:

<div *ngxPermissionsOnly="['canReadSomething']">
     <div>Si tiene el permiso "canReadSomething"</div>
</div>

Y a la vez que se puede usar así, se puede cambiar canReadSomething por el rol, o por ejemplo si un usuario no puede actualizar información, se le agrega esta directiva y se le agrega el permiso requerido en un wrapper al <form>

Se puede usar también <ng-template> , esto para mayor detalle tienen la documentación en:

https://angular.io/api/core/ng-template

Y eso es todo.

Con esto, ahora podrán manejar roles y permisos básicos en sus rutas, desde el arranque en la aplicación para que no deban estar indicando los permisos en servicios extra ni nada y podrán hacer uso muy sencillo de esta librería.

Por último, quiero reclarar 3 cosas:

  • Siempre deben validar todo en back-end.
  • Los permisos se recomiendan sean en “camelCase” y los roles en mayúsculas.
  • Deben validar siempre la JWT para evitar que se las alteren.
¿Cuál es tu reacción?
+1
0
+1
0
+1
0
+1
2
+1
0
Total
0
Shares
Publicación anterior

Creando una tabla paginada con back-end (server-side) en Angular

Siguiente Publicación

Docker para principiantes — Parte 2

Publicaciones Relacionadas