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 4cb7955203..f73173cdca 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 @@ -23,14 +23,12 @@ import { AuthGuardSsoRoleService } from './auth-guard-sso-role.service'; import { JwtHelperService } from '../services/jwt-helper.service'; import { MatDialog } from '@angular/material/dialog'; import { TranslateModule } from '@ngx-translate/core'; -import { UserAccessService } from '../services/user-access.service'; describe('Auth Guard SSO role service', () => { let authGuard: AuthGuardSsoRoleService; let jwtHelperService: JwtHelperService; let routerService: Router; - let userAccessService: UserAccessService; setupTestBed({ imports: [ @@ -44,8 +42,6 @@ describe('Auth Guard SSO role service', () => { authGuard = TestBed.inject(AuthGuardSsoRoleService); jwtHelperService = TestBed.inject(JwtHelperService); routerService = TestBed.inject(Router); - userAccessService = TestBed.inject(UserAccessService); - userAccessService.resetAccess(); }); function spyUserAccess(realmRoles: string[], resourceAccess: any) { 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 816e21f328..c34ab4b948 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 @@ -29,8 +29,8 @@ export class AuthGuardSsoRoleService implements CanActivate { private dialog: MatDialog) { } - async canActivate(route: ActivatedRouteSnapshot): Promise { - await this.userAccessService.fetchUserAccess(); + canActivate(route: ActivatedRouteSnapshot): boolean { + this.userAccessService.fetchUserAccess(); let hasRealmRole = false; let hasClientRole = true; if (route.data) { @@ -40,7 +40,7 @@ export class AuthGuardSsoRoleService implements CanActivate { hasRealmRole = true; } else { const excludedRoles = route.data['excludedRoles'] || []; - hasRealmRole = await this.validateRoles(rolesToCheck, excludedRoles); + hasRealmRole = this.validateRoles(rolesToCheck, excludedRoles); } } @@ -63,14 +63,14 @@ export class AuthGuardSsoRoleService implements CanActivate { return hasRole; } - private async validateRoles(rolesToCheck: string[], excludedRoles?: string[]): Promise { + private validateRoles(rolesToCheck: string[], excludedRoles?: string[]): boolean { if (excludedRoles?.length > 0) { - return await this.hasRoles(rolesToCheck) && !await this.hasRoles(excludedRoles); + return this.hasRoles(rolesToCheck) && !this.hasRoles(excludedRoles); } return this.hasRoles(rolesToCheck); } - private async hasRoles(roles: string[] = []): Promise { + private hasRoles(roles: string[] = []): boolean { return this.userAccessService.hasGlobalAccess(roles); } diff --git a/lib/core/src/lib/auth/services/jwt-helper.service.ts b/lib/core/src/lib/auth/services/jwt-helper.service.ts index 438f3bfd48..4aad71f86f 100644 --- a/lib/core/src/lib/auth/services/jwt-helper.service.ts +++ b/lib/core/src/lib/auth/services/jwt-helper.service.ts @@ -32,6 +32,7 @@ export class JwtHelperService { static REALM_ACCESS = 'realm_access'; static RESOURCE_ACCESS = 'resource_access'; static USER_PREFERRED_USERNAME = 'preferred_username'; + static HXP_AUTHORIZATION = 'hxp_authorization'; constructor(private storageService: StorageService) { } diff --git a/lib/core/src/lib/auth/services/user-access.service.spec.ts b/lib/core/src/lib/auth/services/user-access.service.spec.ts index c2081709fe..7de2934f93 100644 --- a/lib/core/src/lib/auth/services/user-access.service.spec.ts +++ b/lib/core/src/lib/auth/services/user-access.service.spec.ts @@ -19,15 +19,11 @@ import { CoreTestingModule, setupTestBed } from '../../testing'; import { TestBed } from '@angular/core/testing'; import { UserAccessService } from './user-access.service'; import { JwtHelperService } from './jwt-helper.service'; -import { OAuth2Service } from './oauth2.service'; -import { of, throwError } from 'rxjs'; -import { userAccessMock } from '../../mock/user-access.mock'; import { AppConfigService } from '../../app-config'; describe('UserAccessService', () => { let userAccessService: UserAccessService; let jwtHelperService: JwtHelperService; - let oauth2Service: OAuth2Service; let appConfigService: AppConfigService; setupTestBed({ @@ -37,13 +33,11 @@ describe('UserAccessService', () => { beforeEach(() => { userAccessService = TestBed.inject(UserAccessService); - userAccessService.resetAccess(); jwtHelperService = TestBed.inject(JwtHelperService); - oauth2Service = TestBed.inject(OAuth2Service); appConfigService = TestBed.inject(AppConfigService); }); - function spyUserAccess(realmRoles: string[], resourceAccess: any) { + function spyRealmAccess(realmRoles: string[], resourceAccess: any) { spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'decodeToken').and.returnValue({ realm_access: { roles: realmRoles }, @@ -51,131 +45,112 @@ describe('UserAccessService', () => { }); } - it('should return true when no roles to check are passed in global access', async () => { - spyUserAccess(['MOCK_USER_ROLE'], {}); - await userAccessService.fetchUserAccess(); + function spyHxpAuthorization(appkey: string, roles: string[]) { + spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); + spyOn(jwtHelperService, 'decodeToken').and.returnValue({ + hxp_authorization: { + appkey, + role: roles + } + }); + } + + it('should return true when no roles to check are passed in global access', () => { + spyRealmAccess(['MOCK_USER_ROLE'], {}); + userAccessService.fetchUserAccess(); const hasGlobalAccess = userAccessService.hasGlobalAccess([]); expect(hasGlobalAccess).toBe(true); }); - it('should return true when no roles to check are passed in application access', async () => { - spyUserAccess([], { mockApp: { roles: ['MOCK_APP_ROLE'] } }); - await userAccessService.fetchUserAccess(); + it('should return true when no roles to check are passed in application access', () => { + spyRealmAccess([], { mockApp: { roles: ['MOCK_APP_ROLE'] } }); + userAccessService.fetchUserAccess(); const hasApplicationAccess = userAccessService.hasApplicationAccess('mockApp', []); expect(hasApplicationAccess).toBe(true); }); - describe('Access from JWT token', () => { + describe('Access present in realm_access', () => { - it('should return true when the user has one of the global roles', async () => { - spyUserAccess(['MOCK_USER_ROLE', 'MOCK_USER_ROLE_2'], {}); - await userAccessService.fetchUserAccess(); + it('should return true when the user has one of the global roles', () => { + spyRealmAccess(['MOCK_USER_ROLE', 'MOCK_USER_ROLE_2'], {}); + userAccessService.fetchUserAccess(); const hasGlobalAccess = userAccessService.hasGlobalAccess(['MOCK_USER_ROLE']); expect(hasGlobalAccess).toEqual(true); }); - it('should return true when the user has one of the roles for an application', async () => { - spyUserAccess([], { mockApp: { roles: ['MOCK_APP_ROLE', 'MOCK_APP_ROLE_2'] } }); - await userAccessService.fetchUserAccess(); + it('should return true when the user has one of the roles for an application', () => { + spyRealmAccess([], { mockApp: { roles: ['MOCK_APP_ROLE', 'MOCK_APP_ROLE_2'] } }); + userAccessService.fetchUserAccess(); const hasApplicationAccess = userAccessService.hasApplicationAccess('mockApp', ['MOCK_APP_ROLE']); expect(hasApplicationAccess).toEqual(true); }); - it('should return false when the user has none of the global roles', async () => { - spyUserAccess(['MOCK_USER_ROLE'], {}); - await userAccessService.fetchUserAccess(); + it('should return false when the user has none of the global roles', () => { + spyRealmAccess(['MOCK_USER_ROLE'], {}); + userAccessService.fetchUserAccess(); const hasGlobalAccess = userAccessService.hasGlobalAccess(['MOCK_USER_ROLE_2']); expect(hasGlobalAccess).toEqual(false); }); - it('should return false when the user has none of the roles for an application', async () => { - spyUserAccess([], { mockApp: { roles: ['MOCK_APP_ROLE'] } }); - await userAccessService.fetchUserAccess(); + it('should return false when the user has none of the roles for an application', () => { + spyRealmAccess([], { mockApp: { roles: ['MOCK_APP_ROLE'] } }); + userAccessService.fetchUserAccess(); const hasApplicationAccess = userAccessService.hasApplicationAccess('mockApp', ['MOCK_APP_ROLE_2']); expect(hasApplicationAccess).toEqual(false); }); }); - describe('Access from the API', () => { - let getAccessFromApiSpy: jasmine.Spy; + describe('Access present in hxp_authorization', () => { - beforeEach(() => { - spyOn(jwtHelperService, 'getValueFromLocalToken').and.returnValue(undefined); - getAccessFromApiSpy = spyOn(oauth2Service, 'get').and.returnValue(of(userAccessMock)); - appConfigService.config.authType = 'OAUTH'; - }); - - it('should return true when the user has one of the global roles', async () => { - await userAccessService.fetchUserAccess(); + it('should return true when the user has one of the global roles', () => { + spyHxpAuthorization('mockApp1', ['MOCK_GLOBAL_USER_ROLE']); + appConfigService.config = { application: { key: 'mockApp1' } }; + userAccessService.fetchUserAccess(); const hasGlobalAccess = userAccessService.hasGlobalAccess(['MOCK_GLOBAL_USER_ROLE']); expect(hasGlobalAccess).toEqual(true); }); - it('should return true when the user has one of the roles for an application', async () => { - await userAccessService.fetchUserAccess(); + it('should return true when the user has one of the roles for an application', () => { + spyHxpAuthorization('mockApp1', ['MOCK_USER_ROLE_APP_1']); + appConfigService.config = { application: { key: 'mockApp1' } }; + userAccessService.fetchUserAccess(); const hasApplicationAccess = userAccessService.hasApplicationAccess('mockApp1', ['MOCK_USER_ROLE_APP_1']); expect(hasApplicationAccess).toEqual(true); }); - it('should return false when the user has none of the global roles', async () => { - await userAccessService.fetchUserAccess(); + it('should return false when the user has none of the global roles', () => { + spyHxpAuthorization('mockApp1', ['MOCK_USER_ROLE_APP_1']); + appConfigService.config = { application: { key: 'mockApp1' } }; + userAccessService.fetchUserAccess(); const hasGlobalAccess = userAccessService.hasGlobalAccess(['MOCK_USER_ROLE_NOT_EXISTING']); expect(hasGlobalAccess).toEqual(false); }); - it('should return false when the user has none of the roles for an application', async () => { - await userAccessService.fetchUserAccess(); + it('should return false when the user has none of the roles for an application', () => { + spyHxpAuthorization('mockApp1', ['MOCK_USER_ROLE_APP_1']); + appConfigService.config = { application: { key: 'mockApp1' } }; + userAccessService.fetchUserAccess(); const hasApplicationAccess = userAccessService.hasApplicationAccess('mockApp1', ['MOCK_ROLE_NOT_EXISING_IN_APP']); expect(hasApplicationAccess).toEqual(false); }); + }); - it('should not call more than once the api to fetch the user access', async () => { - await userAccessService.fetchUserAccess(); - await userAccessService.fetchUserAccess(); - await userAccessService.fetchUserAccess(); + it('should return false when access is neither in realm_access or hxp_authorization', () => { + spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); + spyOn(jwtHelperService, 'decodeToken').and.returnValue({ mock_access: {} }); + userAccessService.fetchUserAccess(); + const hasGlobalAccess = userAccessService.hasGlobalAccess(['mock_role']); - expect(getAccessFromApiSpy.calls.count()).toEqual(1); - }); - - it('should the url be composed from bpm host of app.config', async () => { - const fakeIdentityHost = 'https://fake-identity-host.fake.com'; - appConfigService.config.bpmHost = fakeIdentityHost; - await userAccessService.fetchUserAccess(); - - expect(getAccessFromApiSpy).toHaveBeenCalledWith({ url: `${fakeIdentityHost}/identity-adapter-service/v1/roles` }); - }); - - it('should the url contain appkey if its present in app config', async () => { - const fakeIdentityHost = 'https://fake-identity-host.fake.com'; - appConfigService.config.bpmHost = fakeIdentityHost; - appConfigService.config.application.key = 'fake-app-key'; - await userAccessService.fetchUserAccess(); - - expect(getAccessFromApiSpy).toHaveBeenCalledWith({ url: `${fakeIdentityHost}/identity-adapter-service/v1/roles` , queryParams: { appkey: 'fake-app-key' } }); - }); - - it('should not fetch the access from the API if is not configured with OAUTH', async () => { - appConfigService.config.authType = 'BASIC'; - await userAccessService.fetchUserAccess(); - - expect(getAccessFromApiSpy).not.toHaveBeenCalled(); - }); - - it('should set empty access list on fething roles error', async () => { - getAccessFromApiSpy.and.returnValue(throwError({ status: 503 })); - await userAccessService.fetchUserAccess(); - - expect(userAccessService.hasGlobalAccess(['MOCKED_ROLES'])).toBe(false); - }); + expect(hasGlobalAccess).toEqual(false); }); }); diff --git a/lib/core/src/lib/auth/services/user-access.service.ts b/lib/core/src/lib/auth/services/user-access.service.ts index 6352774b34..756a997e00 100644 --- a/lib/core/src/lib/auth/services/user-access.service.ts +++ b/lib/core/src/lib/auth/services/user-access.service.ts @@ -18,13 +18,7 @@ import { Injectable } from '@angular/core'; import { JwtHelperService } from './jwt-helper.service'; import { ApplicationAccessModel } from '../models/application-access.model'; -import { UserAccessModel } from '../models/user-access.model'; import { AppConfigService } from '../../app-config/app-config.service'; -import { OAuth2Service } from './oauth2.service'; -import { catchError } from 'rxjs/operators'; -import { of } from 'rxjs'; - -const IDENTITY_MICRO_SERVICE_INGRESS = 'identity-adapter-service'; @Injectable({ providedIn: 'root' @@ -34,60 +28,43 @@ export class UserAccessService { private applicationAccess: ApplicationAccessModel[]; constructor(private jwtHelperService: JwtHelperService, - private appConfigService: AppConfigService, - private oAuth2Service: OAuth2Service) { + private appConfigService: AppConfigService) { } - async fetchUserAccess() { - if (!this.hasFetchedAccess()) { - if (this.hasRolesInJwt()) { - this.fetchAccessFromJwt(); - } else if (this.isOauth()) { - await this.fetchAccessFromApi(); - } + fetchUserAccess() { + if (this.hasRolesInRealmAccess()) { + this.fetchAccessFromRealmAccess(); + } else if (this.hasRolesInHxpAuthorization()) { + this.fetchAccessFromHxpAuthorization(); } } - private fetchAccessFromJwt() { + private fetchAccessFromRealmAccess() { this.globalAccess = this.jwtHelperService.getValueFromLocalToken(JwtHelperService.REALM_ACCESS).roles; this.applicationAccess = this.jwtHelperService.getValueFromLocalToken(JwtHelperService.RESOURCE_ACCESS); } - private async fetchAccessFromApi() { - const url = `${this.identityHost}/${IDENTITY_MICRO_SERVICE_INGRESS}/v1/roles`; - const appkey = this.appConfigService.get('application.key'); - const opts = appkey ? { url, queryParams: { appkey } } : { url }; - - await this.oAuth2Service.get(opts) - .pipe( - catchError(() => of({ - globalAccess: { - roles: [] - }, - applicationAccess: [] - })) - ) - .toPromise() - .then((response: UserAccessModel) => { - this.globalAccess = response.globalAccess.roles; - this.applicationAccess = response.applicationAccess; - }); + private fetchAccessFromHxpAuthorization() { + this.globalAccess = []; + const hxpAuthorization = this.jwtHelperService.getValueFromLocalToken(JwtHelperService.HXP_AUTHORIZATION); + if (hxpAuthorization?.appkey && hxpAuthorization?.role) { + this.applicationAccess = [ + { + name: hxpAuthorization.appkey, + roles: hxpAuthorization.role + } + ]; + } else { + this.applicationAccess = []; + } } - private hasRolesInJwt(): boolean { + private hasRolesInRealmAccess(): boolean { return !!this.jwtHelperService.getValueFromLocalToken(JwtHelperService.REALM_ACCESS); } - private hasFetchedAccess(): boolean { - return !!this.globalAccess && !!this.applicationAccess; - } - - private get identityHost(): string { - return `${this.appConfigService.get('bpmHost')}`; - } - - private isOauth(): boolean { - return this.appConfigService.get('authType') === 'OAUTH'; + private hasRolesInHxpAuthorization(): boolean { + return !!this.jwtHelperService.getValueFromLocalToken(JwtHelperService.HXP_AUTHORIZATION); } /** @@ -98,9 +75,20 @@ export class UserAccessService { */ hasGlobalAccess(rolesToCheck: string[]): boolean { if (rolesToCheck?.length > 0) { - return this.globalAccess ? this.globalAccess.some((role: string) => rolesToCheck.includes(role)) : false; + if (this.hasRolesInRealmAccess()) { + return this.globalAccess ? this.globalAccess.some((role: string) => rolesToCheck.includes(role)) : false; + } else if (this.hasRolesInHxpAuthorization()) { + return this.isCurrentAppKeyInToken() ? this.applicationAccess[0]?.roles.some((role: string) => rolesToCheck.includes(role)) : false; + } + } else { + return true; } - return true; + return false; + } + + private isCurrentAppKeyInToken(): boolean { + const currentAppKey = this.appConfigService.get('application.key'); + return this.applicationAccess?.length ? currentAppKey === this.applicationAccess[0]?.name : false; } /** @@ -112,17 +100,9 @@ export class UserAccessService { */ hasApplicationAccess(appName: string, rolesToCheck: string[]): boolean { if (rolesToCheck?.length > 0) { - const appAccess = this.hasRolesInJwt() ? this.applicationAccess[appName] : this.applicationAccess.find((app: ApplicationAccessModel) => app.name === appName); + const appAccess = this.hasRolesInRealmAccess() ? this.applicationAccess[appName] : this.applicationAccess.find((app: ApplicationAccessModel) => app.name === appName); return appAccess ? appAccess.roles.some(appRole => rolesToCheck.includes(appRole)) : false; } return true; } - - /** - * Resets the cached user access - */ - resetAccess() { - this.globalAccess = undefined; - this.applicationAccess = undefined; - } }