Add deny directive
This commit is contained in:
parent
05d6488bc8
commit
bba66ea0d6
@ -1,8 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component, Type } from '@angular/core';
|
||||
import { IqserPermissionsModule } from '.';
|
||||
import { IqserPermissionsModule } from '../../index';
|
||||
import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { IqserPermissionsService } from './services/permissions.service';
|
||||
import { IqserRolesService } from './services/roles.service';
|
||||
import { IqserPermissionsService } from '../../services/permissions.service';
|
||||
import { IqserRolesService } from '../../services/roles.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
class BaseTestComponent {
|
||||
@ -41,7 +41,7 @@ function getFixtureContent(): HTMLElement {
|
||||
|
||||
describe('Permission directive', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [allow]="'ADMIN'" (permissionsAuthorized)="isAuthorized()" (permissionsUnauthorized)="isUnauthorized()">
|
||||
template: ` <ng-template [allow]="'ADMIN'" (authorized)="isAuthorized()" (unauthorized)="isUnauthorized()">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
47
src/lib/permissions/directives/allow/allow.directive.ts
Normal file
47
src/lib/permissions/directives/allow/allow.directive.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Directive, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { List } from '../../../utils';
|
||||
import { assertTemplate, IqserPermissionsDirective } from '../permissions.directive';
|
||||
|
||||
@Directive({
|
||||
selector: '[allow]',
|
||||
})
|
||||
export class IqserAllowDirective extends IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
/**
|
||||
* Assert the correct type of the expression bound to the `allow` input within the template.
|
||||
*
|
||||
* The presence of this static field is a signal to the Ivy template type check compiler that
|
||||
* when the `IqserPermissionsDirective` structural directive renders its template, the type of the expression bound
|
||||
* to `allow` should be narrowed in some way.
|
||||
* For `allow`, the binding expression itself is used to
|
||||
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `IqserPermissionsDirective`.
|
||||
*/
|
||||
static ngTemplateGuard_allow: 'binding';
|
||||
|
||||
constructor(templateRef: TemplateRef<unknown>) {
|
||||
super(templateRef);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allow(value: string | List) {
|
||||
this.setPermissions(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowThen(template: TemplateRef<unknown>) {
|
||||
assertTemplate('allowThen', template);
|
||||
this.setThenTemplateRef(template);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowElse(template: TemplateRef<unknown>) {
|
||||
assertTemplate('allowElse', template);
|
||||
this.setElseTemplateRef(template);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowIf(value: boolean | Promise<boolean> | Observable<boolean>) {
|
||||
this.setIf(value);
|
||||
}
|
||||
}
|
||||
788
src/lib/permissions/directives/deny/deny.directive.spec.ts
Normal file
788
src/lib/permissions/directives/deny/deny.directive.spec.ts
Normal file
@ -0,0 +1,788 @@
|
||||
import { ChangeDetectionStrategy, Component, Type } from '@angular/core';
|
||||
import { IqserPermissionsModule } from '../../index';
|
||||
import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { IqserPermissionsService } from '../../services/permissions.service';
|
||||
import { IqserRolesService } from '../../services/roles.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
class BaseTestComponent {
|
||||
isAuthorized() {}
|
||||
|
||||
isUnauthorized() {}
|
||||
}
|
||||
|
||||
const ADMIN = 'ADMIN' as const;
|
||||
const GUEST = 'GUEST' as const;
|
||||
|
||||
let fixture: ComponentFixture<BaseTestComponent>;
|
||||
let permissionsService: IqserPermissionsService;
|
||||
let rolesService: IqserRolesService;
|
||||
let isAuthorizedSpy: jest.SpyInstance;
|
||||
let isUnauthorizedSpy: jest.SpyInstance;
|
||||
|
||||
function configureTestBed(component: Type<BaseTestComponent>) {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [component],
|
||||
imports: [IqserPermissionsModule.forRoot()],
|
||||
providers: [{ provide: ComponentFixtureAutoDetect, useValue: true }],
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(component);
|
||||
isAuthorizedSpy = jest.spyOn(fixture.componentInstance, 'isAuthorized');
|
||||
isUnauthorizedSpy = jest.spyOn(fixture.componentInstance, 'isUnauthorized');
|
||||
|
||||
permissionsService = TestBed.inject(IqserPermissionsService);
|
||||
rolesService = TestBed.inject(IqserRolesService);
|
||||
}
|
||||
|
||||
function getFixtureContent(): HTMLElement {
|
||||
return fixture.debugElement.nativeElement.querySelector('div');
|
||||
}
|
||||
|
||||
describe('Permission directive', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [deny]="'ADMIN'" (authorized)="isAuthorized()" (unauthorized)="isUnauthorized()">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should show the component', fakeAsync(() => {
|
||||
permissionsService.load([GUEST]);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
tick();
|
||||
}));
|
||||
|
||||
it('should not show the component', fakeAsync(() => {
|
||||
permissionsService.load([ADMIN, GUEST]);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
expect(isAuthorizedSpy).toHaveBeenCalledTimes(0);
|
||||
}));
|
||||
|
||||
it('should show component when permission removed', fakeAsync(() => {
|
||||
permissionsService.load([ADMIN, GUEST]);
|
||||
tick();
|
||||
|
||||
expect(isUnauthorizedSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
|
||||
permissionsService.remove(ADMIN);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
|
||||
expect(isAuthorizedSpy).toHaveBeenCalledTimes(1);
|
||||
}));
|
||||
|
||||
it('should hide component when permission added', fakeAsync(() => {
|
||||
permissionsService.load([GUEST]);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
expect(isUnauthorizedSpy).toHaveBeenCalledTimes(1);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive with roles', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [deny]="'ADMIN'">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
const awesomePermissions = 'AWESOME';
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when key of role is the same', fakeAsync(() => {
|
||||
rolesService.add({ ADMIN: [awesomePermissions] });
|
||||
permissionsService.add(awesomePermissions);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toBe(null);
|
||||
}));
|
||||
|
||||
it('should show the component when user deletes all roles', fakeAsync(() => {
|
||||
permissionsService.add(awesomePermissions);
|
||||
rolesService.add({ ADMIN: [awesomePermissions] });
|
||||
tick();
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
|
||||
rolesService.clear();
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
}));
|
||||
|
||||
it('should show the component when user deletes one role', fakeAsync(() => {
|
||||
permissionsService.add(awesomePermissions);
|
||||
rolesService.add({ ADMIN: [awesomePermissions] });
|
||||
tick();
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
|
||||
rolesService.remove(ADMIN);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive with roles array', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [deny]="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
const awesomePermission = 'AWESOME';
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when key of role is the same', fakeAsync(() => {
|
||||
permissionsService.add(awesomePermission);
|
||||
rolesService.add({ ADMIN: [awesomePermission] });
|
||||
rolesService.add({ GUEST: [awesomePermission] });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toBe(null);
|
||||
}));
|
||||
|
||||
it('should show the component when user deletes all roles', fakeAsync(() => {
|
||||
permissionsService.add(awesomePermission);
|
||||
rolesService.add({ ADMIN: [awesomePermission] });
|
||||
rolesService.add({ GUEST: [awesomePermission] });
|
||||
tick();
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
|
||||
rolesService.clear();
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
}));
|
||||
|
||||
it('should hide the component when user deletes one role', fakeAsync(() => {
|
||||
permissionsService.add(awesomePermission);
|
||||
rolesService.add({ ADMIN: [awesomePermission] });
|
||||
rolesService.add({ GUEST: [awesomePermission] });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
|
||||
rolesService.remove(ADMIN);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive testing different selectors *deny', () => {
|
||||
@Component({
|
||||
template: ` <div *deny="['ADMIN']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('Should hide the component when key of role is the same', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add('AWESOME');
|
||||
rolesService.add({ ADMIN: ['AWESOME'] });
|
||||
tick();
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should show the component when key of role is the same', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add({ GG: ['Awsesome'] });
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive angular testing different async functions in roles', () => {
|
||||
@Component({
|
||||
template: ` <div *deny="'ADMIN'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when promise returns truthy value', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add({ ADMIN: () => true });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should show the component when promise returns falsy value', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add({ ADMIN: () => false });
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive angular testing different async functions in roles via array', () => {
|
||||
@Component({
|
||||
template: ` <div *deny="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when returns truthy value', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add({ ADMIN: () => true });
|
||||
rolesService.add({ GUEST: () => true });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should hide the component when one returns falsy value', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add({ ADMIN: () => false });
|
||||
rolesService.add({ GUEST: () => true });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toBe(null);
|
||||
}));
|
||||
|
||||
it('should show the component when all return falsy value', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add({ ADMIN: () => false });
|
||||
rolesService.add({ GUEST: () => false });
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
}));
|
||||
|
||||
it('should hide the component when 1 passes second fails', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add({ ADMIN: () => false });
|
||||
rolesService.add({ GUEST: ['AWESOME'] });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive with different async functions in permissions via array', () => {
|
||||
@Component({
|
||||
template: ` <div *deny="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when returns truthy value', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({ ADMIN: () => true });
|
||||
permissionsService.add({ GUEST: () => true });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should show the component when returns falsy value', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({ ADMIN: () => false });
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
}));
|
||||
|
||||
it('should hide the component when returns truthy value', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({
|
||||
ADMIN: () => true,
|
||||
GUEST: () => true,
|
||||
});
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should not show the component when functions with name and store fulfils', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({
|
||||
ADMIN: (name, store) => {
|
||||
expect(name).toBeTruthy();
|
||||
expect(store[name!].name).toBeTruthy();
|
||||
return name === ADMIN;
|
||||
},
|
||||
});
|
||||
|
||||
permissionsService.add({ GUEST: () => false });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive testing different async functions in permissions via string', () => {
|
||||
@Component({
|
||||
template: ` <div *deny="'ADMIN'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should show the component when promise returns truthy value', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({ ADMIN: () => true });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should show the component when promise returns false value', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({ ADMIN: () => false });
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
}));
|
||||
|
||||
it('should show the component when only one of the promises fulfills', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({ ADMIN: () => false });
|
||||
permissionsService.add({ GUEST: () => true });
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
}));
|
||||
|
||||
it('should show the component when all promises fails', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({ ADMIN: () => false });
|
||||
permissionsService.add({ GUEST: () => false });
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
}));
|
||||
|
||||
it('should not show the component when functions with name and store fulfils', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add({
|
||||
ADMIN: (name, store) => {
|
||||
expect(name).toBeTruthy();
|
||||
expect(store[name!].name).toBeTruthy();
|
||||
return name === ADMIN;
|
||||
},
|
||||
});
|
||||
|
||||
permissionsService.add({ GUEST: () => false });
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permissions directive with else block', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<div *deny="['FAILED_BLOCK']; else elseBlock">main</div>
|
||||
|
||||
<ng-template #elseBlock>
|
||||
<div>elseBlockContent</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #thenBlock>
|
||||
<div>thenBlockContent</div>
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should show else block when permissions fail', fakeAsync(() => {
|
||||
let content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`main`);
|
||||
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
|
||||
content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`elseBlockContent`);
|
||||
}));
|
||||
|
||||
it('should show else block when permissions change', fakeAsync(() => {
|
||||
rolesService.add({ FAILED_BLOCK: () => true });
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('elseBlockContent');
|
||||
|
||||
rolesService.remove('FAILED_BLOCK');
|
||||
tick();
|
||||
|
||||
const content2 = getFixtureContent();
|
||||
expect(content2).toBeTruthy();
|
||||
expect(content2.innerHTML).toEqual(`main`);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permissions directive with then block', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<div *deny="['THEN_BLOCK']; else elseBlock; then: thenBlock">main</div>
|
||||
|
||||
<ng-template #elseBlock>
|
||||
<div>elseBlockContent</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #thenBlock>
|
||||
<div>thenBlockContent</div>
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should pass and show then block', fakeAsync(() => {
|
||||
rolesService.add({ THEN_BLOCK: () => false });
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`thenBlockContent`);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permissions directive with empty argument', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<ng-template [deny]="">
|
||||
<div>123</div>
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should succeed and show the component', () => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`123`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Permissions directive with an empty array', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<ng-template [deny]="[]">
|
||||
<div>123</div>
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should succeed and show the component', () => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`123`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Permission directive with true if condition', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [deny]="'ADMIN'" [denyIf]="true">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should show the component when permission is absent', () => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
});
|
||||
|
||||
it('should hide the component when permission is present', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive with true if condition', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [deny]="'ADMIN'" [denyIf]="false">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when permission is present', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should hide the component when permission is absent', fakeAsync(() => {
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive with true promise if condition', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [deny]="'ADMIN'" [denyIf]="condition">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {
|
||||
condition = Promise.resolve(true);
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should show the component when permission is absent', () => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('123');
|
||||
});
|
||||
|
||||
it('should hide the component when permission is present', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive with false promise if condition', () => {
|
||||
@Component({
|
||||
template: ` <div *deny="'ADMIN'; if: condition">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {
|
||||
condition = Promise.resolve(false);
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when permission is present', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should hide the component when permission is absent', fakeAsync(() => {
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permissions directive with then block and if condition', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<div *deny="['THEN_BLOCK']; if: condition; else: elseBlock; then: thenBlock">main</div>
|
||||
|
||||
<ng-template #elseBlock>
|
||||
<div>elseBlockContent</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #thenBlock>
|
||||
<div>thenBlockContent</div>
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {
|
||||
condition = new BehaviorSubject(true);
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should show then block when permission missing', fakeAsync(() => {
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`thenBlockContent`);
|
||||
}));
|
||||
|
||||
it('should show else block when permission added', fakeAsync(() => {
|
||||
permissionsService.add('THEN_BLOCK');
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`elseBlockContent`);
|
||||
}));
|
||||
|
||||
it('should show else block when permission added but condition is false', fakeAsync(() => {
|
||||
permissionsService.add('THEN_BLOCK');
|
||||
(fixture.componentInstance as TestComponent).condition.next(false);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`elseBlockContent`);
|
||||
}));
|
||||
|
||||
it('should show else block when permission is missing and condition is false', fakeAsync(() => {
|
||||
(fixture.componentInstance as TestComponent).condition.next(false);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`elseBlockContent`);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permission directive with false promise, if condition and change detection on push', () => {
|
||||
@Component({
|
||||
template: ` <div *deny="'ADMIN'; if: condition">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {
|
||||
condition = new BehaviorSubject(false);
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
it('should hide the component when permission is present', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should hide the component when permission is present and condition is true', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
(fixture.componentInstance as TestComponent).condition.next(true);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should hide the component when permission is absent', fakeAsync(() => {
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should show the component when permission is missing and condition is true', fakeAsync(() => {
|
||||
(fixture.componentInstance as TestComponent).condition.next(true);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`<div>123</div>`);
|
||||
}));
|
||||
});
|
||||
55
src/lib/permissions/directives/deny/deny.directive.ts
Normal file
55
src/lib/permissions/directives/deny/deny.directive.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { Directive, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { List } from '../../../utils';
|
||||
import { assertTemplate, IqserPermissionsDirective } from '../permissions.directive';
|
||||
|
||||
@Directive({
|
||||
selector: '[deny]',
|
||||
})
|
||||
export class IqserDenyDirective extends IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
/**
|
||||
* Assert the correct type of the expression bound to the `allow` input within the template.
|
||||
*
|
||||
* The presence of this static field is a signal to the Ivy template type check compiler that
|
||||
* when the `IqserPermissionsDirective` structural directive renders its template, the type of the expression bound
|
||||
* to `allow` should be narrowed in some way.
|
||||
* For `allow`, the binding expression itself is used to
|
||||
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `IqserPermissionsDirective`.
|
||||
*/
|
||||
static ngTemplateGuard_deny: 'binding';
|
||||
|
||||
constructor(templateRef: TemplateRef<unknown>) {
|
||||
super(templateRef);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set deny(value: string | List) {
|
||||
this.setPermissions(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set denyThen(template: TemplateRef<unknown>) {
|
||||
assertTemplate('denyThen', template);
|
||||
this.setThenTemplateRef(template);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set denyElse(template: TemplateRef<unknown>) {
|
||||
assertTemplate('denyElse', template);
|
||||
this.setElseTemplateRef(template);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set denyIf(value: boolean | Promise<boolean> | Observable<boolean>) {
|
||||
this.setIf(value);
|
||||
}
|
||||
|
||||
protected override _validate() {
|
||||
if (!this._permissions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(this._permissionsService.hasAtLeastOne(this._permissions) || this._rolesService.hasAtLeastOne(this._permissions));
|
||||
}
|
||||
}
|
||||
133
src/lib/permissions/directives/permissions.directive.ts
Normal file
133
src/lib/permissions/directives/permissions.directive.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Directive,
|
||||
EmbeddedViewRef,
|
||||
EventEmitter,
|
||||
inject,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
TemplateRef,
|
||||
ViewContainerRef,
|
||||
ɵstringify as stringify,
|
||||
} from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, combineLatest, merge, Observable, of, Subject, Subscription, switchMap } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
|
||||
import { IqserRolesService } from '../services/roles.service';
|
||||
import { IqserPermissionsService } from '../services/permissions.service';
|
||||
import { List } from '../../utils';
|
||||
import { isBoolean } from '../utils';
|
||||
|
||||
@Directive()
|
||||
export abstract class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
@Output() readonly authorized = new EventEmitter();
|
||||
@Output() readonly unauthorized = new EventEmitter();
|
||||
protected readonly _permissionsService = inject(IqserPermissionsService);
|
||||
protected readonly _rolesService = inject(IqserRolesService);
|
||||
protected readonly _viewContainer = inject(ViewContainerRef);
|
||||
protected readonly _changeDetector = inject(ChangeDetectorRef);
|
||||
protected _permissions?: string | List;
|
||||
protected _thenTemplateRef: TemplateRef<unknown>;
|
||||
protected _elseTemplateRef?: TemplateRef<unknown>;
|
||||
protected _thenViewRef: EmbeddedViewRef<unknown> | boolean = false;
|
||||
protected _elseViewRef: EmbeddedViewRef<unknown> | boolean = false;
|
||||
protected readonly _updateView = new Subject<void>();
|
||||
protected readonly _subscription = new Subscription();
|
||||
protected readonly _if = new BehaviorSubject<Promise<boolean> | Observable<boolean>>(of(true));
|
||||
|
||||
protected constructor(templateRef: TemplateRef<unknown>) {
|
||||
this._thenTemplateRef = templateRef;
|
||||
|
||||
const ifCondition$ = this._if.pipe(switchMap(condition => condition));
|
||||
|
||||
this._subscription = combineLatest([ifCondition$, this._updateView])
|
||||
.pipe(
|
||||
switchMap(([ifCondition]) => this._validateRolesAndPermissions().pipe(map(hasPermission => ifCondition && hasPermission))),
|
||||
tap(isAllowed => (isAllowed ? this._showThenBlock() : this._showElseBlock())),
|
||||
tap(() => this._changeDetector.markForCheck()),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
This assures that when the directive has an empty input (such as [allow]="") the view is updated
|
||||
*/
|
||||
ngOnInit() {
|
||||
this._updateView.next();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
|
||||
protected setPermissions(value: string | List) {
|
||||
this._permissions = value;
|
||||
this._updateView.next();
|
||||
}
|
||||
|
||||
protected setThenTemplateRef(template: TemplateRef<unknown>) {
|
||||
this._thenTemplateRef = template;
|
||||
this._thenViewRef = false;
|
||||
this._updateView.next();
|
||||
}
|
||||
|
||||
protected setElseTemplateRef(template: TemplateRef<unknown>) {
|
||||
this._elseTemplateRef = template;
|
||||
this._elseViewRef = false;
|
||||
this._updateView.next();
|
||||
}
|
||||
|
||||
protected setIf(value: boolean | Promise<boolean> | Observable<boolean>) {
|
||||
if (isBoolean(value)) {
|
||||
this._if.next(of(value));
|
||||
} else {
|
||||
this._if.next(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected _validateRolesAndPermissions() {
|
||||
return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(map(() => this._validate()));
|
||||
}
|
||||
|
||||
protected _validate() {
|
||||
if (!this._permissions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._permissionsService.has(this._permissions) || this._rolesService.has(this._permissions);
|
||||
}
|
||||
|
||||
protected _showElseBlock() {
|
||||
if (this._elseViewRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.unauthorized.emit();
|
||||
this._thenViewRef = false;
|
||||
this._elseViewRef = this._showTemplate(this._elseTemplateRef);
|
||||
}
|
||||
|
||||
protected _showThenBlock() {
|
||||
if (this._thenViewRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.authorized.emit();
|
||||
this._elseViewRef = false;
|
||||
this._thenViewRef = this._showTemplate(this._thenTemplateRef);
|
||||
}
|
||||
|
||||
protected _showTemplate(template?: TemplateRef<unknown>) {
|
||||
this._viewContainer.clear();
|
||||
return template ? this._viewContainer.createEmbeddedView(template) : true;
|
||||
}
|
||||
}
|
||||
|
||||
export function assertTemplate(property: string, templateRef: TemplateRef<unknown>): void {
|
||||
const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView);
|
||||
if (!isTemplateRefOrNull) {
|
||||
throw new Error(`${property} must be a TemplateRef, but received '${stringify(templateRef)}'.`);
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,6 @@ export * from './types';
|
||||
export * from './services/permissions-guard.service';
|
||||
export * from './services/permissions.service';
|
||||
export * from './services/roles.service';
|
||||
export * from './permissions.directive';
|
||||
export * from './directives/permissions.directive';
|
||||
export * from './permissions.module';
|
||||
export * from './utils';
|
||||
|
||||
@ -1,156 +0,0 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Directive,
|
||||
EmbeddedViewRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
TemplateRef,
|
||||
ViewContainerRef,
|
||||
ɵstringify as stringify,
|
||||
} from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, combineLatest, merge, Observable, of, Subject, Subscription, switchMap } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
|
||||
import { IqserRolesService } from './services/roles.service';
|
||||
import { IqserPermissionsService } from './services/permissions.service';
|
||||
import { List } from '../utils';
|
||||
import { isBoolean } from './utils';
|
||||
|
||||
@Directive({
|
||||
selector: '[allow]',
|
||||
})
|
||||
export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
/**
|
||||
* Assert the correct type of the expression bound to the `allow` input within the template.
|
||||
*
|
||||
* The presence of this static field is a signal to the Ivy template type check compiler that
|
||||
* when the `IqserPermissionsDirective` structural directive renders its template, the type of the expression bound
|
||||
* to `allow` should be narrowed in some way.
|
||||
* For `allow`, the binding expression itself is used to
|
||||
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `IqserPermissionsDirective`.
|
||||
*/
|
||||
static ngTemplateGuard_allow: 'binding';
|
||||
|
||||
@Output() readonly permissionsAuthorized = new EventEmitter();
|
||||
@Output() readonly permissionsUnauthorized = new EventEmitter();
|
||||
|
||||
#permissions?: string | List;
|
||||
#thenTemplateRef: TemplateRef<unknown>;
|
||||
#elseTemplateRef?: TemplateRef<unknown>;
|
||||
#thenViewRef: EmbeddedViewRef<unknown> | boolean = false;
|
||||
#elseViewRef: EmbeddedViewRef<unknown> | boolean = false;
|
||||
|
||||
readonly #updateView = new Subject<void>();
|
||||
readonly #subscription = new Subscription();
|
||||
readonly #if = new BehaviorSubject<Promise<boolean> | Observable<boolean>>(of(true));
|
||||
|
||||
constructor(
|
||||
private readonly _permissionsService: IqserPermissionsService,
|
||||
private readonly _rolesService: IqserRolesService,
|
||||
private readonly _viewContainer: ViewContainerRef,
|
||||
private readonly _changeDetector: ChangeDetectorRef,
|
||||
templateRef: TemplateRef<unknown>,
|
||||
) {
|
||||
this.#thenTemplateRef = templateRef;
|
||||
|
||||
const ifCondition$ = this.#if.pipe(switchMap(condition => condition));
|
||||
|
||||
this.#subscription = combineLatest([ifCondition$, this.#updateView])
|
||||
.pipe(
|
||||
switchMap(([ifCondition]) => this.#validateRolesAndPermissions().pipe(map(hasPermission => ifCondition && hasPermission))),
|
||||
tap(isAllowed => (isAllowed ? this.#showThenBlock() : this.#showElseBlock())),
|
||||
tap(() => this._changeDetector.markForCheck()),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allow(value: string | List) {
|
||||
this.#permissions = value;
|
||||
this.#updateView.next();
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowThen(template: TemplateRef<unknown>) {
|
||||
assertTemplate('allowThen', template);
|
||||
this.#thenTemplateRef = template;
|
||||
this.#thenViewRef = false;
|
||||
this.#updateView.next();
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowElse(template: TemplateRef<unknown>) {
|
||||
assertTemplate('allowElse', template);
|
||||
this.#elseTemplateRef = template;
|
||||
this.#elseViewRef = false;
|
||||
this.#updateView.next();
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowIf(value: boolean | Promise<boolean> | Observable<boolean>) {
|
||||
if (isBoolean(value)) {
|
||||
this.#if.next(of(value));
|
||||
} else {
|
||||
this.#if.next(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This assures that when the directive has an empty input (such as [allow]="") the view is updated
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.#updateView.next();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.#subscription.unsubscribe();
|
||||
}
|
||||
|
||||
#validateRolesAndPermissions() {
|
||||
return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(map(() => this.#validate()));
|
||||
}
|
||||
|
||||
#validate() {
|
||||
if (!this.#permissions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._permissionsService.has(this.#permissions) || this._rolesService.has(this.#permissions);
|
||||
}
|
||||
|
||||
#showElseBlock() {
|
||||
if (this.#elseViewRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.permissionsUnauthorized.emit();
|
||||
this.#thenViewRef = false;
|
||||
this.#elseViewRef = this.#showTemplate(this.#elseTemplateRef);
|
||||
}
|
||||
|
||||
#showThenBlock() {
|
||||
if (this.#thenViewRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.permissionsAuthorized.emit();
|
||||
this.#elseViewRef = false;
|
||||
this.#thenViewRef = this.#showTemplate(this.#thenTemplateRef);
|
||||
}
|
||||
|
||||
#showTemplate(template?: TemplateRef<unknown>) {
|
||||
this._viewContainer.clear();
|
||||
return template ? this._viewContainer.createEmbeddedView(template) : true;
|
||||
}
|
||||
}
|
||||
|
||||
function assertTemplate(property: string, templateRef: TemplateRef<unknown>): void {
|
||||
const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView);
|
||||
if (!isTemplateRefOrNull) {
|
||||
throw new Error(`${property} must be a TemplateRef, but received '${stringify(templateRef)}'.`);
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
import { NgModule, Optional } from '@angular/core';
|
||||
import { IqserPermissionsDirective } from './permissions.directive';
|
||||
import { IqserAllowDirective } from './directives/allow/allow.directive';
|
||||
import { IqserPermissionsService } from './services/permissions.service';
|
||||
import { IqserPermissionsGuard } from './services/permissions-guard.service';
|
||||
import { IqserRolesService } from './services/roles.service';
|
||||
import { IqserDenyDirective } from './directives/deny/deny.directive';
|
||||
|
||||
@NgModule({
|
||||
declarations: [IqserPermissionsDirective],
|
||||
exports: [IqserPermissionsDirective],
|
||||
declarations: [IqserAllowDirective, IqserDenyDirective],
|
||||
exports: [IqserAllowDirective, IqserDenyDirective],
|
||||
})
|
||||
export class IqserPermissionsModule {
|
||||
constructor(@Optional() permissionsService: IqserPermissionsService) {
|
||||
|
||||
@ -34,6 +34,17 @@ export class IqserPermissionsService {
|
||||
return results.every(result => result);
|
||||
}
|
||||
|
||||
hasAtLeastOne(permissions: List | string): boolean {
|
||||
const isEmpty = !permissions || permissions.length === 0;
|
||||
if (isEmpty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const all = this.#permissions$.value;
|
||||
const results = toArray(permissions).map(permission => all[permission]?.(permission, all) ?? false);
|
||||
return results.some(result => result);
|
||||
}
|
||||
|
||||
load(permissions: IqserPermissions | List) {
|
||||
return this.#reduce(permissions);
|
||||
}
|
||||
|
||||
@ -51,6 +51,17 @@ export class IqserRolesService {
|
||||
return this.#checkPermissionsIfNeeded(validations).every(result => result);
|
||||
}
|
||||
|
||||
hasAtLeastOne(roles: string | List): boolean {
|
||||
const isEmpty = !roles || roles.length === 0;
|
||||
|
||||
if (isEmpty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const validations = toArray(roles).map(role => this.#runValidation(role));
|
||||
return this.#checkPermissionsIfNeeded(validations).some(result => result);
|
||||
}
|
||||
|
||||
#checkPermissionsIfNeeded(results: List<string | boolean | List>) {
|
||||
return results.map(result => (isBoolean(result) ? result : this._permissionsService.has(result)));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user