What's the best way to work with permissions in Angular 4?

Viewed 23181

In my Angular 4 project I would like to work with permissions, which I get from an API. Permissions are saved as array with ids. Some single elements like user or blog post have property with allowed permissions, which allow or not allow actions like edit or delete, as array with ids.

What's the best way to check and to handle permissions in Angular 4 projects? Has Angular some out of the bos solutions for permissions handling? Can somebody give me ideas for realization of permissions handling, if Angular doesn't have some out of the box solution?

4 Answers

From my personal point of view, get permission from API is optimal solution for user rights. In addition to checking permission by using canActivate attribute in Router, I like to use checking in navigation interceptor. Because it will keep the url when checking incorrect permissions instead of url localhost:42000/#/.../permission-denied

Here is my code:

In template

  <ng-template [ngIf]="!loading" [ngIfElse]="loadingView">
    <router-outlet *ngIf="canView"></router-outlet>
    <app-permission-denied *ngIf="!canView"></app-permission-denied>
  </ng-template>
  <app-loading #loadingView></app-loading>

In component

import { Component, OnInit} from '@angular/core';
import {
  Router,
  // import as RouterEvent to avoid confusion with the DOM Event
  Event as RouterEvent,
  NavigationStart,
  NavigationEnd,
  NavigationCancel,
  NavigationError
} from '@angular/router'
import { Title } from '@angular/platform-browser';
import { DataService } from '../core/service/data.service';
import { NotificationService } from '../core/service/notification.service';

@Component({
  selector: 'app-main',
  templateUrl: './main.component.html',
  styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit{

  // Sets initial value on first load
  loading: boolean = true;
  canView: boolean = false;
  haveErr: boolean = false;

  constructor(private renderer: Renderer2,
    private router: Router,
    private _dataService: DataService,
    private titleService: Title,
    private _notificationService: NotificationService) {   
    this.router.events.subscribe((event: RouterEvent) => {
      this.navigationInterceptor(event)
    })
  }

  ngOnInit() { }

  navigationInterceptor(event: RouterEvent): void {
    if (event instanceof NavigationStart) {
      this.loading = true;
      this.canView = false;
    }
    if (event instanceof NavigationEnd) {

      //get permission user by pass function ID here...
      let functionId = this.getDataRouter(this.router, "function");

      if (functionId != null) {
        this._dataService.get('your_api_to_check_permission_with_functionId' + functionId).subscribe(
          (data) => {
            if (data && data.CanRead) {
              this.canView = true;
              let title = this.getDataRouter(this.router, "title");
              this.titleService.setTitle(title);
            } else {
              this.canView = false;
              this.titleService.setTitle('Permission denied');
            }
            this.loading = false
          }, (err) => {
            this._dataService.handleError(err);
          });
      } else {
        this.loading = false;
        this.canView = true;
        this._notificationService.printErrorMessage('Dev: please provide function Id to check permission');
      }

    }

    // Set loading state to false in both of the below events to hide in case a request fails
    if (event instanceof NavigationCancel) {
      this.loading = false
    }
    if (event instanceof NavigationError) {
      this.loading = false
    }
  }

  getDataRouter(router, name) {
    var root = router.routerState.snapshot.root;
    while (root) {
      if (root.children && root.children.length) {
        root = root.children[0];
      } else if (root.data && root.data[name]) {
        return root.data[name];
      } else {
        break;
      }
    }
    return null;
  }
}

In router module

const mainRoutes: Routes = [
      { path: 'user', loadChildren: './system/user/user.module#UserModule', data: { function: "USER" } },

      { path: 'function', loadChildren: './system/function/function.module#FunctionModule', data: { function: "FUNCTION" } }
    ]
Related