Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

🛡️ Fire Shield - Angular Adapter

Angular integration for Fire Shield RBAC authorization with services, directives, and route guards.

Features

  • RBACService - Injectable service with RxJS observables
  • Structural Directives - *fsCanPermission, *fsHasRole, *fsCannotPermission
  • Route Guards - canActivatePermission, canActivateRole, canActivateRBAC
  • Reactive State - RxJS BehaviorSubject for user state
  • TypeScript Support - Full type safety
  • Standalone Components - Compatible with Angular 14+

Installation

npm install @fire-shield/angular @fire-shield/core

Quick Start

1. Initialize RBAC Service

// app.config.ts (Angular 17+) or main.ts
import { ApplicationConfig } from '@angular/core';
import { RBAC } from '@fire-shield/core';
import { RBACService } from '@fire-shield/angular';

export const appConfig: ApplicationConfig = {
  providers: [
    // ... other providers
  ]
};

// In your app initialization
export function initializeRBAC(rbacService: RBACService) {
  return () => {
    const rbac = new RBAC();
    rbac.createRole('admin', ['user:*', 'post:*']);
    rbac.createRole('editor', ['post:read', 'post:write']);

    rbacService.initialize(rbac, null);
  };
}

2. Update User on Login

import { Component } from '@angular/core';
import { RBACService } from '@fire-shield/angular';

@Component({
  selector: 'app-login',
  template: `<button (click)="login()">Login</button>`
})
export class LoginComponent {
  constructor(private rbacService: RBACService) {}

  login() {
    const user = { id: 'user-1', roles: ['editor'] };
    this.rbacService.setUser(user);
  }
}

3. Use in Components

import { Component } from '@angular/core';
import { RBACService, CanPermissionDirective } from '@fire-shield/angular';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-posts',
  standalone: true,
  imports: [CommonModule, CanPermissionDirective],
  template: `
    <!-- Using observable -->
    <button *ngIf="canWrite$ | async">Create Post</button>

    <!-- Using directive -->
    <div *fsCanPermission="'post:write'">
      <button>Create Post</button>
    </div>
  `
})
export class PostsComponent {
  canWrite$ = this.rbacService.can$('post:write');

  constructor(private rbacService: RBACService) {}
}

API

RBACService

Injectable service that provides RBAC functionality.

Methods

initialize(rbac: RBAC, initialUser?: RBACUser | null)

const rbac = new RBAC();
rbacService.initialize(rbac, null);

setUser(user: RBACUser | null)

rbacService.setUser({ id: 'user-1', roles: ['editor'] });

getUser(): RBACUser | null

const user = rbacService.getUser();

can(permission: string): boolean

const allowed = rbacService.can('post:write');

can$(permission: string): Observable

rbacService.can$('post:write').subscribe(allowed => {
  console.log('Can write:', allowed);
});

hasRole(role: string): boolean

const isAdmin = rbacService.hasRole('admin');

hasRole$(role: string): Observable

rbacService.hasRole$('admin').subscribe(isAdmin => {
  console.log('Is admin:', isAdmin);
});

authorize(permission: string): AuthorizationResult

const result = rbacService.authorize('post:delete');
if (!result.allowed) {
  console.log('Denied:', result.reason);
}

canAll(permissions: string[]): boolean

const hasAll = rbacService.canAll(['post:read', 'post:write']);

canAny(permissions: string[]): boolean

const hasAny = rbacService.canAny(['post:read', 'post:write']);

Directives

*fsCanPermission

Conditionally renders content based on permission.

<div *fsCanPermission="'post:write'">
  Create Post Button
</div>

*fsHasRole

Conditionally renders content based on role.

<div *fsHasRole="'admin'">
  Admin Panel
</div>

*fsCannotPermission

Conditionally renders content when permission is NOT present.

<div *fsCannotPermission="'premium:access'">
  <p>Upgrade to Premium!</p>
</div>

Route Guards

canActivatePermission

Protects routes based on permissions.

