AAE-25392 Convert route guards to functional - part one (#10113)

* AAE-25392 Convert route guards to functional - part one

* AAE-25392 Code improvement
This commit is contained in:
Ehsan Rezaei 2024-08-29 18:07:01 +02:00 committed by GitHub
parent aeee07d82a
commit c876058c51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 92 additions and 80 deletions

View File

@ -16,23 +16,22 @@
*/ */
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { AuthGuardSsoRoleService } from './auth-guard-sso-role.service'; import { AuthGuardSsoRoleService } from './auth-guard-sso-role.service';
import { JwtHelperService } from '../services/jwt-helper.service'; import { JwtHelperService } from '../services/jwt-helper.service';
import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { NoopTranslateModule } from '@alfresco/adf-core'; import { NoopTranslateModule } from '@alfresco/adf-core';
describe('Auth Guard SSO role service', () => { describe('Auth Guard SSO role service', () => {
let authGuard: AuthGuardSsoRoleService;
let jwtHelperService: JwtHelperService; let jwtHelperService: JwtHelperService;
let routerService: Router; let routerService: Router;
const state: RouterStateSnapshot = {} as RouterStateSnapshot;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopTranslateModule, MatDialogModule] imports: [NoopTranslateModule, MatDialogModule]
}); });
localStorage.clear(); localStorage.clear();
authGuard = TestBed.inject(AuthGuardSsoRoleService);
jwtHelperService = TestBed.inject(JwtHelperService); jwtHelperService = TestBed.inject(JwtHelperService);
routerService = TestBed.inject(Router); routerService = TestBed.inject(Router);
}); });
@ -53,54 +52,65 @@ describe('Auth Guard SSO role service', () => {
it('Should canActivate be true if the Role is present int the JWT token', async () => { it('Should canActivate be true if the Role is present int the JWT token', async () => {
spyUserAccess(['MOCK_USER_ROLE'], {}); spyUserAccess(['MOCK_USER_ROLE'], {});
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard.canActivate(router)).toBeTruthy(); expect(authGuard).toBeTruthy();
}); });
it('Should canActivate be true if case of empty roles to check', async () => { it('Should canActivate be true if case of empty roles to check', async () => {
spyUserAccess([], {}); spyUserAccess([], {});
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: [] }; route.data = { roles: [] };
expect(authGuard.canActivate(router)).toBeTruthy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeTruthy();
}); });
it('Should canActivate be false if the Role is not present int the JWT token', async () => { it('Should canActivate be false if the Role is not present int the JWT token', async () => {
spyUserAccess(['MOCK_ROOT_USER_ROLE'], {}); spyUserAccess(['MOCK_ROOT_USER_ROLE'], {});
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(router)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
}); });
it('Should not redirect if canActivate is', async () => { it('Should not redirect if canActivate is', async () => {
spyUserAccess(['MOCK_USER_ROLE'], {}); spyUserAccess(['MOCK_USER_ROLE'], {});
spyOn(routerService, 'navigate').and.stub(); spyOn(routerService, 'navigate').and.stub();
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(router)).toBeTruthy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeTruthy();
expect(routerService.navigate).not.toHaveBeenCalled(); expect(routerService.navigate).not.toHaveBeenCalled();
}); });
it('Should canActivate return false if the data Role to check is empty', async () => { it('Should canActivate return false if the data Role to check is empty', async () => {
spyUserAccess(['MOCK_USER_ROLE', 'MOCK_ROOT_USER_ROLE'], {}); spyUserAccess(['MOCK_USER_ROLE', 'MOCK_ROOT_USER_ROLE'], {});
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
expect(authGuard.canActivate(router)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
}); });
it('Should redirect to the redirectURL if canActivate is false and redirectUrl is in data', async () => { it('Should redirect to the redirectURL if canActivate is false and redirectUrl is in data', async () => {
spyUserAccess([], {}); spyUserAccess([], {});
spyOn(routerService, 'navigate').and.stub(); spyOn(routerService, 'navigate').and.stub();
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'], redirectUrl: 'no-role-url' }; route.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'], redirectUrl: 'no-role-url' };
expect(authGuard.canActivate(router)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
expect(routerService.navigate).toHaveBeenCalledWith(['/no-role-url']); expect(routerService.navigate).toHaveBeenCalledWith(['/no-role-url']);
}); });
@ -108,10 +118,12 @@ describe('Auth Guard SSO role service', () => {
spyUserAccess([], {}); spyUserAccess([], {});
spyOn(routerService, 'navigate').and.stub(); spyOn(routerService, 'navigate').and.stub();
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(router)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
expect(routerService.navigate).not.toHaveBeenCalled(); expect(routerService.navigate).not.toHaveBeenCalled();
}); });
@ -122,7 +134,9 @@ describe('Auth Guard SSO role service', () => {
route.params = { appName: 'mockApp' }; route.params = { appName: 'mockApp' };
route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(route)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
}); });
it('Should canActivate be false if hasRealm is false and hasClientRole is true', async () => { it('Should canActivate be false if hasRealm is false and hasClientRole is true', async () => {
@ -132,7 +146,9 @@ describe('Auth Guard SSO role service', () => {
route.params = { appName: 'mockApp' }; route.params = { appName: 'mockApp' };
route.data = { clientRoles: ['mockApp'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { clientRoles: ['mockApp'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(route)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
}); });
it('Should canActivate be true if both Real Role and Client Role are present int the JWT token', async () => { it('Should canActivate be true if both Real Role and Client Role are present int the JWT token', async () => {
@ -142,7 +158,9 @@ describe('Auth Guard SSO role service', () => {
route.params = { appName: 'mockApp' }; route.params = { appName: 'mockApp' };
route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(route)).toBeTruthy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeTruthy();
}); });
it('Should canActivate be false if the Client Role is not present int the JWT token with the correct role', async () => { it('Should canActivate be false if the Client Role is not present int the JWT token with the correct role', async () => {
@ -152,7 +170,9 @@ describe('Auth Guard SSO role service', () => {
route.params = { appName: 'mockApp' }; route.params = { appName: 'mockApp' };
route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(route)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
}); });
it('Should canActivate be false hasRealm is true and hasClientRole is false', async () => { it('Should canActivate be false hasRealm is true and hasClientRole is false', async () => {
@ -166,7 +186,9 @@ describe('Auth Guard SSO role service', () => {
route.params = { appName: 'mockApp' }; route.params = { appName: 'mockApp' };
route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; route.data = { clientRoles: ['appName'], roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] };
expect(authGuard.canActivate(route)).toBeFalsy(); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeFalsy();
expect(materialDialog.closeAll).toHaveBeenCalled(); expect(materialDialog.closeAll).toHaveBeenCalled();
}); });
@ -174,18 +196,23 @@ describe('Auth Guard SSO role service', () => {
it('Should canActivate be false when the user has one of the excluded roles', async () => { it('Should canActivate be false when the user has one of the excluded roles', async () => {
spyUserAccess(['MOCK_USER_ROLE'], {}); spyUserAccess(['MOCK_USER_ROLE'], {});
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: ['MOCK_ANOTHER_ROLE'], excludedRoles: ['MOCK_USER_ROLE'] }; route.data = { roles: ['MOCK_ANOTHER_ROLE'], excludedRoles: ['MOCK_USER_ROLE'] };
expect(authGuard.canActivate(router)).toBe(false); const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBe(false);
}); });
it('Should canActivate be true when the user has none of the excluded roles', async () => { it('Should canActivate be true when the user has none of the excluded roles', async () => {
spyUserAccess(['MOCK_ADMIN_ROLE'], {}); spyUserAccess(['MOCK_ADMIN_ROLE'], {});
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'], excludedRoles: ['MOCK_ROOT_USER_ROLE'] }; route.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'], excludedRoles: ['MOCK_ROOT_USER_ROLE'] };
expect(authGuard.canActivate(router)).toBeTruthy();
const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));
expect(authGuard).toBeTruthy();
}); });
}); });
}); });

