Skip to content

Commit

Permalink
AAE-25392 Convert route guards to functional - part one (#10113)
Browse files Browse the repository at this point in the history
* AAE-25392 Convert route guards to functional - part one

* AAE-25392 Code improvement
  • Loading branch information
ehsan-2019 committed Aug 29, 2024
1 parent aeee07d commit c876058
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 80 deletions.
95 changes: 61 additions & 34 deletions lib/core/src/lib/auth/guard/auth-guard-sso-role.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand All @@ -53,65 +52,78 @@ 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: [] };

const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));

expect(authGuard.canActivate(router)).toBeTruthy();
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'] };

const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));

expect(authGuard.canActivate(router)).toBeFalsy();
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();

const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));

expect(authGuard.canActivate(router)).toBeFalsy();
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' };

const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));

expect(authGuard.canActivate(router)).toBeFalsy();
expect(authGuard).toBeFalsy();
expect(routerService.navigate).toHaveBeenCalledWith(['/no-role-url']);
});

it('Should not redirect if canActivate is false and redirectUrl is not in data', async () => {
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'] };

const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));

expect(authGuard.canActivate(router)).toBeFalsy();
expect(authGuard).toBeFalsy();
expect(routerService.navigate).not.toHaveBeenCalled();
});

Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -166,26 +186,33 @@ 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();
});

describe('Excluded Roles', () => {
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'] };

const authGuard = TestBed.runInInjectionContext(() => AuthGuardSsoRoleService(route, state));

expect(authGuard.canActivate(router)).toBe(false);
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();
});
});
});
77 changes: 31 additions & 46 deletions lib/core/src/lib/auth/guard/auth-guard-sso-role.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}

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);
export const AuthGuardSsoRoleService: CanActivateFn = (route: ActivatedRouteSnapshot): boolean => {
const userAccessService = inject(UserAccessService);
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'] || [];
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);
}

if (!hasRole) {
this.dialog.closeAll();
}

return hasRole;
}
const hasRole = hasRealmRole && hasClientRole;

private validateRoles(rolesToCheck: string[], excludedRoles?: string[]): boolean {
if (excludedRoles?.length > 0) {
return this.hasRoles(rolesToCheck) && !this.hasRoles(excludedRoles);
}
return this.hasRoles(rolesToCheck);
if (!hasRole && route?.data && route.data['redirectUrl']) {
const router = inject(Router);
router.navigate(['/' + route.data['redirectUrl']]);
}

private hasRoles(roles: string[] = []): boolean {
return this.userAccessService.hasGlobalAccess(roles);
if (!hasRole) {
const dialog = inject(MatDialog);
dialog.closeAll();
}

}
return hasRole;
};

0 comments on commit c876058

Please sign in to comment.