update services
This commit is contained in:
parent
9275701f93
commit
7d4e0a851a
1
src/global.d.ts
vendored
Normal file
1
src/global.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
import 'jest-extended';
|
||||
@ -1,6 +1,4 @@
|
||||
export * from './models/permission.model';
|
||||
export * from './models/permissions-router-data.model';
|
||||
export * from './models/role.model';
|
||||
export * from './types';
|
||||
export * from './services/permissions-guard.service';
|
||||
export * from './services/permissions.service';
|
||||
export * from './services/roles.service';
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
import { ValidationFn } from './permissions-router-data.model';
|
||||
|
||||
export interface IqserPermission {
|
||||
name: string;
|
||||
validationFn?: ValidationFn<IqserPermission>;
|
||||
}
|
||||
|
||||
export interface IqserPermissionsObject {
|
||||
[name: string]: IqserPermission;
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { ValidationFn } from './permissions-router-data.model';
|
||||
|
||||
export interface IqserRole {
|
||||
name: string;
|
||||
validationFn: ValidationFn<IqserRole> | string[];
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -14,9 +14,9 @@ import {
|
||||
import { merge, Subject, Subscription, switchMap } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
import { notEmpty } from './utils';
|
||||
import { IqserRolesService } from './services/roles.service';
|
||||
import { IqserPermissionsService } from './services/permissions.service';
|
||||
import { List } from '../utils';
|
||||
|
||||
type NgTemplate = TemplateRef<unknown>;
|
||||
|
||||
@ -38,11 +38,11 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
@Output() readonly permissionsAuthorized = new EventEmitter();
|
||||
@Output() readonly permissionsUnauthorized = new EventEmitter();
|
||||
|
||||
#permissions?: string | string[];
|
||||
#permissions?: string | List;
|
||||
#thenTemplateRef: TemplateRef<unknown>;
|
||||
#elseTemplateRef?: TemplateRef<unknown>;
|
||||
#thenViewRef?: EmbeddedViewRef<unknown>;
|
||||
#elseViewRef?: EmbeddedViewRef<unknown>;
|
||||
#thenViewRef: EmbeddedViewRef<unknown> | boolean = false;
|
||||
#elseViewRef: EmbeddedViewRef<unknown> | boolean = false;
|
||||
|
||||
readonly #updateView = new Subject<void>();
|
||||
readonly #subscription = new Subscription();
|
||||
@ -58,7 +58,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set allow(value: string | string[]) {
|
||||
set allow(value: string | List) {
|
||||
this.#permissions = value;
|
||||
this.#updateView.next();
|
||||
}
|
||||
@ -67,7 +67,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
set allowThen(template: NgTemplate) {
|
||||
assertTemplate('allowThen', template);
|
||||
this.#thenTemplateRef = template;
|
||||
this.#thenViewRef = undefined;
|
||||
this.#thenViewRef = false;
|
||||
this.#updateView.next();
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
set allowElse(template: NgTemplate) {
|
||||
assertTemplate('allowElse', template);
|
||||
this.#elseTemplateRef = template;
|
||||
this.#elseViewRef = undefined;
|
||||
this.#elseViewRef = false;
|
||||
this.#updateView.next();
|
||||
}
|
||||
|
||||
@ -91,21 +91,17 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
#waitForRolesAndPermissions() {
|
||||
return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(
|
||||
tap(() => (notEmpty(this.#permissions) ? this.#validate() : this.#showThenBlock())),
|
||||
);
|
||||
return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(tap(() => this.#validate()));
|
||||
}
|
||||
|
||||
#validate(): void {
|
||||
Promise.all([this._permissionsService.has(this.#permissions), this._rolesService.has(this.#permissions)])
|
||||
.then(([hasPermissions, hasRoles]) => {
|
||||
if (hasPermissions || hasRoles) {
|
||||
return this.#showThenBlock();
|
||||
}
|
||||
if (!this.#permissions) {
|
||||
return this.#showThenBlock();
|
||||
}
|
||||
|
||||
return this.#showElseBlock();
|
||||
})
|
||||
.catch(() => this.#showElseBlock());
|
||||
const promises = [this._permissionsService.has(this.#permissions), this._rolesService.has(this.#permissions)];
|
||||
const result = Promise.all(promises).then(([hasPermission, hasRole]) => hasPermission || hasRole);
|
||||
result.then(isAllowed => (isAllowed ? this.#showThenBlock() : this.#showElseBlock())).catch(() => this.#showElseBlock());
|
||||
}
|
||||
|
||||
#showElseBlock() {
|
||||
@ -114,8 +110,8 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
this.permissionsUnauthorized.emit();
|
||||
this.#thenViewRef = undefined;
|
||||
this.#elseViewRef = this.#showTemplate(this.#elseTemplateRef);
|
||||
this.#thenViewRef = false;
|
||||
this.#elseViewRef = this.#showTemplate(this.#elseTemplateRef) ?? true;
|
||||
}
|
||||
|
||||
#showThenBlock() {
|
||||
@ -124,18 +120,13 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
this.permissionsAuthorized.emit();
|
||||
this.#elseViewRef = undefined;
|
||||
this.#thenViewRef = this.#showTemplate(this.#thenTemplateRef);
|
||||
this.#elseViewRef = false;
|
||||
this.#thenViewRef = this.#showTemplate(this.#thenTemplateRef) ?? true;
|
||||
}
|
||||
|
||||
#showTemplate(template?: NgTemplate) {
|
||||
this._viewContainer.clear();
|
||||
|
||||
if (!template) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this._viewContainer.createEmbeddedView(template);
|
||||
return template ? this._viewContainer.createEmbeddedView(template) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ describe('Permissions guard', () => {
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return false when only doesnt match', async () => {
|
||||
it('should return false when allow doesnt match', async () => {
|
||||
testRoute = {
|
||||
data: {
|
||||
permissions: {
|
||||
@ -440,11 +440,11 @@ describe('Role guard with redirectTo as function', () => {
|
||||
roleService = TestBed.inject(IqserRolesService);
|
||||
permissionsService.add('canReadAgenda');
|
||||
permissionsService.add('AWESOME');
|
||||
roleService.add('ADMIN', ['AWESOME', 'canReadAgenda']);
|
||||
roleService.add({ ADMIN: ['AWESOME', 'canReadAgenda'] });
|
||||
});
|
||||
|
||||
it('should dynamically pass if one satisfies', async () => {
|
||||
roleService.add('RUN', ['BLABLA', 'BLABLA2']);
|
||||
roleService.add({ RUN: ['BLABLA', 'BLABLA2'] });
|
||||
|
||||
testRoute = {
|
||||
data: {
|
||||
|
||||
@ -15,18 +15,20 @@ import { first, mergeMap, tap } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
DEFAULT_REDIRECT_KEY,
|
||||
IqserPermissionsRouterData,
|
||||
IqserActivatedRouteSnapshot,
|
||||
IqserRoute,
|
||||
NavigationCommandsFn,
|
||||
NavigationExtrasFn,
|
||||
RedirectTo,
|
||||
RedirectToFn,
|
||||
} from '../models/permissions-router-data.model';
|
||||
} from '../types';
|
||||
import { IqserPermissionsService } from './permissions.service';
|
||||
import { IqserRolesService } from './roles.service';
|
||||
import { isFunction, isRedirectWithParameters, isString, transformPermission } from '../utils';
|
||||
import { isArray, isFunction, isRedirectWithParameters, isString, transformPermission } from '../utils';
|
||||
import { List } from '../../utils';
|
||||
|
||||
export interface IqserPermissionsData {
|
||||
allow: string | string[];
|
||||
allow: string | List;
|
||||
redirectTo?: RedirectTo | RedirectToFn;
|
||||
}
|
||||
|
||||
@ -38,15 +40,15 @@ export class IqserPermissionsGuard implements CanActivate, CanLoad, CanActivateC
|
||||
private readonly _router: Router,
|
||||
) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
canActivate(route: IqserActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
return this.#checkPermissions(route, state);
|
||||
}
|
||||
|
||||
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
canActivateChild(childRoute: IqserActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
return this.#checkPermissions(childRoute, state);
|
||||
}
|
||||
|
||||
canLoad(route: Route) {
|
||||
canLoad(route: IqserRoute) {
|
||||
return this.#checkPermissions(route);
|
||||
}
|
||||
|
||||
@ -58,15 +60,15 @@ export class IqserPermissionsGuard implements CanActivate, CanLoad, CanActivateC
|
||||
return this.#validatePermissions(permissions, route, state);
|
||||
}
|
||||
|
||||
#checkPermissions(route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) {
|
||||
const routePermissions = route?.data ? (route.data['permissions'] as IqserPermissionsRouterData) : undefined;
|
||||
#checkPermissions(route: IqserActivatedRouteSnapshot | IqserRoute, state?: RouterStateSnapshot) {
|
||||
const routePermissions = route.data?.permissions;
|
||||
if (!routePermissions) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
const permissions = transformPermission(routePermissions, route, state);
|
||||
|
||||
if (permissions?.allow?.length > 0) {
|
||||
if (permissions.allow?.length > 0) {
|
||||
return this.#validate(permissions, route, state);
|
||||
}
|
||||
|
||||
@ -126,7 +128,6 @@ export class IqserPermissionsGuard implements CanActivate, CanLoad, CanActivateC
|
||||
|
||||
if (failed) {
|
||||
failedPermission = permission;
|
||||
console.log(`Permission ${permission} is not allowed`);
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -172,7 +173,7 @@ export class IqserPermissionsGuard implements CanActivate, CanLoad, CanActivateC
|
||||
return this.#redirectToAnotherRoute(failedPermissionRedirectTo, route, failedPermission, state);
|
||||
}
|
||||
|
||||
if (isFunction<RedirectToFn>(permissions.redirectTo) || isString(permissions.redirectTo) || Array.isArray(permissions.redirectTo)) {
|
||||
if (isFunction<RedirectToFn>(permissions.redirectTo) || isString(permissions.redirectTo) || isArray(permissions.redirectTo)) {
|
||||
return this.#redirectToAnotherRoute(permissions.redirectTo, route, failedPermission, state);
|
||||
}
|
||||
|
||||
@ -195,7 +196,7 @@ export class IqserPermissionsGuard implements CanActivate, CanLoad, CanActivateC
|
||||
!isFunction<RedirectToFn>(redirectTo) &&
|
||||
!isString(redirectTo) &&
|
||||
!isRedirectWithParameters(redirectTo) &&
|
||||
!Array.isArray(redirectTo)
|
||||
!isArray(redirectTo)
|
||||
) {
|
||||
return redirectTo[failedPermission];
|
||||
}
|
||||
|
||||
@ -8,6 +8,10 @@ const GUEST = 'GUEST' as const;
|
||||
describe('Permissions Service', () => {
|
||||
let permissionsService: IqserPermissionsService;
|
||||
|
||||
function getPermissionsLength() {
|
||||
return Object.keys(permissionsService.get()).length;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [IqserPermissionsModule.forRoot()],
|
||||
@ -35,32 +39,29 @@ describe('Permissions Service', () => {
|
||||
});
|
||||
|
||||
it('should remove all permissions from permissions object', () => {
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(0);
|
||||
expect(getPermissionsLength()).toEqual(0);
|
||||
|
||||
permissionsService.add(ADMIN);
|
||||
permissionsService.add(GUEST);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(2);
|
||||
expect(getPermissionsLength()).toEqual(2);
|
||||
|
||||
permissionsService.clear();
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(0);
|
||||
expect(getPermissionsLength()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should add multiple permissions', () => {
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(0);
|
||||
expect(getPermissionsLength()).toEqual(0);
|
||||
|
||||
permissionsService.add([ADMIN, GUEST]);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(2);
|
||||
expect(permissionsService.get()).toEqual({
|
||||
ADMIN: { name: 'ADMIN' },
|
||||
GUEST: { name: 'GUEST' },
|
||||
});
|
||||
expect(getPermissionsLength()).toEqual(2);
|
||||
expect(Object.keys(permissionsService.get())).toEqual([ADMIN, GUEST]);
|
||||
});
|
||||
|
||||
it('should return true when permission name is present in permissions object', async () => {
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(0);
|
||||
expect(getPermissionsLength()).toEqual(0);
|
||||
|
||||
permissionsService.add([ADMIN, GUEST]);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(2);
|
||||
expect(getPermissionsLength()).toEqual(2);
|
||||
|
||||
let result = await permissionsService.has('ADMIN');
|
||||
expect(result).toEqual(true);
|
||||
@ -72,74 +73,75 @@ describe('Permissions Service', () => {
|
||||
expect(result).toEqual(true);
|
||||
|
||||
result = await permissionsService.has(['ADMIN', 'IRIISISTABLE']);
|
||||
expect(result).toEqual(true);
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return true when permission function return true', async () => {
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(0);
|
||||
expect(getPermissionsLength()).toEqual(0);
|
||||
|
||||
permissionsService.add(ADMIN, () => true);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(1);
|
||||
permissionsService.add({ ADMIN: () => true });
|
||||
expect(getPermissionsLength()).toEqual(1);
|
||||
|
||||
let result = await permissionsService.has('ADMIN');
|
||||
expect(result).toEqual(true);
|
||||
|
||||
permissionsService.add(GUEST, () => false);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(2);
|
||||
permissionsService.add({ GUEST: () => false });
|
||||
expect(getPermissionsLength()).toEqual(2);
|
||||
|
||||
result = await permissionsService.has('GUEST');
|
||||
expect(result).toEqual(false);
|
||||
|
||||
permissionsService.add('TEST1', () => Promise.resolve(true));
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(3);
|
||||
permissionsService.add({ TEST1: () => Promise.resolve(true) });
|
||||
expect(getPermissionsLength()).toEqual(3);
|
||||
|
||||
result = await permissionsService.has('TEST1');
|
||||
expect(result).toEqual(true);
|
||||
|
||||
permissionsService.add('TEST2', () => Promise.resolve(false));
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(4);
|
||||
permissionsService.add({ TEST2: () => Promise.resolve(false) });
|
||||
expect(getPermissionsLength()).toEqual(4);
|
||||
|
||||
result = await permissionsService.has('TEST2');
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
// TODO: permissions array with function should not be allowed
|
||||
it('should return true when permissions array function return true', async () => {
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(0);
|
||||
expect(getPermissionsLength()).toEqual(0);
|
||||
|
||||
permissionsService.add([ADMIN], () => true);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(1);
|
||||
permissionsService.add({ ADMIN: () => true });
|
||||
expect(getPermissionsLength()).toEqual(1);
|
||||
|
||||
let result = await permissionsService.has('ADMIN');
|
||||
expect(result).toEqual(true);
|
||||
|
||||
permissionsService.add([GUEST], () => false);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(2);
|
||||
permissionsService.add({ GUEST: () => false });
|
||||
expect(getPermissionsLength()).toEqual(2);
|
||||
|
||||
result = await permissionsService.has('GUEST');
|
||||
expect(result).toEqual(false);
|
||||
|
||||
permissionsService.add(['TEST1'], () => Promise.resolve(true));
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(3);
|
||||
permissionsService.add({ TEST1: () => Promise.resolve(true) });
|
||||
expect(getPermissionsLength()).toEqual(3);
|
||||
|
||||
result = await permissionsService.has('TEST1');
|
||||
expect(result).toEqual(true);
|
||||
|
||||
permissionsService.add(['TEST9'], () => Promise.resolve(false));
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(4);
|
||||
permissionsService.add({ TEST9: () => Promise.resolve(false) });
|
||||
expect(getPermissionsLength()).toEqual(4);
|
||||
|
||||
result = await permissionsService.has(['TEST9']);
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('should call validationFn with permission name and store', async () => {
|
||||
permissionsService.add('TEST11', (name, store) => {
|
||||
expect(name).toEqual('TEST11');
|
||||
expect(store['TEST11']).toBeTruthy();
|
||||
return Promise.resolve(true);
|
||||
permissionsService.add({
|
||||
TEST11: (name, store) => {
|
||||
expect(name).toEqual('TEST11');
|
||||
expect(store['TEST11']).toBeTruthy();
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
});
|
||||
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(1);
|
||||
expect(getPermissionsLength()).toEqual(1);
|
||||
|
||||
const result = await permissionsService.has(['TEST11']);
|
||||
expect(result).toEqual(true);
|
||||
|
||||
@ -1,103 +1,79 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, firstValueFrom, from, Observable, of } from 'rxjs';
|
||||
import { catchError, first, map, mergeAll, switchMap } from 'rxjs/operators';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
|
||||
import { isFunction, toArray } from '../utils';
|
||||
import { ValidationFn } from '../models/permissions-router-data.model';
|
||||
import { IqserPermission, IqserPermissionsObject } from '../models/permission.model';
|
||||
import { isArray, isString, toArray } from '../utils';
|
||||
import { IqserPermissions, PermissionValidationFn } from '../types';
|
||||
import { List } from '../../utils';
|
||||
|
||||
@Injectable()
|
||||
export class IqserPermissionsService {
|
||||
readonly permissions$: Observable<IqserPermissionsObject>;
|
||||
readonly #permissions$ = new BehaviorSubject<IqserPermissionsObject>({});
|
||||
readonly permissions$: Observable<IqserPermissions>;
|
||||
readonly #permissions$ = new BehaviorSubject<IqserPermissions>({});
|
||||
|
||||
constructor() {
|
||||
this.permissions$ = this.#permissions$.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all permissions from permissions source
|
||||
*/
|
||||
clear(): void {
|
||||
this.#permissions$.next({});
|
||||
}
|
||||
|
||||
has(permission?: string | string[]): Promise<boolean> {
|
||||
if (!permission || (Array.isArray(permission) && permission.length === 0)) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
permission = toArray(permission);
|
||||
return this.#hasArray(permission);
|
||||
has(permission: string): Promise<boolean>;
|
||||
has(permissions: List): Promise<boolean>;
|
||||
has(permissions: string | List): Promise<boolean>;
|
||||
has(value: string | List): Promise<boolean> {
|
||||
const isEmpty = !value || value.length === 0;
|
||||
return isEmpty ? Promise.resolve(true) : this.#has(toArray(value));
|
||||
}
|
||||
|
||||
load(permissions: string[], validationFn?: ValidationFn<IqserPermission>): void {
|
||||
const newPermissions = permissions.reduce((source, name) => this.#reduce(source, name, validationFn), {});
|
||||
this.#permissions$.next(newPermissions);
|
||||
load(permissions: IqserPermissions | List) {
|
||||
return this.#reduce(permissions);
|
||||
}
|
||||
|
||||
add(permission: string | string[], validationFn?: ValidationFn<IqserPermission>) {
|
||||
const permissions = toArray(permission).reduce(
|
||||
(source, name) => this.#reduce(source, name, validationFn),
|
||||
this.#permissions$.value,
|
||||
);
|
||||
|
||||
return this.#permissions$.next(permissions);
|
||||
add(permissions: string | List | IqserPermissions) {
|
||||
const _permissions = isString(permissions) ? { [permissions]: () => true } : permissions;
|
||||
return this.#reduce(_permissions, this.#permissions$.value);
|
||||
}
|
||||
|
||||
remove(name: string): void {
|
||||
remove(permission: string) {
|
||||
const permissions = { ...this.#permissions$.value };
|
||||
delete permissions[name];
|
||||
delete permissions[permission];
|
||||
this.#permissions$.next(permissions);
|
||||
}
|
||||
|
||||
get(): IqserPermissionsObject;
|
||||
get(name: string): IqserPermission | undefined;
|
||||
get(name?: string): IqserPermission | IqserPermissionsObject | undefined {
|
||||
return name ? this.#permissions$.value[name] : this.#permissions$.value;
|
||||
get(): IqserPermissions;
|
||||
get(permission: string): PermissionValidationFn | undefined;
|
||||
get(permission?: string): IqserPermissions | PermissionValidationFn | undefined {
|
||||
return permission ? this.#permissions$.value[permission] : this.#permissions$.value;
|
||||
}
|
||||
|
||||
#reduce(source: IqserPermissionsObject, name: string, validationFn?: ValidationFn<IqserPermission>): IqserPermissionsObject {
|
||||
if (!!validationFn && isFunction(validationFn)) {
|
||||
return { ...source, [name]: { name, validationFn } };
|
||||
#reduce(permissions: IqserPermissions | List, initialValue = {} as IqserPermissions) {
|
||||
if (isArray(permissions)) {
|
||||
return this.#permissions$.next(this.#reduceList(permissions, initialValue));
|
||||
}
|
||||
|
||||
return { ...source, [name]: { name } };
|
||||
return this.#permissions$.next(this.#reduceObject(permissions, initialValue));
|
||||
}
|
||||
|
||||
#hasArray(permissions: string[]): Promise<boolean> {
|
||||
const promises = permissions.map(key => {
|
||||
if (this.#hasValidationFn(key)) {
|
||||
const validationFunction = this.#permissions$.value[key].validationFn;
|
||||
if (!validationFunction) {
|
||||
return of(false);
|
||||
}
|
||||
const immutableValue = { ...this.#permissions$.value };
|
||||
#reduceObject(permissions: IqserPermissions, initialValue: IqserPermissions = {}) {
|
||||
return Object.entries(permissions).reduce((acc, [permission, validationFn]) => {
|
||||
return { ...acc, [permission]: validationFn };
|
||||
}, initialValue);
|
||||
}
|
||||
|
||||
return of(null).pipe(
|
||||
switchMap(async () => validationFunction(key, immutableValue)),
|
||||
catchError(() => of(false)),
|
||||
);
|
||||
}
|
||||
#reduceList(permissions: List, initialValue: IqserPermissions = {}): IqserPermissions {
|
||||
return permissions.reduce((acc, permission) => {
|
||||
return { ...acc, [permission]: () => true };
|
||||
}, initialValue);
|
||||
}
|
||||
|
||||
return of(!!this.#permissions$.value[key]);
|
||||
#has(permissions: List): Promise<boolean> {
|
||||
const promises = permissions.map(permission => {
|
||||
const validationFn = this.#permissions$.value[permission];
|
||||
return validationFn?.(permission, this.#permissions$.value) ?? false;
|
||||
});
|
||||
|
||||
const res = from(promises).pipe(
|
||||
mergeAll(),
|
||||
first(data => data !== false, false),
|
||||
map(data => data !== false),
|
||||
);
|
||||
|
||||
return firstValueFrom(res);
|
||||
}
|
||||
|
||||
#hasValidationFn(key: string): boolean {
|
||||
return (
|
||||
!!this.#permissions$.value[key] &&
|
||||
!!this.#permissions$.value[key].validationFn &&
|
||||
isFunction(this.#permissions$.value[key].validationFn)
|
||||
);
|
||||
return Promise.all(promises).then(results => results.every(result => result));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,23 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { ValidationFn } from '../models/permissions-router-data.model';
|
||||
import { RoleValidationFn } from '../types';
|
||||
import { IqserRolesService } from './roles.service';
|
||||
import { IqserPermissionsService } from './permissions.service';
|
||||
import { IqserPermissionsModule } from '../permissions.module';
|
||||
import { IqserRole } from '../models/role.model';
|
||||
|
||||
const ADMIN = 'ADMIN' as const;
|
||||
const GUEST = 'GUEST' as const;
|
||||
|
||||
describe('Roles Service', () => {
|
||||
let rolesService: IqserRolesService;
|
||||
let permissionsService: IqserPermissionsService;
|
||||
|
||||
function getRolesLength() {
|
||||
return Object.keys(rolesService.get()).length;
|
||||
}
|
||||
|
||||
function getPermissionsLength() {
|
||||
return Object.keys(permissionsService.get()).length;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [IqserPermissionsModule.forRoot()],
|
||||
@ -28,16 +34,16 @@ describe('Roles Service', () => {
|
||||
it('should add role to role object', () => {
|
||||
expect(rolesService.get(ADMIN)).toBeFalsy();
|
||||
|
||||
rolesService.add(ADMIN, ['edit', 'remove']);
|
||||
rolesService.add({ ADMIN: ['edit', 'remove'] });
|
||||
|
||||
expect(rolesService.get(ADMIN)).toBeTruthy();
|
||||
expect(rolesService.get()).toEqual({ ADMIN: { name: ADMIN, validationFn: ['edit', 'remove'] } });
|
||||
expect(rolesService.get()).toEqual({ ADMIN: ['edit', 'remove'] });
|
||||
});
|
||||
|
||||
it('should remove role from role object', () => {
|
||||
expect(rolesService.get(ADMIN)).toBeFalsy();
|
||||
|
||||
rolesService.add(ADMIN, ['edit', 'remove']);
|
||||
rolesService.add({ ADMIN: ['edit', 'remove'] });
|
||||
expect(rolesService.get(ADMIN)).toBeTruthy();
|
||||
|
||||
rolesService.remove(ADMIN);
|
||||
@ -45,39 +51,41 @@ describe('Roles Service', () => {
|
||||
});
|
||||
|
||||
it('should remove all roles from object', () => {
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
|
||||
rolesService.add(ADMIN, ['edit', 'remove']);
|
||||
rolesService.add(GUEST, ['edit', 'remove']);
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(2);
|
||||
rolesService.add({
|
||||
ADMIN: ['edit', 'remove'],
|
||||
GUEST: ['edit', 'remove'],
|
||||
});
|
||||
expect(getRolesLength()).toEqual(2);
|
||||
|
||||
rolesService.clear();
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should add multiple roles', () => {
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
rolesService.add({
|
||||
ADMIN: ['Nice'],
|
||||
GUEST: ['Awesome'],
|
||||
});
|
||||
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(2);
|
||||
expect(getRolesLength()).toEqual(2);
|
||||
expect(rolesService.get()).toEqual({
|
||||
ADMIN: { name: ADMIN, validationFn: ['Nice'] },
|
||||
GUEST: { name: GUEST, validationFn: ['Awesome'] },
|
||||
ADMIN: ['Nice'],
|
||||
GUEST: ['Awesome'],
|
||||
});
|
||||
});
|
||||
|
||||
it('return true when role name is present in Roles object', async () => {
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
it('return true when role name is present in roles object', async () => {
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
|
||||
rolesService.add({
|
||||
ADMIN: ['Nice'],
|
||||
GUEST: ['Awesome'],
|
||||
});
|
||||
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(2);
|
||||
expect(getRolesLength()).toEqual(2);
|
||||
|
||||
let result = await rolesService.has(ADMIN);
|
||||
expect(result).toEqual(true);
|
||||
@ -86,23 +94,24 @@ describe('Roles Service', () => {
|
||||
expect(result).toEqual(false);
|
||||
|
||||
result = await rolesService.has([ADMIN, 'IRIISISTABLE']);
|
||||
expect(result).toEqual(true);
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('return true when role permission name is present in Roles object', async () => {
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
|
||||
rolesService.add({
|
||||
ADMIN: ['Nice'],
|
||||
GUEST: ['Awesome'],
|
||||
});
|
||||
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(2);
|
||||
expect(getRolesLength()).toEqual(2);
|
||||
|
||||
let result = await rolesService.has(ADMIN);
|
||||
expect(result).toEqual(true);
|
||||
|
||||
result = await rolesService.has([ADMIN, 'IRRISISTABLE']);
|
||||
expect(result).toEqual(true);
|
||||
expect(result).toEqual(false);
|
||||
|
||||
result = await rolesService.has('SHOULDNOTHAVEROLE');
|
||||
expect(result).toEqual(false);
|
||||
@ -112,17 +121,11 @@ describe('Roles Service', () => {
|
||||
});
|
||||
|
||||
it('should return role when requested with has role', () => {
|
||||
rolesService.add('role', () => true);
|
||||
const role = rolesService.get('role');
|
||||
rolesService.add({ role: () => true });
|
||||
const validationFn = rolesService.get('role') as RoleValidationFn;
|
||||
|
||||
if (!role) {
|
||||
return expect(role).toBeTruthy();
|
||||
}
|
||||
|
||||
expect(role.name).toBe('role');
|
||||
|
||||
const validationFn = role.validationFn as ValidationFn<IqserRole>;
|
||||
expect(validationFn(role.name, rolesService.get())).toEqual(true);
|
||||
expect(validationFn).toBeTruthy();
|
||||
expect(validationFn('role', rolesService.get())).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true when checking with empty permission(not specified)', async () => {
|
||||
@ -136,31 +139,32 @@ describe('Roles Service', () => {
|
||||
});
|
||||
|
||||
it('should return false when role is not specified in the list', async () => {
|
||||
rolesService.add('test', ['One']);
|
||||
rolesService.add({ test: ['One'] });
|
||||
|
||||
const result = await rolesService.has('nice');
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when passing empty array', async () => {
|
||||
rolesService.add('test', ['One']);
|
||||
rolesService.add({ test: ['One'] });
|
||||
|
||||
const result = await rolesService.has([]);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should add permissions to roles automatically', async () => {
|
||||
rolesService.add('test', ['one', 'two']);
|
||||
rolesService.add({ test: ['one', 'two'] });
|
||||
|
||||
const result = await rolesService.has('test');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should remove roles and permissions add the same time', async () => {
|
||||
rolesService.add('test', ['one', 'two']);
|
||||
rolesService.add({ test: ['one', 'two'] });
|
||||
|
||||
let result = await rolesService.has('test');
|
||||
expect(result).toBe(true);
|
||||
|
||||
result = await permissionsService.has('one');
|
||||
expect(result).toBe(true);
|
||||
|
||||
@ -169,41 +173,42 @@ describe('Roles Service', () => {
|
||||
|
||||
result = await rolesService.has('test');
|
||||
expect(result).toBe(false);
|
||||
|
||||
result = await permissionsService.has('one');
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should remove all permissions and roles', () => {
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
|
||||
rolesService.add(ADMIN, ['edit', 'remove']);
|
||||
rolesService.add(GUEST, ['edit1', 'remove2']);
|
||||
rolesService.add({ ADMIN: ['edit', 'remove'] });
|
||||
rolesService.add({ GUEST: ['edit1', 'remove2'] });
|
||||
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(2);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(4);
|
||||
expect(getRolesLength()).toEqual(2);
|
||||
expect(getPermissionsLength()).toEqual(4);
|
||||
|
||||
rolesService.clear();
|
||||
permissionsService.clear();
|
||||
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(0);
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
expect(getPermissionsLength()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should add multiple roles with permissions', () => {
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(0);
|
||||
expect(getRolesLength()).toEqual(0);
|
||||
|
||||
rolesService.add({
|
||||
ADMIN: ['Nice'],
|
||||
GUEST: ['Awesome', 'Another awesome'],
|
||||
});
|
||||
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(2);
|
||||
expect(getRolesLength()).toEqual(2);
|
||||
expect(rolesService.get()).toEqual({
|
||||
ADMIN: { name: ADMIN, validationFn: ['Nice'] },
|
||||
GUEST: { name: GUEST, validationFn: ['Awesome', 'Another awesome'] },
|
||||
ADMIN: ['Nice'],
|
||||
GUEST: ['Awesome', 'Another awesome'],
|
||||
});
|
||||
|
||||
expect(Object.keys(rolesService.get()).length).toEqual(2);
|
||||
expect(Object.keys(permissionsService.get()).length).toEqual(3);
|
||||
expect(getRolesLength()).toEqual(2);
|
||||
expect(getPermissionsLength()).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,38 +1,27 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, firstValueFrom, from, Observable, of } from 'rxjs';
|
||||
import { catchError, every, first, map, mergeAll, mergeMap, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { IqserRoles, RoleValidationFn } from '../types';
|
||||
import { isArray, isBoolean, isString, toArray } from '../utils';
|
||||
import { List } from '../../utils';
|
||||
import { IqserPermissionsService } from './permissions.service';
|
||||
import { ValidationFn } from '../models/permissions-router-data.model';
|
||||
import { IqserRole } from '../models/role.model';
|
||||
import { isFunction, isString, toArray } from '../utils';
|
||||
|
||||
export interface IqserRolesObject {
|
||||
[name: string]: IqserRole;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class IqserRolesService {
|
||||
readonly roles$: Observable<IqserRolesObject>;
|
||||
readonly #roles$ = new BehaviorSubject<IqserRolesObject>({});
|
||||
readonly roles$: Observable<IqserRoles>;
|
||||
readonly #roles$ = new BehaviorSubject<IqserRoles>({});
|
||||
|
||||
constructor(private readonly _permissionsService: IqserPermissionsService) {
|
||||
this.roles$ = this.#roles$.asObservable();
|
||||
}
|
||||
|
||||
add(role: string, validationFn: ValidationFn<IqserRole> | string[]): void;
|
||||
add(rolesObj: Record<string, ValidationFn<IqserRole> | string[]>): void;
|
||||
add(role: string | Record<string, ValidationFn<IqserRole> | string[]>, validationFn?: ValidationFn<IqserRole> | string[]) {
|
||||
if (isString(role) && validationFn) {
|
||||
return this.#add(role, validationFn);
|
||||
}
|
||||
add(roles: string | List | IqserRoles) {
|
||||
const _roles = isString(roles) ? { [roles]: () => true } : roles;
|
||||
this.#reduce(_roles, this.#roles$.value);
|
||||
}
|
||||
|
||||
if (typeof role === 'object') {
|
||||
return Object.keys(role).forEach(key => this.#add(key, role[key]));
|
||||
}
|
||||
|
||||
throw new Error('Invalid add role arguments');
|
||||
load(roles: List | IqserRoles) {
|
||||
this.#reduce(roles);
|
||||
}
|
||||
|
||||
clear() {
|
||||
@ -45,85 +34,55 @@ export class IqserRolesService {
|
||||
this.#roles$.next(roles);
|
||||
}
|
||||
|
||||
get(): IqserRolesObject;
|
||||
|
||||
get(role: string): IqserRole | undefined;
|
||||
|
||||
get(role?: string): IqserRolesObject | IqserRole | undefined {
|
||||
get(): IqserRoles;
|
||||
get(role: string): RoleValidationFn | List | undefined;
|
||||
get(role?: string): IqserRoles | RoleValidationFn | List | undefined {
|
||||
return role ? this.#roles$.value[role] : this.#roles$.value;
|
||||
}
|
||||
|
||||
has(names?: string | string[]): Promise<boolean> {
|
||||
const isNamesEmpty = !names || (Array.isArray(names) && names.length === 0);
|
||||
has(roles: string | List): Promise<boolean> {
|
||||
const isEmpty = !roles || roles.length === 0;
|
||||
|
||||
if (isNamesEmpty) {
|
||||
if (isEmpty) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
names = toArray(names);
|
||||
|
||||
return Promise.all([this.#hasRoleKey(names), this.#hasPermission(names)]).then(([hasRoles, hasPermissions]) => {
|
||||
return !!hasRoles || !!hasPermissions;
|
||||
});
|
||||
const validations = toArray(roles).map(role => this.#runValidation(role));
|
||||
return Promise.all(validations)
|
||||
.then(results => this.#checkPermissionsIfNeeded(results))
|
||||
.then(results => results.every(result => result === true));
|
||||
}
|
||||
|
||||
#add(role: string, validationFn: ValidationFn<IqserRole> | string[]) {
|
||||
const roles: IqserRolesObject = {
|
||||
...this.#roles$.value,
|
||||
[role]: { name: role, validationFn },
|
||||
};
|
||||
#checkPermissionsIfNeeded(results: List<string | boolean | List>) {
|
||||
return results.map(result => (isBoolean(result) ? result : this._permissionsService.has(result)));
|
||||
}
|
||||
|
||||
if (Array.isArray(validationFn)) {
|
||||
this._permissionsService.add(validationFn);
|
||||
#runValidation(role: string) {
|
||||
const validationFn = this.#roles$.value[role];
|
||||
|
||||
if (isArray(validationFn)) {
|
||||
return this._permissionsService.has(validationFn);
|
||||
}
|
||||
|
||||
return this.#roles$.next(roles);
|
||||
return validationFn?.(role, this.#roles$.value) ?? false;
|
||||
}
|
||||
|
||||
#hasRoleKey(roleName: string[]): Promise<boolean> {
|
||||
const promises = roleName.map(key => {
|
||||
const role = this.#roles$.value[key];
|
||||
const hasValidationFn = !!role && !!role.validationFn;
|
||||
#reduce(roles: List | IqserRoles, initialValue: IqserRoles = {}) {
|
||||
const result = isArray(roles) ? this.#reduceList(roles, initialValue) : this.#reduceObject(roles, initialValue);
|
||||
return this.#roles$.next(result);
|
||||
}
|
||||
|
||||
if (hasValidationFn && isFunction<ValidationFn<IqserRole>>(role.validationFn)) {
|
||||
const validationFn = role.validationFn;
|
||||
const immutableValue = { ...this.#roles$.value };
|
||||
|
||||
return of(null).pipe(
|
||||
switchMap(async () => validationFn(key, immutableValue)),
|
||||
catchError(() => of(false)),
|
||||
);
|
||||
#reduceObject(roles: IqserRoles, initialValue: IqserRoles = {}) {
|
||||
return Object.entries(roles).reduce((acc, [role, validationFn]) => {
|
||||
if (isArray(validationFn)) {
|
||||
this._permissionsService.add(validationFn);
|
||||
}
|
||||
|
||||
return of(false);
|
||||
});
|
||||
|
||||
const res = from(promises).pipe(
|
||||
mergeAll(),
|
||||
first(data => data !== false, false),
|
||||
map(data => data !== false),
|
||||
);
|
||||
|
||||
return firstValueFrom(res);
|
||||
return { ...acc, [role]: validationFn };
|
||||
}, initialValue);
|
||||
}
|
||||
|
||||
#hasPermission(roleNames: string[]): Promise<boolean | undefined> {
|
||||
const res = from(roleNames).pipe(
|
||||
mergeMap(key => {
|
||||
const role = this.#roles$.value[key];
|
||||
|
||||
if (role && !isFunction<ValidationFn<IqserRole>>(role.validationFn)) {
|
||||
return from(role.validationFn).pipe(
|
||||
mergeMap(permission => this._permissionsService.has(permission)),
|
||||
every(hasPermission => hasPermission === true),
|
||||
);
|
||||
}
|
||||
|
||||
return of(false);
|
||||
}),
|
||||
first(hasPermission => hasPermission === true, false),
|
||||
);
|
||||
|
||||
return firstValueFrom(res);
|
||||
#reduceList(roles: List, initialValue: IqserRoles = {}) {
|
||||
return roles.reduce((acc, role) => ({ ...acc, [role]: () => true }), initialValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { ActivatedRouteSnapshot, NavigationExtras, Route, RouterStateSnapshot } from '@angular/router';
|
||||
import { List } from '../utils';
|
||||
|
||||
export type IqserPermissions = Record<string, PermissionValidationFn>;
|
||||
export type IqserRoles = Record<string, RoleValidationFn | List>;
|
||||
|
||||
export interface IqserPermissionsRouterData {
|
||||
allow: string | string[] | AllowFn;
|
||||
allow: string | List | AllowFn;
|
||||
redirectTo?: RedirectTo | RedirectToFn;
|
||||
}
|
||||
|
||||
@ -10,29 +14,29 @@ export interface IqserRedirectToNavigationParameters {
|
||||
navigationExtras?: NavigationExtras | NavigationExtrasFn;
|
||||
}
|
||||
|
||||
export type AllowFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => string | string[];
|
||||
export type AllowFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => string | List;
|
||||
|
||||
export type RedirectTo =
|
||||
| string
|
||||
| string[]
|
||||
| List
|
||||
| IqserRedirectToNavigationParameters
|
||||
| { [name: string]: IqserRedirectToNavigationParameters | string | RedirectToFn };
|
||||
| Record<string, IqserRedirectToNavigationParameters | string | RedirectToFn>;
|
||||
|
||||
export type RedirectToFn = (failedPermission: string, route?: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => RedirectTo;
|
||||
|
||||
export type NavigationCommandsFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => any[];
|
||||
export type NavigationExtrasFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => NavigationExtras;
|
||||
export type ValidationFn<T> = (name: string, store: Record<string, T>) => Promise<void | string | boolean> | boolean | string[];
|
||||
export type RoleValidationFn = (name: string, store: IqserRoles) => Promise<string | List | boolean> | boolean | List;
|
||||
export type PermissionValidationFn = (name: string, store: IqserPermissions) => Promise<boolean> | boolean;
|
||||
|
||||
export type IqserActivatedRouteSnapshot = ActivatedRouteSnapshot & {
|
||||
data: {
|
||||
permissions: IqserPermissionsRouterData;
|
||||
permissions?: IqserPermissionsRouterData;
|
||||
};
|
||||
};
|
||||
|
||||
export type IqserRoute = Route & {
|
||||
data: {
|
||||
permissions: IqserPermissionsRouterData;
|
||||
permissions?: IqserPermissionsRouterData;
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { AllowFn, IqserPermissionsRouterData, IqserRedirectToNavigationParameters } from './models/permissions-router-data.model';
|
||||
import { AllowFn, IqserPermissionsRouterData, IqserRedirectToNavigationParameters } from './types';
|
||||
import { ActivatedRouteSnapshot, Route, RouterStateSnapshot } from '@angular/router';
|
||||
import { IqserPermissionsData } from './services/permissions-guard.service';
|
||||
import { List } from '../utils';
|
||||
|
||||
export function isFunction<T>(value: unknown): value is T {
|
||||
return typeof value === 'function';
|
||||
@ -16,18 +17,20 @@ export function isPlainObject(value: unknown): boolean {
|
||||
}
|
||||
|
||||
export function isString(value: unknown): value is string {
|
||||
return !!value && typeof value === 'string';
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
export function notEmpty(value?: string | string[]): boolean {
|
||||
return Array.isArray(value) ? value.length > 0 : !!value;
|
||||
export function isBoolean(value: unknown): value is boolean {
|
||||
return typeof value === 'boolean';
|
||||
}
|
||||
|
||||
export function toArray(value?: string | string[]): string[] {
|
||||
if (isString(value)) {
|
||||
return [value];
|
||||
}
|
||||
return value ?? [];
|
||||
export function isArray<T>(value: unknown): value is List<T>;
|
||||
export function isArray<T>(value: unknown): value is T[] {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
export function toArray(value?: string | List): List {
|
||||
return isString(value) ? [value] : value ?? [];
|
||||
}
|
||||
|
||||
export function isRedirectWithParameters(object: any | IqserRedirectToNavigationParameters): object is IqserRedirectToNavigationParameters {
|
||||
@ -39,11 +42,11 @@ export function transformPermission(
|
||||
route: ActivatedRouteSnapshot | Route,
|
||||
state?: RouterStateSnapshot,
|
||||
): IqserPermissionsData {
|
||||
const only = isFunction<AllowFn>(permissions.allow) ? permissions.allow(route, state) : toArray(permissions.allow);
|
||||
const allow = isFunction<AllowFn>(permissions.allow) ? permissions.allow(route, state) : toArray(permissions.allow);
|
||||
const redirectTo = permissions.redirectTo;
|
||||
|
||||
return {
|
||||
allow: only,
|
||||
allow,
|
||||
redirectTo,
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user