0

Hi Angular Developers,

Please I need your help, I need to block routing with specific roles, This is my Doc with profiles:

enter image description here

My angular guards return and object with profiles: {admin: true, current: false}

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from '../services/auth.service';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})

export class RoleGuard implements CanActivate {

  constructor(
    public authService: AuthService,
    public router: Router
  ) { }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<any> | boolean {

    return this.authService.getAuthWithProfile().pipe(
      map((data) => {
        const roles = data.roles;
        return roles; // This returns   {admin: true, current: false}
      })
    );

   }
  }

The question is How implement the guard for Roles in the Angular Routing, for example:

{ path: 'tasks', component: TasksComponent, canActivate: [RoleGuard] },
lyonTypescript
  • 125
  • 1
  • 3
  • 10
  • What do the properties of data.roles mean? Based on your example do you want the navigate to continue or be blocked? Also do you have data attached to the route that is used for decisioning the guard? – Alexander Staroselsky Oct 02 '19 at 01:03
  • Thanks for your reply Alexander. In that property come 2 roles assigned to the user, which are {admin: true, current: false}, I want the navigation to be blocked, depending on the role that comes in the props – lyonTypescript Oct 02 '19 at 12:37
  • Okay for this guard in your example, what role needs to be present and true for navigation to continue? Do all routes using this guard use the same logic? Do you have multiple guards for each route that check for different specific roles? – Alexander Staroselsky Oct 02 '19 at 13:19
  • For example if admin is true, for this route { path: 'admin, component: AdminComponent, canActivate: [RoleGuard] }, I should access the route as Admin. The idea is that guard check for multiples roles. Thanks Alexander – lyonTypescript Oct 02 '19 at 13:38
  • That’s an issue. If you are using the same guard for different routes that need different roles, then how do you match that up. Do you know the exact roles needed for each route in your route configuration ahead of time and are those roles static? How example does this admin route always use the same role, or is it dynamic from some data source? – Alexander Staroselsky Oct 02 '19 at 13:43
  • ok I understand you. The administrator path is always the same. being like this, a route for a role, for example for the administrator {admin: true}, how should I implement it in the routing? – lyonTypescript Oct 02 '19 at 14:11

1 Answers1

1

One approach is using data property of Route. You can attach custom values to any given route. In your example you could create a property called roles that can be used in your canActivate(). In the following example an array of roles is added to the route for TasksComponent. In the guard we can extract roles from next.data.roles then check that the roles returning from Firebase are present and active:

Route:

{
  path: 'tasks',
  component: TasksComponent,
  data: {
    roles: ['admin', 'subscriber']
  }
  canActivate: [RoleGuard]
}

Guard:

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | boolean {
  const routeRoles = next.data && next.data.roles; // ['admin', 'subscriber']

  return this.authService.getAuthWithProfile().pipe(
    map((data) => {
      const roles = data.roles; // { admin: true, current: false }
      const activeRoles = Object.entries(roles).filter(([role, isActive]) => isActive).map(([role, isActive]) => role); // ['admin']

      return routeRoles.some(routeRole => activeRoles.includes(routeRole)); // true
    })
  );

  }
}

data.roles on the route does not have to be an array, it can instead be an object and you can approach checking the existence of the activated route in whatever fashion you are comfortable with. For example, if there is just a single role:

{
  path: 'tasks',
  component: TasksComponent,
  data: {
    role: 'admin'
  }
  canActivate: [RoleGuard]
}

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | boolean {
  const routeRole = next.data && next.data.role; // 'admin'

  return this.authService.getAuthWithProfile().pipe(
    map((data) => {
      const roles = data.roles; // { admin: true, current: false }
      const activeRoles = Object.entries(roles).filter(([role, isActive]) => isActive).map(([role, isActive]) => role); // ['admin']

      return routeRoles.includes(routeRole); // true
    })
  );

  }
}

Hopefully that helps!

Alexander Staroselsky
  • 37,209
  • 15
  • 79
  • 91