AAE-26321 Add an injection token to JwtHelperService for OAuthStorage (#10288)

* AAE-26321 Add an injection token to JwtHelperService for OAuthStorage

* AAE-26321 remove unneeded method spies

* AAE-26321 Add missing providers to depending tests
This commit is contained in:
Wojciech Duda 2024-10-08 23:20:41 +02:00 committed by GitHub
parent 1d21c3ef80
commit a93f0bd069
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 74 additions and 71 deletions

View File

@ -18,9 +18,10 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } 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 { JWT_STORAGE_SERVICE, JwtHelperService } from '../services/jwt-helper.service';
import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { NoopTranslateModule } from '../../testing/noop-translate.module'; import { NoopTranslateModule } from '../../testing/noop-translate.module';
import { StorageService } from '../../common';
describe('Auth Guard SSO role service', () => { describe('Auth Guard SSO role service', () => {
let jwtHelperService: JwtHelperService; let jwtHelperService: JwtHelperService;
@ -29,7 +30,8 @@ describe('Auth Guard SSO role service', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopTranslateModule, MatDialogModule] imports: [NoopTranslateModule, MatDialogModule],
providers: [{ provide: JWT_STORAGE_SERVICE, useClass: StorageService }]
}); });
localStorage.clear(); localStorage.clear();
jwtHelperService = TestBed.inject(JwtHelperService); jwtHelperService = TestBed.inject(JwtHelperService);

View File

