From a5ee70090cc1c8543afa8420fea9442afc4f8d41 Mon Sep 17 00:00:00 2001 From: Karan Takalkar Date: Mon, 18 May 2020 13:23:26 +0530 Subject: [PATCH] feat: add has-permission structural directive (#722) Fixes: #721 --- src/app/accounting/accounting.component.html | 20 ++--- src/app/accounting/accounting.module.ts | 2 + src/app/directives/directives.module.ts | 20 +++++ .../has-permission.directive.spec.ts | 8 ++ .../has-permission.directive.ts | 76 +++++++++++++++++++ 5 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 src/app/directives/directives.module.ts create mode 100644 src/app/directives/has-permission/has-permission.directive.spec.ts create mode 100644 src/app/directives/has-permission/has-permission.directive.ts diff --git a/src/app/accounting/accounting.component.html b/src/app/accounting/accounting.component.html index 3c2083aa2..5d6e72530 100644 --- a/src/app/accounting/accounting.component.html +++ b/src/app/accounting/accounting.component.html @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@

These are predefined postings

- + @@ -24,7 +24,7 @@

Manual journal entry transactions recorded in a journal

- + @@ -32,7 +32,7 @@

Advanced search option for journal entries

- + @@ -40,7 +40,7 @@

List of Financial Activities and GL Account Mappings

- + @@ -56,7 +56,7 @@ - + @@ -64,7 +64,7 @@

List of accounts used by the organization

- + @@ -72,7 +72,7 @@

Journal entries made at the end of accounting period

- + @@ -80,7 +80,7 @@

List of all accounting rules

- + @@ -88,7 +88,7 @@

Accrues income, expenses and liabilities as on the provided date

- + diff --git a/src/app/accounting/accounting.module.ts b/src/app/accounting/accounting.module.ts index de8e5fd00..f4fd492f1 100644 --- a/src/app/accounting/accounting.module.ts +++ b/src/app/accounting/accounting.module.ts @@ -4,6 +4,7 @@ import { NgModule } from '@angular/core'; /** Custom Modules */ import { SharedModule } from '../shared/shared.module'; import { AccountingRoutingModule } from './accounting-routing.module'; +import { DirectivesModule } from '../directives/directives.module'; /** Custom Components */ import { AccountingComponent } from './accounting.component'; @@ -44,6 +45,7 @@ import { ViewProvisioningJournalEntriesComponent } from './provisioning-entries/ @NgModule({ imports: [ SharedModule, + DirectivesModule, AccountingRoutingModule ], declarations: [ diff --git a/src/app/directives/directives.module.ts b/src/app/directives/directives.module.ts new file mode 100644 index 000000000..390a004b1 --- /dev/null +++ b/src/app/directives/directives.module.ts @@ -0,0 +1,20 @@ +/** Angular Imports */ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +/** Custom Directives */ +import { HasPermissionDirective } from './has-permission/has-permission.directive'; + +/** + * Directives Module + * + * All custom directives should be declared and exported here. + */ +@NgModule({ + imports: [ + CommonModule + ], + declarations: [HasPermissionDirective], + exports: [HasPermissionDirective] +}) +export class DirectivesModule { } diff --git a/src/app/directives/has-permission/has-permission.directive.spec.ts b/src/app/directives/has-permission/has-permission.directive.spec.ts new file mode 100644 index 000000000..9d3ecbc98 --- /dev/null +++ b/src/app/directives/has-permission/has-permission.directive.spec.ts @@ -0,0 +1,8 @@ +import { HasPermissionDirective } from './has-permission.directive'; + +describe('HasPermissionDirective', () => { + it('should create an instance', () => { + const directive = new HasPermissionDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/directives/has-permission/has-permission.directive.ts b/src/app/directives/has-permission/has-permission.directive.ts new file mode 100644 index 000000000..63c5ec812 --- /dev/null +++ b/src/app/directives/has-permission/has-permission.directive.ts @@ -0,0 +1,76 @@ +/** Angular Imports */ +import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; + +/** Custom Services */ +import { AuthenticationService } from '../../core/authentication/authentication.service'; + +/** + * Has Permission Directive + */ +@Directive({ + selector: '[mifosxHasPermission]' +}) +export class HasPermissionDirective { + + /** User Permissions */ + private userPermissions: any[]; + + /** + * Extracts User Permissions from User Credentials + * @param {TemplateRef} templateRef Template Reference + * @param {ViewContainerRef} viewContainer View Container Reference + * @param {AuthenticationService} authenticationService AuthenticationService + */ + constructor(private templateRef: TemplateRef, + private viewContainer: ViewContainerRef, + private authenticationService: AuthenticationService) { + const savedCredentials = this.authenticationService.getCredentials(); + this.userPermissions = savedCredentials.permissions; + } + + /** + * Evaluates the condition to show template. + */ + @Input() + set mifosxHasPermission(permission: any) { + if (typeof permission !== 'string') { + throw new Error('hasPermission value must be a string'); + } + if (this.hasPermission(permission)) { + /** Shows Template */ + this.viewContainer.createEmbeddedView(this.templateRef); + } else { + /** Hides Template */ + this.viewContainer.clear(); + } + } + + /** + * Checks if user is permitted. + * @param {string} permission Permission + * @returns {true} + * -`ALL_FUNCTIONS`: user is a Super user. + * -`ALL_FUNCTIONS_READ`: user has all read permissions and passed permission is 'read' type. + * - User has special permission to access that feature. + * @returns {false} + * - Passed permission doesn't fall under either of above given permission grants. + * - No value was passed to the has permission directive. + */ + private hasPermission(permission: string) { + permission = permission.trim(); + if (this.userPermissions.includes('ALL_FUNCTIONS')) { + return true; + } else if (permission !== '') { + if (permission.substring(0, 5) === 'READ_' && this.userPermissions.includes('ALL_FUNCTIONS_READ')) { + return true; + } else if (this.userPermissions.includes(permission)) { + return true; + } else { + return false; + } + } else { + return false; + } + } + +}