Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context to validation function #146

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@ export class NgxPermissionsDirective implements OnInit, OnDestroy, OnChanges {

@Input() ngxPermissionsOnlyAuthorisedStrategy: string | StrategyFunction;
@Input() ngxPermissionsOnlyUnauthorisedStrategy: string | StrategyFunction;
@Input() ngxPermissionsOnlyContext: any;

@Input() ngxPermissionsExceptUnauthorisedStrategy: string | StrategyFunction;
@Input() ngxPermissionsExceptAuthorisedStrategy: string | StrategyFunction;
@Input() ngxPermissionsExceptContext: any;

@Input() ngxPermissionsUnauthorisedStrategy: string | StrategyFunction;
@Input() ngxPermissionsAuthorisedStrategy: string | StrategyFunction;


@Output() permissionsAuthorized = new EventEmitter();
@Output() permissionsUnauthorized = new EventEmitter();

Expand Down Expand Up @@ -126,7 +129,7 @@ export class NgxPermissionsDirective implements OnInit, OnDestroy, OnChanges {
private validateExceptAndOnlyPermissions(): void {
Promise
.all([
this.permissionsService.hasPermission(this.ngxPermissionsExcept),
this.permissionsService.hasPermission(this.ngxPermissionsExcept, this.ngxPermissionsExceptContext),
this.rolesService.hasOnlyRoles(this.ngxPermissionsExcept)
])
.then(([hasPermission, hasRole]) => {
Expand All @@ -152,7 +155,10 @@ export class NgxPermissionsDirective implements OnInit, OnDestroy, OnChanges {

private validateOnlyPermissions(): void {
Promise
.all([this.permissionsService.hasPermission(this.ngxPermissionsOnly), this.rolesService.hasOnlyRoles(this.ngxPermissionsOnly)])
.all([
this.permissionsService.hasPermission(this.ngxPermissionsOnly, this.ngxPermissionsOnlyContext),
this.rolesService.hasOnlyRoles(this.ngxPermissionsOnly)
])
.then(([hasPermissions, hasRoles]) => {
if (hasPermissions || hasRoles) {
this.handleAuthorisedPermission(this.ngxPermissionsOnlyThen || this.ngxPermissionsThen || this.templateRef);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface NgxPermissionsRouterData {
only?: string | string[] | OnlyFn;
except?: string | string[] | ExceptFn;
redirectTo?: RedirectTo | RedirectToFn;
context?: any;
}

export interface NgxRedirectToNavigationParameters {
Expand All @@ -13,6 +14,7 @@ export interface NgxRedirectToNavigationParameters {

export declare type OnlyFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => string | string[];
export declare type ExceptFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => string | string[];
export declare type ContextFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => string | string[];

export declare type RedirectTo =
string
Expand All @@ -23,6 +25,7 @@ export declare type RedirectToFn =

export declare type NavigationCommandsFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => any[];
export declare type NavigationExtrasFn = (route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) => NavigationExtras;
export declare type ValidationFn = ((name?: string, store?: any) => Promise<void | string | boolean> | boolean | string[]);
export declare type ValidationFn =
((name?: string, store?: any, context?: any) => Promise<void | string | boolean> | boolean | string[]);

export const DEFAULT_REDIRECT_KEY = 'default';
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('Permissions guard only', () => {
spyOn(fakeRouter, 'navigate');

service.addPermission('ADMIN');
service.addPermission('ADMIN_CONTEXT', (name, store, context) => context === true);
permissionGuard = new NgxPermissionsGuard(service, rolesService, fakeRouter as Router);
}));

Expand All @@ -41,6 +42,30 @@ describe('Permissions guard only', () => {
});
}));

it ('should return true when only fulfils, with context', fakeAsync(() => {
testRoute = { data: {
permissions: {
only: 'ADMIN_CONTEXT',
context: true
}
}};
(permissionGuard.canActivate(testRoute, {} as RouterStateSnapshot) as any).then((data) => {
expect(data).toEqual(true);
});
}));

it ('should return false when only doesnt match, with context', fakeAsync(() => {
testRoute = { data: {
permissions: {
only: 'ADMIN_CONTEXT',
context: false
}
}};
(permissionGuard.canActivate(testRoute, {} as RouterStateSnapshot) as any).then((data) => {
expect(data).toEqual(false);
});
}));

it ('should return true when only is empty array', fakeAsync(() => {
testRoute = { data: {
permissions: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
NgxPermissionsRouterData,
NgxRedirectToNavigationParameters,
OnlyFn,
ContextFn,
RedirectTo,
RedirectToFn
} from '../model/permissions-router-data.model';
Expand All @@ -32,6 +33,7 @@ export interface NgxPermissionsData {
only?: string | string[];
except?: string | string[];
redirectTo?: RedirectTo | RedirectToFn;
context?: any;
}

@Injectable()
Expand Down Expand Up @@ -78,13 +80,16 @@ export class NgxPermissionsGuard implements CanActivate, CanLoad, CanActivateChi
const except = isFunction<ExceptFn>(permissions.except)
? permissions.except(route, state)
: transformStringToArray(permissions.except);
const context = isFunction<ContextFn>(permissions.context)
? permissions.context(route, state)
: transformStringToArray(permissions.context);
const redirectTo = permissions.redirectTo;


return {
only,
except,
redirectTo
redirectTo,
context,
};
}

Expand All @@ -110,7 +115,7 @@ export class NgxPermissionsGuard implements CanActivate, CanLoad, CanActivateChi
.pipe(
mergeMap(permissionsExcept => {
return forkJoin([
this.permissionsService.hasPermission(permissionsExcept),
this.permissionsService.hasPermission(permissionsExcept, permissions.context),
this.rolesService.hasOnlyRoles(permissionsExcept)
]).pipe(
tap(hasPermissions => {
Expand Down Expand Up @@ -141,7 +146,7 @@ export class NgxPermissionsGuard implements CanActivate, CanLoad, CanActivateChi
}

return Promise.all([
this.permissionsService.hasPermission(permissions.except),
this.permissionsService.hasPermission(permissions.except, permissions.context),
this.rolesService.hasOnlyRoles(permissions.except)
]).then(([hasPermission, hasRoles]) => {
if (hasPermission || hasRoles) {
Expand Down Expand Up @@ -223,7 +228,7 @@ export class NgxPermissionsGuard implements CanActivate, CanLoad, CanActivateChi
.pipe(
mergeMap(permissionsOnly => {
return forkJoin([
this.permissionsService.hasPermission(permissionsOnly),
this.permissionsService.hasPermission(permissionsOnly, permissions.context),
this.rolesService.hasOnlyRoles(permissionsOnly)
]).pipe(
tap(hasPermissions => {
Expand Down Expand Up @@ -295,7 +300,7 @@ export class NgxPermissionsGuard implements CanActivate, CanLoad, CanActivateChi
};

return Promise.all([
this.permissionsService.hasPermission(permissions.only),
this.permissionsService.hasPermission(permissions.only, permissions.context),
this.rolesService.hasOnlyRoles(permissions.only)
]).then(([hasPermission, hasRole]) => {
if (hasPermission || hasRole) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,40 @@ describe('Permissions Service', () => {
// });
}));

it ('return true when role permission with context function return true', fakeAsync(() => {
expect(Object.keys(localService.getPermissions()).length).toEqual(0);
localService.addPermission(PermissionsNamesEnum.ADMIN as any, (name, store, context) => {
return context;
});
expect(Object.keys(localService.getPermissions()).length).toEqual(1);
localService.hasPermission('ADMIN', true).then((data) => {
expect(data).toEqual(true);
});

localService.addPermission(PermissionsNamesEnum.GUEST as any, (name, store, context) => {
return false && context;
});
expect(Object.keys(localService.getPermissions()).length).toEqual(2);
localService.hasPermission('GUEST', true).then((data) => {
expect(data).toEqual(false);
});

localService.addPermission('TEST1' as any, (name, store, context) => {
return Promise.resolve(context);
});
expect(Object.keys(localService.getPermissions()).length).toEqual(3);
localService.hasPermission('TEST1', true).then((data) => {
expect(data).toEqual(true);
});
localService.addPermission('TEST2' as any, (name, store, context) => {
return Promise.resolve(context);
});
expect(Object.keys(localService.getPermissions()).length).toEqual(4);
localService.hasPermission('TEST2', false).then((data) => {
expect(data).toEqual(false);
});
}));

it ('return call function with name and store in array', fakeAsync(() => {

localService.addPermission(['TEST11'] as any, (n, store) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ export class NgxPermissionsService {
this.permissionsSource.next({});
}

public hasPermission(permission: string | string[]): Promise<boolean> {
public hasPermission(permission: string | string[], context?: any): Promise<boolean> {
if (!permission || (Array.isArray(permission) && permission.length === 0)) {
return Promise.resolve(true);
}

permission = transformStringToArray(permission);
return this.hasArrayPermission(permission);
return this.hasArrayPermission(permission, context);
}

public loadPermissions(permissions: string[], validationFunction?: ValidationFn): void {
Expand Down Expand Up @@ -95,14 +95,14 @@ export class NgxPermissionsService {
};
}

private hasArrayPermission(permissions: string[]): Promise<boolean> {
private hasArrayPermission(permissions: string[], context?: any): Promise<boolean> {
const promises: Observable<boolean>[] = permissions.map(key => {
if (this.hasPermissionValidationFunction(key)) {
const validationFunction = this.permissionsSource.value[key].validationFunction;
const immutableValue = {...this.permissionsSource.value};

return of(null).pipe(
map(() => validationFunction(key, immutableValue)),
map(() => validationFunction(key, immutableValue, context)),
switchMap((promise: Promise<boolean> | boolean): ObservableInput<boolean> => isBoolean(promise) ?
of(promise as boolean) : promise as Promise<boolean>),
catchError(() => of(false))
Expand Down