Sign Up for Free

RunKit +

Try any Node.js package right in your browser

This is a playground to test code. It runs a full Node.js environment and already has all of npm’s 400,000 packages pre-installed, including ngx-access with all npm packages installed. Try it out:

require("@angular/common/package.json"); // @angular/common is a peer dependency. require("@angular/core/package.json"); // @angular/core is a peer dependency. var ngxAccess = require("ngx-access")

This service is provided by RunKit and is not affiliated with npm, Inc or the package authors.

ngx-access v1.1.2

Add access control to your components using hierarchical configuration with logical expressions.

🔑 ngx-access 🔑


Add access control to your components using hierarchical configuration with logical expressions.

Npm version Build Status

Benefits of ngx-access

  • No more endless "ngIf statements" in your components
  • Define your access control as logical expressions
  • No need to add useless accesses for your Route/Layout components
  • Centralize your access control configuration
  • Document your application access control policy
  • Provide your own reactive strategy to verify if the user has a given access

In a nutshell

Access Expression Usage

<input *ngxAccessExpr="'CanUpdateAll | (CanUpdateUser & CanUpdateUserPassword)'" type="password" />

input element is displayed only if user has CanUpdateAll access or both CanUpdateUser and CanUpdateUserEmail accesses.

If user has CanUpdateAll access, CanUpdateUser and CanUpdateUserEmail will not be evaluated.

Access Configuration Usage

{
  "Home": {
    "Main": {
      "User": {
        "Email": {
          "Read": "CanReadUserEmail",
          "Update": "CanUpdateUserEmail"
        },
        "Password": {
          "Update": "CanUpdateUserPassword"
        },
        "Address": {
          "Read": "CanReadUserAddress",
          "Update": "CanUpdateUserAddress"
        }
      }
    }
  }
}
<app-user-form *ngxAccess="'Home.Main.User : Update'"></app-user-form>

app-user-form component is displayed only if the user has at least one of the Update accesses defined in the Home.Main.User access path hierarchy, namely: CanUpdateUserEmail or CanUpdateUserPassword or CanUpdateUserAddress accesses.

Demo

  • https://stackblitz.com/github/chihab/ngx-access

Getting Started

Install ngx-access

npm install --save ngx-access

Define the access control strategy

import { AccessStrategy } from 'ngx-access';

@Injectable()
export class TrueAccessStrategy implements AccessStrategy {
  /**
  * called method over matched access in the access expression
  * example: has("CanUpdateUserEmail")
  **/
  has(access: string): Observable<boolean> {
    // return this.authService.getUserAccesses()
    //    .some(userAccess => userAccess === access)
    return of(true);
  }
}

Import AccessModule

import { AccessGuard, AccessModule } from 'ngx-access';

// Better define in a configuration file [See below]
const accesses = {
  Home: {
    Main: {
      User: {
        Email: {
          Read: "CanReadUserEmail",
          Update: "CanUpdateUserEmail"
        },
        Password: {
          Update: "CanUpdateUserPassword"
        },
        Address: {
          Read: "CanReadUserAddress",
          Update: "CanUpdateUserAddress"
        }
      }
    }
  }
}

@NgModule({
   imports: [
      AccessModule.forRoot({
         accesses,
         strategy: { 
           provide: AccessStrategy, 
           useClass: TrueAccessStrategy 
        }
      })
   ]
   ...
})
export class AppModule { }

Usage in template

Simple usage

<app-user-form *ngxAccess="'Home.Main.User:Update'"></app-user-form>

<app-user-form *ngxAccess="'Home.Main.User:Update'; else unauthorized"></app-user-form>

<ng-template #unauthorized>
  You do not have enough permissions to update user info
</ng-template>

Multiple Access Paths

<app-home *ngxAccess="['Home.Main:Update', 'Home.Main:Read']"></app-home>

Router Links

<a href="" *ngxAccess="'Home.Main.User:Read'" [routerLink]="[...]" >
    View User
</a>
<a href="" *ngxAccess="'Home.Main.User:Update'" [routerLink]="[...]" >
    Edit User
</a>

Container Component

Repeat access path

<div>
  <input *ngxAccess="'Main.User.Email:Update'" [(ngModel)]="user.email"></span>
  <input *ngxAccess="'Main.User.Password:Update'" [(ngModel)]="user.password"></span>
  <app-address *ngxAccess="'Main.User.Address:Update'" [(ngModel)]="user.address"></app-address>
</div>

DRY version

<div ngxAccess="Main.User:Update">
  <input *ngxAccess="'$.Email'" [(ngModel)]="user.email"></span>
  <input *ngxAccess="'$.Password'" [(ngModel)]="user.password"></span>
  <app-address *ngxAccess="'$.Address'" [(ngModel)]="user.address"></app-address>
</div>

Explanation

$ is replaced by Main.User.

Update is appended to the resulting string.

Usage in code

Route guard

import { AccessGuard, AccessModule, AccessStrategy } from 'ngx-access';

@NgModule({
   imports: [
      AccessModule.forRoot({
         accesses: {
            User: {
               Profile: {
                  Read: 'CanAccessUserProfile'
               },
               Billing: {
                  Read: 'CanAccessUserBilling'
               }
            }
         },
         redirect: '/forbidden',
         strategy: { provide: AccessStrategy, useClass: MyAccessStrategy }
      }),
      RouterModule.forRoot([
         ...
         { path: 'forbidden', component: UnauthorizedComponent },
         {
            path: 'profile',
            component: ProfileComponent,
            canActivate: [AccessGuard],
            data: {
               accesses: ['User.Profile:Read', 'User.Profile:Update']
            }
         }
      ])
   ]
   ...
})
export class AppModule { }

Component

import { Component, OnInit } from '@angular/core';
import { AccessService } from 'ngx-access';

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

  submit() {
    let formData = {};
    if (this.accessService.hasAccess('User.Profile:Update')) {
      // Populate formData...
    }
    ...
  }

}  

Configuration

Access Expression

TypeDescriptionEvaluation
&"Access1 & Access2"true if user has Access1 AND Access2.
|```"Access1Access2"```
&/|```"Access1 & (Access2Access3)"```

Example

{
  "Home": {
    "Notifications": {
       "Read": "
          CanReadNotifications & 
          (CanReadUpdateNotifications | CanReadDeleteNotifications | CanReadCreateNotifications)
        "
    }
  }
}

Usage

<navbar>
  <a href="" *ngxAccess="'Home.Notifications:Read'" routerLink="/notifications" >
      Display Notifications
  </a>
</navbar>

Behavior

Link is displayed only if user has CanReadNotifications access AND at least one of CanReadUpdateNotifications OR CanReadDeleteNotifications OR CanReadCreateNotifications accesses.

External access configuration

1. Enable JSON imports in tsconfig.json

{
  ...
  "compilerOptions": {
    ...
    "declaration": false,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    ...
  }
}

2. Create ngx-access configuration

{
  "Home": {
    "User": {
      "Email": {
        "Read": "CanReadUserEmail",
        "Update": "CanUpdateUserEmail"
      },
      "Password": {
        "Update": "CanUpdateUserPassword"
      },
      "Address": {
        "Update": "CanUpdateUserAddress"
      }
    }
  }
}

3. Import ngx-access configuration

import accesses from './path/to/access.json';

@NgModule({
   imports: [
      AccessModule.forRoot({
         accesses,
         ...
      })
   ]
   ... 
})

Credits

LEP by NimitzDEV

License

MIT © Chihab Otmani

RunKit is a free, in-browser JavaScript dev environment for prototyping Node.js code, with every npm package installed. Sign up to share your code.
Sign Up for Free