From c876058c511af93445e69c2f077803c2c63e163e Mon Sep 17 00:00:00 2001 From: Ehsan Rezaei Date: Thu, 29 Aug 2024 18:07:01 +0200 Subject: [PATCH] AAE-25392 Convert route guards to functional - part one (#10113) * AAE-25392 Convert route guards to functional - part one * AAE-25392 Code improvement --- .../guard/auth-guard-sso-role.service.spec.ts | 95 ++++++++++++------- .../auth/guard/auth-guard-sso-role.service.ts | 77 ++++++--------- 2 files changed, 92 insertions(+), 80 deletions(-) diff --git a/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.spec.ts b/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.spec.ts index 27daafd19d..17092f0506 100644 --- a/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.spec.ts +++ b/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.spec.ts @@ -16,23 +16,22 @@ */ 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 { JwtHelperService } from '../services/jwt-helper.service'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { NoopTranslateModule } from '@alfresco/adf-core'; describe('Auth Guard SSO role service', () => { - let authGuard: AuthGuardSsoRoleService; let jwtHelperService: JwtHelperService; let routerService: Router; + const state: RouterStateSnapshot = {} as RouterStateSnapshot; beforeEach(() => { TestBed.configureTestingModule({ imports: [NoopTranslateModule, MatDialogModule] }); localStorage.clear(); - authGuard = TestBed.inject(AuthGuardSsoRoleService); jwtHelperService = TestBed.inject(JwtHelperService); 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 () => { spyUserAccess(['MOCK_USER_ROLE'], {}); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + 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 () => { spyUserAccess([], {}); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: [] }; + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + 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 () => { spyUserAccess(['MOCK_ROOT_USER_ROLE'], {}); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + 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 () => { spyUserAccess(['MOCK_USER_ROLE'], {}); spyOn(routerService, 'navigate').and.stub(); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + 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(); }); it('Should canActivate return false if the data Role to check is empty', async () => { 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 () => { spyUserAccess([], {}); spyOn(routerService, 'navigate').and.stub(); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'], redirectUrl: 'no-role-url' }; + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + 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']); }); @@ -108,10 +118,12 @@ describe('Auth Guard SSO role service', () => { spyUserAccess([], {}); spyOn(routerService, 'navigate').and.stub(); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'] }; + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + 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(); }); @@ -122,7 +134,9 @@ describe('Auth Guard SSO role service', () => { route.params = { appName: 'mockApp' }; 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 () => { @@ -132,7 +146,9 @@ describe('Auth Guard SSO role service', () => { route.params = { appName: 'mockApp' }; 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 () => { @@ -142,7 +158,9 @@ describe('Auth Guard SSO role service', () => { route.params = { appName: 'mockApp' }; 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 () => { @@ -152,7 +170,9 @@ describe('Auth Guard SSO role service', () => { route.params = { appName: 'mockApp' }; 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 () => { @@ -166,7 +186,9 @@ describe('Auth Guard SSO role service', () => { route.params = { appName: 'mockApp' }; 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(); }); @@ -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 () => { spyUserAccess(['MOCK_USER_ROLE'], {}); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: ['MOCK_ANOTHER_ROLE'], excludedRoles: ['MOCK_USER_ROLE'] }; + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + 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 () => { spyUserAccess(['MOCK_ADMIN_ROLE'], {}); - const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); - router.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'], excludedRoles: ['MOCK_ROOT_USER_ROLE'] }; - expect(authGuard.canActivate(router)).toBeTruthy(); + const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot(); + route.data = { roles: ['MOCK_USER_ROLE', 'MOCK_ADMIN_ROLE'], excludedRoles: ['MOCK_ROOT_USER_ROLE'] }; + + const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state)); + + expect(authGuard).toBeTruthy(); }); }); }); diff --git a/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.ts b/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.ts index 71870de7ec..dae768297a 100644 --- a/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.ts +++ b/lib/core/src/lib/auth/guard/auth-guard-sso-role.service.ts @@ -15,63 +15,48 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; +import { inject } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { UserAccessService } from '../services/user-access.service'; -@Injectable({ - providedIn: 'root' -}) -export class AuthGuardSsoRoleService implements CanActivate { - constructor(private userAccessService: UserAccessService, - private router: Router, - private dialog: MatDialog) { - } +export const AuthGuardSsoRoleService: CanActivateFn = (route: ActivatedRouteSnapshot): boolean => { + const userAccessService = inject(UserAccessService); + userAccessService.fetchUserAccess(); - canActivate(route: ActivatedRouteSnapshot): boolean { - this.userAccessService.fetchUserAccess(); - let hasRealmRole = false; - let hasClientRole = true; - if (route.data) { - if (route.data['roles']) { - const rolesToCheck: string[] = route.data['roles']; - if (rolesToCheck.length === 0) { - hasRealmRole = true; - } else { - const excludedRoles = route.data['excludedRoles'] || []; - hasRealmRole = this.validateRoles(rolesToCheck, excludedRoles); + let hasRealmRole = false; + let hasClientRole = true; + if (route.data) { + if (route.data['roles']) { + const rolesToCheck: string[] = route.data['roles']; + if (rolesToCheck.length === 0) { + hasRealmRole = true; + } else { + const excludedRoles = route.data['excludedRoles'] || []; + if (excludedRoles?.length > 0) { + hasRealmRole = userAccessService.hasGlobalAccess(rolesToCheck) && !userAccessService.hasGlobalAccess(excludedRoles); } - } - - if (route.data['clientRoles']) { - const clientRoleName = route.params[route.data['clientRoles']]; - const rolesToCheck = route.data['roles']; - hasClientRole = this.userAccessService.hasApplicationAccess(clientRoleName, rolesToCheck); + hasRealmRole = userAccessService.hasGlobalAccess(rolesToCheck); } } - const hasRole = hasRealmRole && hasClientRole; - if (!hasRole && route?.data && route.data['redirectUrl']) { - this.router.navigate(['/' + route.data['redirectUrl']]); + if (route.data['clientRoles']) { + const clientRoleName = route.params[route.data['clientRoles']]; + const rolesToCheck = route.data['roles']; + hasClientRole = userAccessService.hasApplicationAccess(clientRoleName, rolesToCheck); } + } + const hasRole = hasRealmRole && hasClientRole; - if (!hasRole) { - this.dialog.closeAll(); - } - - return hasRole; + if (!hasRole && route?.data && route.data['redirectUrl']) { + const router = inject(Router); + router.navigate(['/' + route.data['redirectUrl']]); } - private validateRoles(rolesToCheck: string[], excludedRoles?: string[]): boolean { - if (excludedRoles?.length > 0) { - return this.hasRoles(rolesToCheck) && !this.hasRoles(excludedRoles); - } - return this.hasRoles(rolesToCheck); + if (!hasRole) { + const dialog = inject(MatDialog); + dialog.closeAll(); } - private hasRoles(roles: string[] = []): boolean { - return this.userAccessService.hasGlobalAccess(roles); - } - -} + return hasRole; +};