Skip to content

Commit

Permalink
AAE-26321 Add an injection token to JwtHelperService for OAuthStorage (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
wojd0 authored Oct 8, 2024
1 parent 1d21c3e commit a93f0bd
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
import { TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
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 { NoopTranslateModule } from '../../testing/noop-translate.module';
import { StorageService } from '../../common';

describe('Auth Guard SSO role service', () => {
let jwtHelperService: JwtHelperService;
Expand All @@ -29,7 +30,8 @@ describe('Auth Guard SSO role service', () => {

beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopTranslateModule, MatDialogModule]
imports: [NoopTranslateModule, MatDialogModule],
providers: [{ provide: JWT_STORAGE_SERVICE, useClass: StorageService }]
});
localStorage.clear();
jwtHelperService = TestBed.inject(JwtHelperService);
Expand Down
3 changes: 2 additions & 1 deletion lib/core/src/lib/auth/guard/auth-guard.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { EMPTY, of } from 'rxjs';
import { MatDialogModule } from '@angular/material/dialog';
import { RouterTestingModule } from '@angular/router/testing';
import { NoopTranslateModule } from '../../testing/noop-translate.module';
import { JWT_STORAGE_SERVICE } from '../public-api';

describe('AuthGuardService', () => {
let state: RouterStateSnapshot;
Expand All @@ -45,7 +46,7 @@ describe('AuthGuardService', () => {
imports: [NoopTranslateModule, MatDialogModule, RouterTestingModule],
providers: [
AppConfigService,
StorageService,
{ provide: JWT_STORAGE_SERVICE, useClass: StorageService },
{ provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } },
{
provide: OidcAuthenticationService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import { TestBed } from '@angular/core/testing';
import { OidcAuthenticationService } from './oidc-authentication.service';
import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { AppConfigService, AuthService } from '@alfresco/adf-core';
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 {
oauth2: {
Expand Down Expand Up @@ -62,6 +64,7 @@ describe('OidcAuthenticationService', () => {
OidcAuthenticationService,
{ provide: AppConfigService, useClass: MockAppConfigService },
{ provide: OAuthService, useClass: MockOAuthService },
{ provide: JWT_STORAGE_SERVICE, useValue: StorageService },
{ provide: OAuthStorage, useValue: {} },
{ provide: AUTH_MODULE_CONFIG, useValue: {} },
{ provide: AuthService, useValue: {} }
Expand Down
4 changes: 2 additions & 2 deletions lib/core/src/lib/auth/services/identity-user.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from '../mock/identity-user.mock';
import { mockGroups, mockJoinGroupRequest } from '../mock/identity-group.mock';
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 { IdentityRoleModel } from '../models/identity-role.model';
import { AdfHttpClient } from '../../../../api/src';
Expand All @@ -53,7 +53,7 @@ describe('IdentityUserService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopTranslateModule],
providers: [StorageService, AdfHttpClient]
providers: [StorageService, AdfHttpClient, { provide: JWT_STORAGE_SERVICE, useClass: StorageService }]
});
storageService = TestBed.inject(StorageService);
service = TestBed.inject(IdentityUserService);
Expand Down
89 changes: 41 additions & 48 deletions lib/core/src/lib/auth/services/jwt-helper.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,29 @@
* 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 { TestBed } from '@angular/core/testing';

describe('JwtHelperService', () => {
const mockLocalStorage = {
access_token: 'my-access_token',
id_token: 'my-id_token'
};

describe('JwtHelperService', () => {
let jwtHelperService: JwtHelperService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [JwtHelperService]
providers: [
JwtHelperService,
{
provide: JWT_STORAGE_SERVICE,
useValue: {
getItem: (key: string) => mockLocalStorage[key]
}
}
]
});
jwtHelperService = TestBed.inject(JwtHelperService);
});
Expand All @@ -44,96 +56,77 @@ describe('JwtHelperService', () => {
});

describe('RealmRole ', () => {

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');
expect(result).toBeTruthy();
});

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

it('Should be false if the realm_access does not contain the role', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
{
realm_access: { roles: ['role3'] }
});
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
realm_access: { roles: ['role3'] }
});
const result = jwtHelperService.hasRealmRole('role1');
expect(result).toBeFalsy();
});

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, 'decodeToken').and.returnValue(
{
realm_access: { roles: ['role1'] }
});
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
realm_access: { roles: ['role1'] }
});
const result = jwtHelperService.hasRealmRoles(['role3', 'role2']);
expect(result).toBeFalsy();
});
});
});