@ -28,6 +28,7 @@ import { EMPTY, of } from 'rxjs';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { NoopTranslateModule } from '../../testing/noop-translate.module'; import { NoopTranslateModule } from '../../testing/noop-translate.module';
import { JWT_STORAGE_SERVICE } from '../public-api';
describe('AuthGuardService', () => { describe('AuthGuardService', () => {
let state: RouterStateSnapshot; let state: RouterStateSnapshot;
@ -45,7 +46,7 @@ describe('AuthGuardService', () => {
imports: [NoopTranslateModule, MatDialogModule, RouterTestingModule], imports: [NoopTranslateModule, MatDialogModule, RouterTestingModule],
providers: [ providers: [
AppConfigService, AppConfigService,
StorageService, { provide: JWT_STORAGE_SERVICE, useClass: StorageService },
{ provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }, { provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } },
{ {
provide: OidcAuthenticationService, provide: OidcAuthenticationService,

View File

@ -18,8 +18,10 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { OidcAuthenticationService } from './oidc-authentication.service'; import { OidcAuthenticationService } from './oidc-authentication.service';
import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { AppConfigService, AuthService } from '@alfresco/adf-core';
import { AUTH_MODULE_CONFIG } from './auth-config'; import { AUTH_MODULE_CONFIG } from './auth-config';
import { StorageService } from '../../common';
import { AuthService, JWT_STORAGE_SERVICE } from '../public-api';
import { AppConfigService } from '../../app-config';
interface MockAppConfigOAuth2 { interface MockAppConfigOAuth2 {
oauth2: { oauth2: {
@ -62,6 +64,7 @@ describe('OidcAuthenticationService', () => {
OidcAuthenticationService, OidcAuthenticationService,
{ provide: AppConfigService, useClass: MockAppConfigService }, { provide: AppConfigService, useClass: MockAppConfigService },
{ provide: OAuthService, useClass: MockOAuthService }, { provide: OAuthService, useClass: MockOAuthService },
{ provide: JWT_STORAGE_SERVICE, useValue: StorageService },
{ provide: OAuthStorage, useValue: {} }, { provide: OAuthStorage, useValue: {} },
{ provide: AUTH_MODULE_CONFIG, useValue: {} }, { provide: AUTH_MODULE_CONFIG, useValue: {} },
{ provide: AuthService, useValue: {} } { provide: AuthService, useValue: {} }

View File

@ -29,7 +29,7 @@ import {
} from '../mock/identity-user.mock'; } from '../mock/identity-user.mock';
import { mockGroups, mockJoinGroupRequest } from '../mock/identity-group.mock'; import { mockGroups, mockJoinGroupRequest } from '../mock/identity-group.mock';
import { IdentityUserService } from './identity-user.service'; import { IdentityUserService } from './identity-user.service';
import { JwtHelperService } from './jwt-helper.service'; import { JWT_STORAGE_SERVICE, JwtHelperService } from './jwt-helper.service';
import { mockToken } from '../mock/jwt-helper.service.spec'; import { mockToken } from '../mock/jwt-helper.service.spec';
import { IdentityRoleModel } from '../models/identity-role.model'; import { IdentityRoleModel } from '../models/identity-role.model';
import { AdfHttpClient } from '../../../../api/src'; import { AdfHttpClient } from '../../../../api/src';
@ -53,7 +53,7 @@ describe('IdentityUserService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopTranslateModule], imports: [NoopTranslateModule],
providers: [StorageService, AdfHttpClient] providers: [StorageService, AdfHttpClient, { provide: JWT_STORAGE_SERVICE, useClass: StorageService }]
}); });
storageService = TestBed.inject(StorageService); storageService = TestBed.inject(StorageService);
service = TestBed.inject(IdentityUserService); service = TestBed.inject(IdentityUserService);

View File

@ -15,17 +15,29 @@
* limitations under the License. * limitations under the License.
*/ */
import { JwtHelperService } from './jwt-helper.service'; import { JWT_STORAGE_SERVICE, JwtHelperService } from './jwt-helper.service';
import { mockToken } from '../mock/jwt-helper.service.spec'; import { mockToken } from '../mock/jwt-helper.service.spec';
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
describe('JwtHelperService', () => { const mockLocalStorage = {
access_token: 'my-access_token',
id_token: 'my-id_token'
};
describe('JwtHelperService', () => {
let jwtHelperService: JwtHelperService; let jwtHelperService: JwtHelperService;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
providers: [JwtHelperService] providers: [
JwtHelperService,
{
provide: JWT_STORAGE_SERVICE,
useValue: {
getItem: (key: string) => mockLocalStorage[key]
}
}
]
}); });
jwtHelperService = TestBed.inject(JwtHelperService); jwtHelperService = TestBed.inject(JwtHelperService);
}); });
@ -44,26 +56,19 @@ describe('JwtHelperService', () => {
}); });
describe('RealmRole ', () => { describe('RealmRole ', () => {
it('Should be true if the realm_access contains the single role', () => { it('Should be true if the realm_access contains the single role', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'decodeToken').and.returnValue({
realm_access: { roles: ['role1'] }
spyOn(jwtHelperService, 'decodeToken').and.returnValue( });
{
realm_access: { roles: ['role1'] }
});
const result = jwtHelperService.hasRealmRole('role1'); const result = jwtHelperService.hasRealmRole('role1');
expect(result).toBeTruthy(); expect(result).toBeTruthy();
}); });
it('Should be true if the realm_access contains at least one of the roles', () => { it('Should be true if the realm_access contains at least one of the roles', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'decodeToken').and.returnValue({
realm_access: { roles: ['role1'] }
spyOn(jwtHelperService, 'decodeToken').and.returnValue( });
{
realm_access: { roles: ['role1'] }
});
const result = jwtHelperService.hasRealmRoles(['role1', 'role2']); const result = jwtHelperService.hasRealmRoles(['role1', 'role2']);
expect(result).toBeTruthy(); expect(result).toBeTruthy();
@ -71,46 +76,37 @@ describe('JwtHelperService', () => {
it('Should be false if the realm_access does not contain the role', () => { it('Should be false if the realm_access does not contain the role', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue( spyOn(jwtHelperService, 'decodeToken').and.returnValue({
{ realm_access: { roles: ['role3'] }
realm_access: { roles: ['role3'] } });
});
const result = jwtHelperService.hasRealmRole('role1'); const result = jwtHelperService.hasRealmRole('role1');
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });
it('Should be false if the realm_access does not contain at least one of the roles', () => { it('Should be false if the realm_access does not contain at least one of the roles', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue( spyOn(jwtHelperService, 'decodeToken').and.returnValue({
{ realm_access: { roles: ['role1'] }
realm_access: { roles: ['role1'] } });
});
const result = jwtHelperService.hasRealmRoles(['role3', 'role2']); const result = jwtHelperService.hasRealmRoles(['role3', 'role2']);
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });
}); });
describe('ClientRole ', () => { describe('ClientRole ', () => {
it('Should be true if the resource_access contains the single role', () => { it('Should be true if the resource_access contains the single role', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'decodeToken').and.returnValue({
resource_access: { fakeApp: { roles: ['role1'] } }
spyOn(jwtHelperService, 'decodeToken').and.returnValue( });
{
resource_access: { fakeApp: { roles: ['role1'] } }
});
const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1']); const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1']);
expect(result).toBeTruthy(); expect(result).toBeTruthy();
}); });
it('Should be true if the resource_access contains at least one of the roles', () => { it('Should be true if the resource_access contains at least one of the roles', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'decodeToken').and.returnValue({
resource_access: { fakeApp: { roles: ['role1'] } }
spyOn(jwtHelperService, 'decodeToken').and.returnValue( });
{
resource_access: { fakeApp: { roles: ['role1'] } }
});
const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']); const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']);
expect(result).toBeTruthy(); expect(result).toBeTruthy();
@ -118,22 +114,19 @@ describe('JwtHelperService', () => {
it('Should be false if the resource_access does not contain the role', () => { it('Should be false if the resource_access does not contain the role', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue( spyOn(jwtHelperService, 'decodeToken').and.returnValue({
{ resource_access: { fakeApp: { roles: ['role3'] } }
resource_access: { fakeApp: { roles: ['role3'] } } });
});
const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']); const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']);
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });
it('Should be false if the resource_access does not contain the client role related to the app', () => { it('Should be false if the resource_access does not contain the client role related to the app', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token'); spyOn(jwtHelperService, 'decodeToken').and.returnValue({
spyOn(jwtHelperService, 'decodeToken').and.returnValue( resource_access: { anotherFakeApp: { roles: ['role1'] } }
{ });
resource_access: { anotherFakeApp: { roles: ['role1'] } }
});
const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']); const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']);
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });
}); });
}); });

