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>
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);
}
}
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)
);
}
}
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();
}
}
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>
In the next article we can create some directive connected with our PermissinionService and use it without *ngIf condition c.d.n.
Top comments (0)