describe('ClientRole ', () => {

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

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

it('Should be false if the resource_access does not contain the role', () => {
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
{
resource_access: { fakeApp: { roles: ['role3'] } }
});
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
resource_access: { fakeApp: { roles: ['role3'] } }
});
const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']);
expect(result).toBeFalsy();
});

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(
{
resource_access: { anotherFakeApp: { roles: ['role1'] } }
});
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
resource_access: { anotherFakeApp: { roles: ['role1'] } }
});
const result = jwtHelperService.hasRealmRolesForClientRole('fakeApp', ['role1', 'role2']);
expect(result).toBeFalsy();
});
});
});
});
24 changes: 12 additions & 12 deletions lib/core/src/lib/auth/services/jwt-helper.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
* limitations under the License.
*/

import { Injectable } from '@angular/core';
import { StorageService } from '../../common/services/storage.service';
import { Inject, Injectable, InjectionToken } from '@angular/core';
import { OAuthStorage } from 'angular-oauth2-oidc';

export const JWT_STORAGE_SERVICE = new InjectionToken<OAuthStorage>('JWT_STORAGE_SERVICE');

@Injectable({
providedIn: 'root'
})
export class JwtHelperService {

static USER_NAME = 'name';
static FAMILY_NAME = 'family_name';
static GIVEN_NAME = 'given_name';
Expand All @@ -34,8 +35,7 @@ export class JwtHelperService {
static USER_PREFERRED_USERNAME = 'preferred_username';
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.
Expand Down Expand Up @@ -85,7 +85,7 @@ export class JwtHelperService {
* @param key Key name of the field to retrieve
* @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);
}

Expand All @@ -105,7 +105,7 @@ export class JwtHelperService {
* @returns access token
*/
getAccessToken(): string {
return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN);
return this.jwtStorage.getItem(JwtHelperService.USER_ACCESS_TOKEN);
}

/**
Expand All @@ -114,7 +114,7 @@ export class JwtHelperService {
* @param key Key name of the field to retrieve
* @returns Value from the token
*/
getValueFromLocalIdToken<T>(key: string): T {
getValueFromLocalIdToken<T>(key: string): T {
return this.getValueFromToken(this.getIdToken(), key);
}

Expand All @@ -123,8 +123,8 @@ export class JwtHelperService {
*
* @returns id token
*/
getIdToken(): string {
return this.storageService.getItem(JwtHelperService.USER_ID_TOKEN);
getIdToken(): string {
return this.jwtStorage.getItem(JwtHelperService.USER_ID_TOKEN);
}

/**
Expand Down Expand Up @@ -186,7 +186,7 @@ export class JwtHelperService {
* @param rolesToCheck List of role names to check
* @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));
}

Expand All @@ -197,7 +197,7 @@ export class JwtHelperService {
* @param rolesToCheck List of role names to check
* @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));
}

Expand Down
5 changes: 3 additions & 2 deletions lib/core/src/lib/auth/services/user-access.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

import { TestBed } from '@angular/core/testing';
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 { HttpClientTestingModule } from '@angular/common/http/testing';
import { StorageService } from '../../common';

describe('UserAccessService', () => {
let userAccessService: UserAccessService;
Expand All @@ -29,7 +30,7 @@ describe('UserAccessService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [UserAccessService]
providers: [UserAccessService, { provide: JWT_STORAGE_SERVICE, useClass: StorageService }]
});
userAccessService = TestBed.inject(UserAccessService);
jwtHelperService = TestBed.inject(JwtHelperService);
Expand Down
4 changes: 3 additions & 1 deletion lib/core/src/lib/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { DynamicChipListComponent } from './dynamic-chip-list';
import { IdentityUserInfoComponent } from './identity-user-info';
import { UnsavedChangesDialogComponent } from './dialogs';
import { MaterialModule } from './material.module';
import { JWT_STORAGE_SERVICE } from './auth/services/jwt-helper.service';

@NgModule({
imports: [
Expand Down Expand Up @@ -150,7 +151,8 @@ export class CoreModule {
useValue: {
duration: 10000
}
}
},
{ provide: JWT_STORAGE_SERVICE, useExisting: StorageService }
]
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

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 { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { SimpleChange } from '@angular/core';
Expand Down Expand Up @@ -121,7 +121,8 @@ describe('EditProcessFilterCloudComponent', () => {
{ provide: DateAdapter, useClass: DateFnsAdapter },
{ provide: NotificationCloudService, useValue: { makeGQLQuery: () => of([]) } },
{ 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]
});
Expand Down

0 comments on commit a93f0bd

Please sign in to comment.