DEV Community

Piotr Rgl.
Piotr Rgl.

Posted on • Updated on

Angular app permissions

This article is about the approach to frontend permissions based on simple library created in angular.

Let we imagine that we have simple block of code template e.g.

<ul>
  <li>Apple</li>
  <li>Banana</li>
  <li>Strawberry</li>
</ul>
Enter fullscreen mode Exit fullscreen mode

For some reason we want to prevent to display 'Apple' for specific user permission.

To be more precise we can create technically some simulation of permissions e.g.

const permissions: Permission[] = [
  {
    name: 'SOME_SIMPLE_PERMISSION',
  },
  {
    name: 'FRUITS',
    subjects:[{
      name:'APPLE',
      accessType:['read']
    }]
  },
];

@Injectable()
export class FakeApiService {
  public getPermissions(): Observable<Permission[]> {
    return of(permissions);
  }
}
Enter fullscreen mode Exit fullscreen mode

In the next step let we create some permission service:

export type PermissionAccessType = 'create' | 'read' | 'update' | 'delete';

export interface Permission {
  name: string;
  subjects?: Array<Permission>;
  accessType?: Array<PermissionAccessType>;
}

@Injectable()
export class PermissinionService {
  private _permissions$: Observable<Array<Permission>>;
  private _permissionsEmitter$: BehaviorSubject<Array<Permission>> = new BehaviorSubject<Array<Permission>>([]);
  private _currentPermission$: Observable<Permission | undefined>;

  constructor() {
    this._permissions$ = this._permissionsEmitter$.asObservable();
  }

  public setPermissions(permissions: Array<Permission>): void {
    this._permissionsEmitter$.next(permissions);
  }

  public checkIfPermission(
    name: string,
    accessType?: PermissionAccessType[]
  ): this {
    this._currentPermission$ = this._permissions$.pipe(
      map((permissions: Permission[]) => {
        const permission: Permission | undefined = permissions.find(
          (permission: Permission) => permission.name === name
        );

        if (permission && accessType) {
          return this.checkAccessType(accessType, permission)
            ? permission
            : undefined;
        }

        return permission;
      })
    );

    return this;
  }

  public for(subjectName: string): this {
    this._currentPermission$ = this._currentPermission$.pipe(
      map((permission: Permission | undefined) =>
        permission
          ? permission.subjects?.find(
              (subject: Permission) => subject.name === subjectName
            )
          : undefined
      )
    );

    return this;
  }

  public withAccessType(accessType: PermissionAccessType[]): this {
    this._currentPermission$ = this._currentPermission$.pipe(
      map((permission: Permission | undefined) => {
        if (permission) {
          return this.checkAccessType(accessType, permission)
            ? permission
            : undefined;
        }

        return undefined;
      })
    );

    return this;
  }

  public exits(): Observable<boolean> {
    return this._currentPermission$.pipe(map(Boolean));
  }

  private checkAccessType(
    accessType: PermissionAccessType[],
    permission: Permission
  ): boolean {
    return accessType.every((accessType: PermissionAccessType) =>
      permission.accessType?.includes(accessType)
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

After do that we can simple use it for the component e.g.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
  public canReadApple$: Observable<boolean>;

  constructor(private permissionService: PermissinionService) {}

  public ngOnInit(): void {
    this.canReadApple$ = this.permissionService
      .checkIfPermission('FRUITS')
      .for('APPLE')
      .withAccessType(['read'])
      .exits();
  }
}
Enter fullscreen mode Exit fullscreen mode

And on the end let we back to our template (app.component.html), and let we use our permission logic:

<ul>
  <li *ngIf="canReadApple$ | async">Apple</li>
  <li>Banana</li>
  <li>Strawberry</li>
</ul>
Enter fullscreen mode Exit fullscreen mode

In the next article we can create some directive connected with our PermissinionService and use it without *ngIf condition c.d.n.

GitHub: https://github.com/p-rogulski/uic-auth

Top comments (0)