add deny input to allow directive & update tests
This commit is contained in:
parent
bba66ea0d6
commit
a4b9c045cd
@ -28,8 +28,8 @@ export class AppComponent implements OnInit {
|
||||
|
||||
<div
|
||||
*allow="['ADMIN', 'GUEST']"
|
||||
(permissionsAuthorized)="yourCustomAuthorizedFunction()"
|
||||
(permissionsUnauthorized)="yourCustomAuthorizedFunction()"
|
||||
(authorized)="yourCustomAuthorizedFunction()"
|
||||
(unauthorized)="yourCustomAuthorizedFunction()"
|
||||
>
|
||||
<div>You can see this text congrats</div>
|
||||
</div>
|
||||
@ -38,11 +38,24 @@ export class AppComponent implements OnInit {
|
||||
<div>You can see this text congrats</div>
|
||||
</ng-template>
|
||||
|
||||
----------------------------------------------
|
||||
<div
|
||||
*deny="['ADMIN', 'GUEST']"
|
||||
(authorized)="yourCustomAuthorizedFunction()"
|
||||
(unauthorized)="yourCustomAuthorizedFunction()"
|
||||
>
|
||||
<div>You cant see this text congrats</div>
|
||||
</div>
|
||||
|
||||
<ng-template deny="ADMIN">
|
||||
<div>You cant see this text congrats</div>
|
||||
</ng-template>
|
||||
----------------------------------------------
|
||||
|
||||
<ng-template
|
||||
[allow]="['MANAGER']"
|
||||
[allowIf]="someObject.isEmpty"
|
||||
[allowDeny]="['GUEST']"
|
||||
[allowThen]="thenBlock"
|
||||
[allowElse]="elseBlock">
|
||||
</ng-template>
|
||||
@ -57,7 +70,7 @@ export class AppComponent implements OnInit {
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
<div *allow="['can-read']; if: !someObject.isEmpty; else: elseBlock; then: thenBlock">main</div>
|
||||
<div *allow="['can-read']; deny: ['GUEST']; if: !someObject.isEmpty; else: elseBlock; then: thenBlock">main</div>
|
||||
|
||||
<ng-template #elseBlock>
|
||||
<div>elseBlock</div>
|
||||
|
||||
@ -41,11 +41,13 @@ function getFixtureContent(): HTMLElement {
|
||||
|
||||
describe('Permission directive', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [allow]="'ADMIN'" (authorized)="isAuthorized()" (unauthorized)="isUnauthorized()">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
template: `
|
||||
<ng-template [allow]="'ADMIN'" (authorized)="isAuthorized()" (unauthorized)="isUnauthorized()">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -104,11 +106,13 @@ describe('Permission directive', () => {
|
||||
|
||||
describe('Permission directive with roles', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [allow]="'ADMIN'">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
template: `
|
||||
<ng-template [allow]="'ADMIN'">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
const awesomePermissions = 'AWESOME';
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
@ -166,11 +170,13 @@ describe('Permission directive with roles', () => {
|
||||
|
||||
describe('Permission directive with roles array', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [allow]="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
template: `
|
||||
<ng-template [allow]="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
const awesomePermission = 'AWESOME';
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
@ -221,11 +227,13 @@ describe('Permission directive with roles array', () => {
|
||||
|
||||
describe('Permission directive testing different selectors *allow', () => {
|
||||
@Component({
|
||||
template: ` <div *allow="['ADMIN']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
template: `
|
||||
<div *allow="['ADMIN']; deny: 'GUEST'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -249,15 +257,56 @@ describe('Permission directive testing different selectors *allow', () => {
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should hide the component when deny permission is added', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
permissionsService.add(GUEST);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should hide the component when deny role is added', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
tick();
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual('<div>123</div>');
|
||||
|
||||
rolesService.add(GUEST);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should show the component when deny permission is remove', fakeAsync(() => {
|
||||
permissionsService.add([ADMIN, GUEST]);
|
||||
tick();
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
|
||||
permissionsService.remove(GUEST);
|
||||
tick();
|
||||
|
||||
const 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 *allow="'ADMIN'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
template: `
|
||||
<div *allow="'ADMIN'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -284,11 +333,13 @@ describe('Permission directive angular testing different async functions in role
|
||||
|
||||
describe('Permission directive angular testing different async functions in roles via array', () => {
|
||||
@Component({
|
||||
template: ` <div *allow="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
template: `
|
||||
<div *allow="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -327,11 +378,13 @@ describe('Permission directive angular testing different async functions in role
|
||||
|
||||
describe('Permission directive with different async functions in permissions via array', () => {
|
||||
@Component({
|
||||
template: ` <div *allow="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
template: `
|
||||
<div *allow="['ADMIN', 'GUEST']">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -390,11 +443,13 @@ describe('Permission directive with different async functions in permissions via
|
||||
|
||||
describe('Permission directive testing different async functions in permissions via string', () => {
|
||||
@Component({
|
||||
template: ` <div *allow="'ADMIN'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
template: `
|
||||
<div *allow="'ADMIN'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -472,7 +527,8 @@ describe('Permissions directive with else block', () => {
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -513,7 +569,8 @@ describe('Permissions directive with then block', () => {
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -535,7 +592,8 @@ describe('Permissions directive with empty argument', () => {
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -554,7 +612,8 @@ describe('Permissions directive with an empty array', () => {
|
||||
</ng-template>
|
||||
`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -567,11 +626,13 @@ describe('Permissions directive with an empty array', () => {
|
||||
|
||||
describe('Permission directive with true if condition', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [allow]="'ADMIN'" [allowIf]="true">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
template: `
|
||||
<ng-template [allow]="'ADMIN'" [allowIf]="true">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -591,11 +652,13 @@ describe('Permission directive with true if condition', () => {
|
||||
|
||||
describe('Permission directive with true if condition', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [allow]="'ADMIN'" [allowIf]="false">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
template: `
|
||||
<ng-template [allow]="'ADMIN'" [allowIf]="false">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {}
|
||||
class TestComponent extends BaseTestComponent {
|
||||
}
|
||||
|
||||
beforeEach(() => configureTestBed(TestComponent));
|
||||
|
||||
@ -613,9 +676,10 @@ describe('Permission directive with true if condition', () => {
|
||||
|
||||
describe('Permission directive with true promise if condition', () => {
|
||||
@Component({
|
||||
template: ` <ng-template [allow]="'ADMIN'" [allowIf]="condition">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
template: `
|
||||
<ng-template [allow]="'ADMIN'" [allowIf]="condition">
|
||||
<div>123</div>
|
||||
</ng-template>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {
|
||||
condition = Promise.resolve(true);
|
||||
@ -639,9 +703,10 @@ describe('Permission directive with true promise if condition', () => {
|
||||
|
||||
describe('Permission directive with false promise if condition', () => {
|
||||
@Component({
|
||||
template: ` <div *allow="'ADMIN'; if: condition">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
template: `
|
||||
<div *allow="'ADMIN'; if: condition">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {
|
||||
condition = Promise.resolve(false);
|
||||
@ -718,9 +783,10 @@ describe('Permissions directive with then block and if condition', () => {
|
||||
|
||||
describe('Permission directive with false promise, if condition and change detection on push', () => {
|
||||
@Component({
|
||||
template: ` <div *allow="'ADMIN'; if: condition">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
template: `
|
||||
<div *allow="'ADMIN'; if: condition; deny: 'GUEST'">
|
||||
<div>123</div>
|
||||
</div>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
class TestComponent extends BaseTestComponent {
|
||||
@ -740,7 +806,7 @@ describe('Permission directive with false promise, if condition and change detec
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should show else block when permission is missing and condition is false', fakeAsync(() => {
|
||||
it('should show component when permission is added and condition is true', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
(fixture.componentInstance as TestComponent).condition.next(true);
|
||||
tick();
|
||||
@ -749,4 +815,19 @@ describe('Permission directive with false promise, if condition and change detec
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`<div>123</div>`);
|
||||
}));
|
||||
|
||||
it('should hide component when permission is present, condition is true and deny permission is present', fakeAsync(() => {
|
||||
permissionsService.add(ADMIN);
|
||||
(fixture.componentInstance as TestComponent).condition.next(true);
|
||||
tick();
|
||||
|
||||
const content = getFixtureContent();
|
||||
expect(content).toBeTruthy();
|
||||
expect(content.innerHTML).toEqual(`<div>123</div>`);
|
||||
|
||||
permissionsService.add(GUEST);
|
||||
tick();
|
||||
|
||||
expect(getFixtureContent()).toEqual(null);
|
||||
}));
|
||||
});
|
||||
|
||||
@ -18,6 +18,7 @@ export class IqserAllowDirective extends IqserPermissionsDirective implements On
|
||||
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `IqserPermissionsDirective`.
|
||||
*/
|
||||
static ngTemplateGuard_allow: 'binding';
|
||||
#deny?: string | List;
|
||||
|
||||
constructor(templateRef: TemplateRef<unknown>) {
|
||||
super(templateRef);
|
||||
@ -40,8 +41,35 @@ export class IqserAllowDirective extends IqserPermissionsDirective implements On
|
||||
this.setElseTemplateRef(template);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowDeny(value: string | List) {
|
||||
this.#deny = value;
|
||||
this._updateView.next();
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allowIf(value: boolean | Promise<boolean> | Observable<boolean>) {
|
||||
this.setIf(value);
|
||||
}
|
||||
|
||||
get #hasDenyPermission() {
|
||||
if (!this.#deny) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._permissionsService.hasSome(this.#deny) || this._rolesService.hasSome(this.#deny);
|
||||
}
|
||||
|
||||
protected override _validate() {
|
||||
const hasDenyPermission = this.#hasDenyPermission;
|
||||
if (!this._permissions) {
|
||||
return !hasDenyPermission;
|
||||
}
|
||||
|
||||
if (hasDenyPermission) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._permissionsService.has(this._permissions) || this._rolesService.has(this._permissions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,6 @@ export class IqserDenyDirective extends IqserPermissionsDirective implements OnD
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(this._permissionsService.hasAtLeastOne(this._permissions) || this._rolesService.hasAtLeastOne(this._permissions));
|
||||
return !(this._permissionsService.hasSome(this._permissions) || this._rolesService.hasSome(this._permissions));
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,25 +24,11 @@ export class IqserPermissionsService {
|
||||
has(permissions: List): boolean;
|
||||
has(permissions: string | List): boolean;
|
||||
has(permissions: string | List): 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.every(result => result);
|
||||
return this.#evaluate(permissions).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);
|
||||
hasSome(permissions: List | string): boolean {
|
||||
return this.#evaluate(permissions).some(result => result);
|
||||
}
|
||||
|
||||
load(permissions: IqserPermissions | List) {
|
||||
@ -69,8 +55,11 @@ export class IqserPermissionsService {
|
||||
}
|
||||
|
||||
has$(permission: string): Observable<boolean>;
|
||||
|
||||
has$(permissions: List): Observable<boolean>;
|
||||
|
||||
has$(permissions: string | List): Observable<boolean>;
|
||||
|
||||
has$(permissions: string | List) {
|
||||
const isEmpty = !permissions || permissions.length === 0;
|
||||
if (isEmpty) {
|
||||
@ -83,6 +72,16 @@ export class IqserPermissionsService {
|
||||
);
|
||||
}
|
||||
|
||||
#evaluate(permissions: List | string) {
|
||||
const isEmpty = !permissions || permissions.length === 0;
|
||||
if (isEmpty) {
|
||||
return [true];
|
||||
}
|
||||
|
||||
const all = this.#permissions$.value;
|
||||
return toArray(permissions).map(permission => all[permission]?.(permission, all) ?? false);
|
||||
}
|
||||
|
||||
#reduce(permissions: IqserPermissions | List, initialValue = {} as IqserPermissions) {
|
||||
if (isArray(permissions)) {
|
||||
return this.#permissions$.next(this.#reduceList(permissions, initialValue));
|
||||
|
||||
@ -41,25 +41,22 @@ export class IqserRolesService {
|
||||
}
|
||||
|
||||
has(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).every(result => result);
|
||||
const results = this.#evaluate(roles);
|
||||
return this.#checkPermissionsIfNeeded(results).every(result => result);
|
||||
}
|
||||
|
||||
hasAtLeastOne(roles: string | List): boolean {
|
||||
const isEmpty = !roles || roles.length === 0;
|
||||
hasSome(roles: string | List): boolean {
|
||||
const results = this.#evaluate(roles);
|
||||
return this.#checkPermissionsIfNeeded(results).some(result => result);
|
||||
}
|
||||
|
||||
#evaluate(roles: string | List) {
|
||||
const isEmpty = !roles || roles.length === 0;
|
||||
if (isEmpty) {
|
||||
return true;
|
||||
return [true];
|
||||
}
|
||||
|
||||
const validations = toArray(roles).map(role => this.#runValidation(role));
|
||||
return this.#checkPermissionsIfNeeded(validations).some(result => result);
|
||||
return toArray(roles).map(role => this.#runValidation(role));
|
||||
}
|
||||
|
||||
#checkPermissionsIfNeeded(results: List<string | boolean | List>) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user