Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Angular -> how to check if the user has permission using role-based access with the role save in the data base

I'm trying to make a role-based access for my app in angular, and I need some help because I'm newbie in angular ...

First this is what I have in the route where I establish which roles can access it...

from app-routing.module.ts

{
  path: 'profile',
  component: ComunityComponent,
  canActivate: [AuthGuard],
  data: {
    allowedRoles: ['admin', 'user']
  }
},

Second I use the canActivate function to check if the user can access the route or not. from auth.guard.ts

private hasAccess: boolean;

constructor(private router: Router, private auth: AuthService) {}

canActivate(next: ActivatedRouteSnapshot,
              state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const allowedRoles = next.data.allowedRoles;
    if (localStorage.getItem('currentUser')) {
      return this.checkAccess(localStorage.getItem('currentUser'), allowedRoles);
    }

    this.router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
    return false;
  }

Third, I develop the function accessLoginToken, that will find the role of the logged user through an httpclient.get service which returns an array with the roles assigned to the user. Example {success: true, roles:['user']} from auth.service.ts

 accessLoginToken(token: string) {
    const check = '/users/access';
    const httpOptions = {
      headers: new HttpHeaders({
        'Authorization': 'Bearer ' + token,
      })
    };
    return this.http.get<any>('/api' + check, httpOptions).pipe(
      catchError(err => this.handleError('accessLoginToken', err))
    );
  }

Fourth in the function checkAccess, I compare it with those of the route and if they match I allow the access otherwise not. from auth.guard.ts

private checkAccess(token: string, allowedRoles: string[]): boolean {
    this.hasAccess = false;
    this.auth.accessLoginToken(token).subscribe(data => {
      if (data.success) {
        data.roles.forEach(rol => {
          if (allowedRoles.findIndex(rols => rols === rol) >= 0) {
            this.hasAccess = true;
          }
        });
      }
    }, (error) => {
      console.log(error);
    });
    console.log(this.hasAccess);
    return this.hasAccess;
  }

The main problem is that I can not get the httpclient subscribe to change the value of the hasAccess variable to true to allow access. Even when the code this.hasAccess = true; is executed the function returns false

By the way I do not like the idea of ​​saving the role in a session variable like the access_token so I'm trying to keep it in the database...

any help on the subject will be appreciated .. thanks

like image 420
Raciel Brito Dorta Avatar asked Oct 17 '22 08:10

Raciel Brito Dorta


1 Answers

You're correct, the checkAccess function finishes before the async api call finishes, so false is always returned. Instead you should pass the observable to the canActivate method.

private checkAccess(token: string, allowedRoles: string[]): boolean {
    this.hasAccess = false;
    return this.auth.accessLoginToken(token).pipe(
        map(data => {
            if (data.success) {
                data.roles.forEach(rol => {
                    if (allowedRoles.findIndex(rols => rols === rol) >= 0) {
                        this.hasAccess = true;
                    }
                });
            }
            return this.hasAccess;
        })
    )
}

Because an observable is returned to the canActivate method, the guard will subscribe to the observable and wait for a true or false result.

like image 115
LLai Avatar answered Oct 19 '22 01:10

LLai