View File

@ -15,22 +15,15 @@
* limitations under the License. * limitations under the License.
*/ */
import { Injectable } from '@angular/core'; import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { UserAccessService } from '../services/user-access.service'; import { UserAccessService } from '../services/user-access.service';
@Injectable({ export const AuthGuardSsoRoleService: CanActivateFn = (route: ActivatedRouteSnapshot): boolean => {
providedIn: 'root' const userAccessService = inject(UserAccessService);
}) userAccessService.fetchUserAccess();
export class AuthGuardSsoRoleService implements CanActivate {
constructor(private userAccessService: UserAccessService,
private router: Router,
private dialog: MatDialog) {
}
canActivate(route: ActivatedRouteSnapshot): boolean {
this.userAccessService.fetchUserAccess();
let hasRealmRole = false; let hasRealmRole = false;
let hasClientRole = true; let hasClientRole = true;
if (route.data) { if (route.data) {
@ -40,38 +33,30 @@ export class AuthGuardSsoRoleService implements CanActivate {
hasRealmRole = true; hasRealmRole = true;
} else { } else {
const excludedRoles = route.data['excludedRoles'] || []; const excludedRoles = route.data['excludedRoles'] || [];
hasRealmRole = this.validateRoles(rolesToCheck, excludedRoles); if (excludedRoles?.length > 0) {
hasRealmRole = userAccessService.hasGlobalAccess(rolesToCheck) && !userAccessService.hasGlobalAccess(excludedRoles);
}
hasRealmRole = userAccessService.hasGlobalAccess(rolesToCheck);
} }
} }
if (route.data['clientRoles']) { if (route.data['clientRoles']) {
const clientRoleName = route.params[route.data['clientRoles']]; const clientRoleName = route.params[route.data['clientRoles']];
const rolesToCheck = route.data['roles']; const rolesToCheck = route.data['roles'];
hasClientRole = this.userAccessService.hasApplicationAccess(clientRoleName, rolesToCheck); hasClientRole = userAccessService.hasApplicationAccess(clientRoleName, rolesToCheck);
} }
} }
const hasRole = hasRealmRole && hasClientRole; const hasRole = hasRealmRole && hasClientRole;
if (!hasRole && route?.data && route.data['redirectUrl']) { if (!hasRole && route?.data && route.data['redirectUrl']) {
this.router.navigate(['/' + route.data['redirectUrl']]); const router = inject(Router);
router.navigate(['/' + route.data['redirectUrl']]);
} }
if (!hasRole) { if (!hasRole) {
this.dialog.closeAll(); const dialog = inject(MatDialog);
dialog.closeAll();
} }
return hasRole; return hasRole;
} };
private validateRoles(rolesToCheck: string[], excludedRoles?: string[]): boolean {
if (excludedRoles?.length > 0) {
return this.hasRoles(rolesToCheck) && !this.hasRoles(excludedRoles);
}
return this.hasRoles(rolesToCheck);
}
private hasRoles(roles: string[] = []): boolean {
return this.userAccessService.hasGlobalAccess(roles);
}
}