import { Routes } from '@angular/router';
import { canActivatePermission } from '@fire-shield/angular';

export const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [canActivatePermission],
    data: { permission: 'admin:access', redirectTo: '/unauthorized' }
  }
];

canActivateRole

Protects routes based on roles.

export const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [canActivateRole],
    data: { role: 'admin', redirectTo: '/unauthorized' }
  }
];

canActivateRBAC

Combined guard for both permissions and roles.

export const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [canActivateRBAC],
    data: {
      permission: 'admin:access',
      role: 'admin',
      redirectTo: '/unauthorized'
    }
  }
];

Multiple Permissions/Roles

export const routes: Routes = [
  {
    path: 'posts',
    component: PostsComponent,
    canActivate: [canActivatePermission],
    data: {
      permissions: ['post:read', 'post:write'],
      requireAll: true, // or false for "any"
      redirectTo: '/unauthorized'
    }
  }
];

Examples

Component with Multiple Checks

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RBACService, CanPermissionDirective, HasRoleDirective } from '@fire-shield/angular';

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [CommonModule, CanPermissionDirective, HasRoleDirective],
  template: `
    <h1>Dashboard</h1>

    <!-- Observable approach -->
    <nav>
      <a routerLink="/">Home</a>
      <a *ngIf="canManagePosts$ | async" routerLink="/posts">Posts</a>
      <a *ngIf="isAdmin$ | async" routerLink="/admin">Admin</a>
    </nav>

    <!-- Directive approach -->
    <div *fsCanPermission="'post:write'">
      <button>Create Post</button>
    </div>

    <div *fsHasRole="'admin'">
      <h2>Admin Controls</h2>
    </div>

    <div *fsCannotPermission="'premium:access'">
      <p>Upgrade to Premium for more features!</p>
    </div>
  `
})
export class DashboardComponent {
  canManagePosts$ = this.rbacService.can$('post:write');
  isAdmin$ = this.rbacService.hasRole$('admin');

  constructor(private rbacService: RBACService) {}
}

Form with Conditional Fields

@Component({
  selector: 'app-user-form',
  standalone: true,
  imports: [CommonModule, FormsModule, CanPermissionDirective],
  template: `
    <form>
      <input name="name" [(ngModel)]="user.name" placeholder="Name" />

      <div *fsCanPermission="'user:edit:email'">
        <input name="email" [(ngModel)]="user.email" type="email" />
      </div>

      <div *fsCanPermission="'user:edit:role'">
        <select name="role" [(ngModel)]="user.role">
          <option value="user">User</option>
          <option value="editor">Editor</option>
          <option *fsHasRole="'admin'" value="admin">Admin</option>
        </select>
      </div>

      <button type="submit">Save</button>
    </form>
  `
})
export class UserFormComponent {
  user = { name: '', email: '', role: 'user' };

  constructor(private rbacService: RBACService) {}
}

Route Configuration

import { Routes } from '@angular/router';
import { canActivatePermission, canActivateRole, canActivateRBAC } from '@fire-shield/angular';

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'posts',
    component: PostsComponent,
    canActivate: [canActivatePermission],
    data: { permission: 'post:read', redirectTo: '/unauthorized' }
  },
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [canActivateRole],
    data: { role: 'admin', redirectTo: '/unauthorized' }
  },
  {
    path: 'editor',
    component: EditorComponent,
    canActivate: [canActivateRBAC],
    data: {
      permissions: ['post:read', 'post:write'],
      role: 'editor',
      requireAll: true,
      redirectTo: '/unauthorized'
    }
  },
  {
    path: 'unauthorized',
    component: UnauthorizedComponent
  }
];

TypeScript Support

import type { RBACUser } from '@fire-shield/angular';

interface User extends RBACUser {
  email: string;
  name: string;
}

const user: User = {
  id: 'user-1',
  roles: ['editor'],
  email: 'user@example.com',
  name: 'John Doe'
};

rbacService.setUser(user);

Learn More

License

DIB © Fire Shield Team