463 lines
16 KiB
TypeScript
463 lines
16 KiB
TypeScript
import { TestBed } from '@angular/core/testing';
|
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
|
import { IqserActivatedRouteSnapshot, IqserPermissionsModule } from '..';
|
|
import { IqserPermissionsService } from './permissions.service';
|
|
import { IqserRolesService } from './roles.service';
|
|
import { IqserPermissionsGuard } from './permissions-guard.service';
|
|
|
|
const ADMIN = 'ADMIN' as const;
|
|
|
|
const defaultRouterState = {} as RouterStateSnapshot;
|
|
const defaultRouter: Partial<Router> = {
|
|
navigate: () => Promise.resolve(true),
|
|
};
|
|
|
|
let router: Router;
|
|
let routerNavigationSpy: jest.SpyInstance;
|
|
let testRoute: Partial<IqserActivatedRouteSnapshot>;
|
|
let permissionGuard: IqserPermissionsGuard;
|
|
let permissionsService: IqserPermissionsService;
|
|
|
|
function configureTestBed() {
|
|
router = { ...defaultRouter } as Router;
|
|
routerNavigationSpy = jest.spyOn(router, 'navigate');
|
|
|
|
TestBed.configureTestingModule({
|
|
imports: [IqserPermissionsModule.forRoot()],
|
|
providers: [
|
|
{
|
|
provide: Router,
|
|
useValue: router,
|
|
},
|
|
],
|
|
});
|
|
|
|
permissionGuard = TestBed.inject(IqserPermissionsGuard);
|
|
permissionsService = TestBed.inject(IqserPermissionsService);
|
|
}
|
|
|
|
describe('Permissions guard', () => {
|
|
beforeEach(async () => {
|
|
configureTestBed();
|
|
permissionsService.add([ADMIN]);
|
|
});
|
|
|
|
it('should create an instance', () => {
|
|
expect(permissionGuard).toBeTruthy();
|
|
});
|
|
|
|
it('should return true when only fulfils', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ADMIN,
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
expect(result).toEqual(true);
|
|
});
|
|
|
|
it('should return true when only is empty array', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: [],
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
expect(result).toEqual(true);
|
|
});
|
|
|
|
it('should return true when no permissions specified', async () => {
|
|
testRoute = {};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
expect(result).toEqual(true);
|
|
});
|
|
|
|
it('should return false when allow doesnt match', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: 'DOESNT MATCH',
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
expect(result).toEqual(false);
|
|
});
|
|
|
|
it('should return false when no permission match', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['DOESNT MATCH', 'DOESNT MATCH 2'],
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
expect(result).toEqual(false);
|
|
});
|
|
|
|
it('should return false when only doesnt match and navigate to 404', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: 'DOESNT MATCH',
|
|
redirectTo: './404',
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['./404']);
|
|
});
|
|
|
|
it('should return false when only doesnt match and navigate to array 404', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: 'DOESNT MATCH',
|
|
redirectTo: ['./404'],
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['./404']);
|
|
});
|
|
|
|
it('should return false when only doesnt match and navigate to redirectTo function', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: 'DOESNT MATCH',
|
|
redirectTo: () => ['./403'],
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['./403']);
|
|
});
|
|
});
|
|
|
|
describe('Permissions guard dynamically', () => {
|
|
beforeEach(async () => {
|
|
configureTestBed();
|
|
permissionsService.add(ADMIN);
|
|
});
|
|
|
|
it('should return true when only function matches', async () => {
|
|
testRoute = {
|
|
params: {
|
|
id: 44,
|
|
},
|
|
data: {
|
|
permissions: {
|
|
allow: route => {
|
|
if ((route as ActivatedRouteSnapshot).params['id'] === 44) {
|
|
return [ADMIN];
|
|
}
|
|
|
|
return 'notManager';
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
expect(result).toEqual(true);
|
|
});
|
|
|
|
it('should return false when only function is called and should redirect', async () => {
|
|
testRoute = {
|
|
params: {
|
|
id: 100,
|
|
},
|
|
data: {
|
|
permissions: {
|
|
allow: route => {
|
|
if ((route as ActivatedRouteSnapshot).params['id'] === 44) {
|
|
return [ADMIN];
|
|
}
|
|
|
|
return 'notManager';
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
expect(result).toEqual(false);
|
|
});
|
|
});
|
|
|
|
describe('Permissions guard dynamic redirectTo', () => {
|
|
beforeEach(() => {
|
|
configureTestBed();
|
|
permissionsService.add(ADMIN);
|
|
});
|
|
|
|
it('should redirect to parameters from navigationCommands and navigationExtras', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: 'TIED',
|
|
redirectTo: {
|
|
navigationCommands: ['123'],
|
|
navigationExtras: {
|
|
skipLocationChange: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['123'], { skipLocationChange: true });
|
|
});
|
|
|
|
it('should redirect to function parameters from navigationCommands and navigationExtras', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: 'TIED',
|
|
redirectTo: {
|
|
navigationCommands: () => ['123'],
|
|
navigationExtras: () => ({
|
|
skipLocationChange: true,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['123'], { skipLocationChange: true });
|
|
});
|
|
});
|
|
|
|
describe('Permissions guard with multiple redirectTo rules', () => {
|
|
beforeEach(() => {
|
|
configureTestBed();
|
|
permissionsService.add('canReadAgenda');
|
|
});
|
|
|
|
it('should fail on canEditAgenda and redirect to dashboard', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canReadAgenda', 'canEditAgenda', 'canRun'],
|
|
redirectTo: {
|
|
canReadAgenda: 'agendaList',
|
|
canEditAgenda: 'dashboard',
|
|
default: 'login',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['dashboard']);
|
|
});
|
|
|
|
it('should redirect to dashboard when canEditAgenda fails', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canReadAgenda', 'canEditAgenda', 'canRun'],
|
|
redirectTo: {
|
|
canReadAgenda: 'agendaList',
|
|
canEditAgenda: () => 'dashboard',
|
|
default: 'login',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['dashboard']);
|
|
});
|
|
|
|
it('should redirect to 123 when canEditAgenda fails with navigation parameters', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canEditAgenda', 'canRun'],
|
|
redirectTo: {
|
|
canReadAgenda: 'agendaList',
|
|
canEditAgenda: {
|
|
navigationCommands: ['123'],
|
|
navigationExtras: {
|
|
skipLocationChange: true,
|
|
},
|
|
},
|
|
default: 'login',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['123'], { skipLocationChange: true });
|
|
});
|
|
|
|
it('should redirect to 123 when canEditAgenda returns parameters from function', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canReadAgenda', 'canEditAgenda', 'canRun'],
|
|
redirectTo: {
|
|
canReadAgenda: 'agendaList',
|
|
canEditAgenda: () => {
|
|
return {
|
|
navigationCommands: ['123'],
|
|
navigationExtras: {
|
|
skipLocationChange: true,
|
|
},
|
|
};
|
|
},
|
|
default: 'login',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['123'], { skipLocationChange: true });
|
|
});
|
|
|
|
it('should redirect to default when a permission fails with no redirection rule', async () => {
|
|
permissionsService.add(['canEditAgenda']);
|
|
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canEditAgenda', 'Can run'],
|
|
redirectTo: {
|
|
canEditAgenda: 'dashboard',
|
|
default: 'login',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['login']);
|
|
expect(routerNavigationSpy).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should activate path when nothing fails', async () => {
|
|
permissionsService.add('canEditAgenda');
|
|
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canEditAgenda'],
|
|
redirectTo: {
|
|
canReadAgenda: 'agendaList',
|
|
canEditAgenda: 'dashboard',
|
|
canRun: 'run',
|
|
default: 'login',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(true);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('Permissions guard redirectTo as function', () => {
|
|
beforeEach(() => {
|
|
configureTestBed();
|
|
permissionsService.add('canReadAgenda');
|
|
});
|
|
|
|
it('should dynamically redirect', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canRun'],
|
|
redirectTo: failedPermission => failedPermission,
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(false);
|
|
expect(routerNavigationSpy).toHaveBeenCalledWith(['canRun']);
|
|
expect(routerNavigationSpy).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should allow to pass when at least one of parameters allow passing', async () => {
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['canReadAgenda', 'CAN_SWIM'],
|
|
redirectTo: () => 'login',
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(true);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('Role guard with redirectTo as function', () => {
|
|
let roleService: IqserRolesService;
|
|
|
|
beforeEach(() => {
|
|
configureTestBed();
|
|
roleService = TestBed.inject(IqserRolesService);
|
|
permissionsService.add('canReadAgenda');
|
|
permissionsService.add('AWESOME');
|
|
roleService.add({ ADMIN: ['AWESOME', 'canReadAgenda'] });
|
|
});
|
|
|
|
it('should dynamically pass if one satisfies', async () => {
|
|
roleService.add({ RUN: ['BLABLA', 'BLABLA2'] });
|
|
|
|
testRoute = {
|
|
data: {
|
|
permissions: {
|
|
allow: ['RUN', 'AWESOME'],
|
|
redirectTo: failedPermission => failedPermission,
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = await permissionGuard.canActivate(testRoute as ActivatedRouteSnapshot, defaultRouterState);
|
|
expect(result).toEqual(true);
|
|
expect(routerNavigationSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|