View File

@ -15,14 +15,15 @@
* limitations under the License. * limitations under the License.
*/ */
import { Injectable } from '@angular/core'; import { Inject, Injectable, InjectionToken } from '@angular/core';
import { StorageService } from '../../common/services/storage.service'; import { OAuthStorage } from 'angular-oauth2-oidc';
export const JWT_STORAGE_SERVICE = new InjectionToken<OAuthStorage>('JWT_STORAGE_SERVICE');
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class JwtHelperService { export class JwtHelperService {
static USER_NAME = 'name'; static USER_NAME = 'name';
static FAMILY_NAME = 'family_name'; static FAMILY_NAME = 'family_name';
static GIVEN_NAME = 'given_name'; static GIVEN_NAME = 'given_name';
@ -34,8 +35,7 @@ export class JwtHelperService {
static USER_PREFERRED_USERNAME = 'preferred_username'; static USER_PREFERRED_USERNAME = 'preferred_username';
static HXP_AUTHORIZATION = 'hxp_authorization'; static HXP_AUTHORIZATION = 'hxp_authorization';
constructor(private storageService: StorageService) { constructor(@Inject(JWT_STORAGE_SERVICE) private jwtStorage: OAuthStorage) {}
}
/** /**
* Decodes a JSON web token into a JS object. * Decodes a JSON web token into a JS object.
@ -85,7 +85,7 @@ export class JwtHelperService {
* @param key Key name of the field to retrieve * @param key Key name of the field to retrieve
* @returns Value from the token * @returns Value from the token
*/ */
getValueFromLocalToken<T>(key: string): T { getValueFromLocalToken<T>(key: string): T {
return this.getValueFromToken(this.getAccessToken(), key) || this.getValueFromToken(this.getIdToken(), key); return this.getValueFromToken(this.getAccessToken(), key) || this.getValueFromToken(this.getIdToken(), key);
} }
@ -105,7 +105,7 @@ export class JwtHelperService {
* @returns access token * @returns access token
*/ */
getAccessToken(): string { getAccessToken(): string {
return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN); return this.jwtStorage.getItem(JwtHelperService.USER_ACCESS_TOKEN);
} }
/** /**
@ -114,7 +114,7 @@ export class JwtHelperService {
* @param key Key name of the field to retrieve * @param key Key name of the field to retrieve
* @returns Value from the token * @returns Value from the token
*/ */
getValueFromLocalIdToken<T>(key: string): T { getValueFromLocalIdToken<T>(key: string): T {
return this.getValueFromToken(this.getIdToken(), key); return this.getValueFromToken(this.getIdToken(), key);
} }
@ -123,8 +123,8 @@ export class JwtHelperService {
* *
* @returns id token * @returns id token
*/ */
getIdToken(): string { getIdToken(): string {
return this.storageService.getItem(JwtHelperService.USER_ID_TOKEN); return this.jwtStorage.getItem(JwtHelperService.USER_ID_TOKEN);
} }
/** /**
@ -186,7 +186,7 @@ export class JwtHelperService {
* @param rolesToCheck List of role names to check * @param rolesToCheck List of role names to check
* @returns True if it contains at least one of the given roles, false otherwise * @returns True if it contains at least one of the given roles, false otherwise
*/ */
hasRealmRoles(rolesToCheck: string []): boolean { hasRealmRoles(rolesToCheck: string[]): boolean {
return rolesToCheck.some((currentRole) => this.hasRealmRole(currentRole)); return rolesToCheck.some((currentRole) => this.hasRealmRole(currentRole));
} }
@ -197,7 +197,7 @@ export class JwtHelperService {
* @param rolesToCheck List of role names to check * @param rolesToCheck List of role names to check
* @returns True if it contains at least one of the given roles, false otherwise * @returns True if it contains at least one of the given roles, false otherwise
*/ */
hasRealmRolesForClientRole(clientName: string, rolesToCheck: string []): boolean { hasRealmRolesForClientRole(clientName: string, rolesToCheck: string[]): boolean {
return rolesToCheck.some((currentRole) => this.hasClientRole(clientName, currentRole)); return rolesToCheck.some((currentRole) => this.hasClientRole(clientName, currentRole));
} }

View File

@ -17,9 +17,10 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { UserAccessService } from './user-access.service'; import { UserAccessService } from './user-access.service';
import { JwtHelperService } from './jwt-helper.service'; import { JWT_STORAGE_SERVICE, JwtHelperService } from './jwt-helper.service';
import { AppConfigService } from '../../app-config'; import { AppConfigService } from '../../app-config';
import { HttpClientTestingModule } from '@angular/common/http/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing';
import { StorageService } from '../../common';
describe('UserAccessService', () => { describe('UserAccessService', () => {
let userAccessService: UserAccessService; let userAccessService: UserAccessService;
@ -29,7 +30,7 @@ describe('UserAccessService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientTestingModule], imports: [HttpClientTestingModule],
providers: [UserAccessService] providers: [UserAccessService, { provide: JWT_STORAGE_SERVICE, useClass: StorageService }]
}); });
userAccessService = TestBed.inject(UserAccessService); userAccessService = TestBed.inject(UserAccessService);
jwtHelperService = TestBed.inject(JwtHelperService); jwtHelperService = TestBed.inject(JwtHelperService);

View File

@ -56,6 +56,7 @@ import { DynamicChipListComponent } from './dynamic-chip-list';
import { IdentityUserInfoComponent } from './identity-user-info'; import { IdentityUserInfoComponent } from './identity-user-info';
import { UnsavedChangesDialogComponent } from './dialogs'; import { UnsavedChangesDialogComponent } from './dialogs';
import { MaterialModule } from './material.module'; import { MaterialModule } from './material.module';
import { JWT_STORAGE_SERVICE } from './auth/services/jwt-helper.service';
@NgModule({ @NgModule({
imports: [ imports: [
@ -150,7 +151,8 @@ export class CoreModule {
useValue: { useValue: {
duration: 10000 duration: 10000
} }
} },
{ provide: JWT_STORAGE_SERVICE, useExisting: StorageService }
] ]
}; };
} }

View File

@ -16,7 +16,7 @@
*/ */
import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { AlfrescoApiService } from '@alfresco/adf-content-services';
import { ADF_DATE_FORMATS, FullNamePipe, NoopTranslateModule, UserPreferencesService } from '@alfresco/adf-core'; import { ADF_DATE_FORMATS, FullNamePipe, JWT_STORAGE_SERVICE, NoopTranslateModule, StorageService, UserPreferencesService } from '@alfresco/adf-core';
import { HarnessLoader } from '@angular/cdk/testing'; import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { SimpleChange } from '@angular/core'; import { SimpleChange } from '@angular/core';
@ -121,7 +121,8 @@ describe('EditProcessFilterCloudComponent', () => {
{ provide: DateAdapter, useClass: DateFnsAdapter }, { provide: DateAdapter, useClass: DateFnsAdapter },
{ provide: NotificationCloudService, useValue: { makeGQLQuery: () => of([]) } }, { provide: NotificationCloudService, useValue: { makeGQLQuery: () => of([]) } },
{ provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS }, { provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS },
{ provide: IDENTITY_USER_SERVICE_TOKEN, useExisting: IdentityUserServiceMock } { provide: IDENTITY_USER_SERVICE_TOKEN, useExisting: IdentityUserServiceMock },
{ provide: JWT_STORAGE_SERVICE, useClass: StorageService }
], ],
declarations: [PeopleCloudComponent, DateRangeFilterComponent] declarations: [PeopleCloudComponent, DateRangeFilterComponent]
}); });