- No more endless if statements in your components
- Define only the accesses you really need
- Do not add useless accesses for your Route/Layout components
- Define your access control as logical expressions
- Centralize your access control configuration
- Document your application security
- Define your own strategy to verify if the user has a given access
{
"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 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.
npm install --save ngx-access
import { AccessStrategy } from 'ngx-access';
@Injectable()
export class TrueAccessStrategy implements AccessStrategy {
/**
* called method over each matched access
* example: has("CanUpdateUserEmail")
**/
has(access: string): Observable<boolean> {
// return this.authService.getUserAccesses().some(userAccess => userAccess === access)
return of(true);
}
}
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 { }
<app-user-form *ngxAccess="'Home.Main.User:Update'" [user]="user"></app-user-form>
<app-user-form *ngxAccess="'Home.Main.User:Update'; else unauthorized" [user]="user"></app-user-form>
<ng-template #unauthorized>
You do not have enough permissions to update user info
</ng-template>
<app-home *ngxAccess="['Home.Main:Update', 'Home.Main:Read']"></app-home>
<a href="" [routerLink]="['view', user.id]" *ngxAccess="'Home.Main.User:Read'">
View User
</a>
<a href="" [routerLink]="['edit', user.id]" *ngxAccess="'Home.Main.User:Update'">
Edit User
</a>
<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>
<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>
$
is replaced by Main.User
.
Update
is appended to the resulting string.
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: {
expression: 'User.Profile:Read'
}
}
])
]
...
})
export class AppModule { }
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...
}
...
}
}
Type | Description | Evaluation |
---|---|---|
Single | "Access1" |
true if user has Access1 |
Array | ["Access1", "Access2"] |
true if user has Access1 OR Access2 |
And | "Access1 AND Access2" |
true if user has Access1 AND Access2. |
Or [WIP] | "Access1 OR Access2" |
true if user has Access1 OR Access2 |
And/Or [WIP] | "Access1 AND (Access2 OR Access3)" |
true if user has Access1 AND (Access2 OR Access3) |
{
"Home": {
"Notifications": {
"Read": "
CanReadNotifications AND
(CanReadUpdateNotifications OR CanReadDeleteNotifications OR CanReadCreateNotifications)
"
}
}
}
<navbar>
<a href="" routerLink="/notifications" *ngxAccess="'Home.Notifications:Read'">
Display Notifications
</a>
</navbar>
Link is displayed only if user has CanReadNotifications
access AND at least one of CanReadUpdateNotifications OR CanReadDeleteNotifications OR CanReadCreateNotifications
accesses.
{
...
"compilerOptions": {
...
"declaration": false,
"resolveJsonModule": true,
"esModuleInterop": true,
...
}
}
{
"Home": {
"User": {
"Email": {
"Read": "CanReadUserEmail",
"Update": "CanUpdateUserEmail"
},
"Password": {
"Update": "CanUpdateUserPassword"
},
"Address": {
"Update": "CanUpdateUserAddress"
}
}
}
}
import accesses from './path/to/access.json';
@NgModule({
imports: [
AccessModule.forRoot({
accesses,
...
})
]
...
})
MIT © Chihab Otmani