diff --git a/lib/core/src/lib/auth/oidc/auth-config.ts b/lib/core/src/lib/auth/oidc/auth-config.ts index 7983ac06fc..30eec28037 100644 --- a/lib/core/src/lib/auth/oidc/auth-config.ts +++ b/lib/core/src/lib/auth/oidc/auth-config.ts @@ -20,6 +20,7 @@ import { InjectionToken } from '@angular/core'; export interface AuthModuleConfig { readonly useHash: boolean; preventClearHashAfterLogin?: boolean; + overrideAuthStoragePrefix?: boolean; } export const AUTH_MODULE_CONFIG = new InjectionToken('AUTH_MODULE_CONFIG'); diff --git a/lib/core/src/lib/auth/oidc/auth.module.ts b/lib/core/src/lib/auth/oidc/auth.module.ts index a067960c22..84a5253bbe 100644 --- a/lib/core/src/lib/auth/oidc/auth.module.ts +++ b/lib/core/src/lib/auth/oidc/auth.module.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; +import { APP_INITIALIZER, EnvironmentProviders, ModuleWithProviders, NgModule, Provider } from '@angular/core'; import { AUTH_CONFIG, OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; import { AuthenticationService } from '../services/authentication.service'; import { StorageService } from '../../common/services/storage.service'; @@ -25,6 +25,8 @@ import { AuthRoutingModule } from './auth-routing.module'; import { AuthService } from './auth.service'; import { RedirectAuthService } from './redirect-auth.service'; import { AuthenticationConfirmationComponent } from './view/authentication-confirmation/authentication-confirmation.component'; +import { CustomAuthStorageService } from '../services/custom-auth-storage.service'; +import { STORAGE_SERVICE } from '../../common/interface/storage-service.interface'; /** * Create a Login Factory function @@ -41,7 +43,7 @@ export function loginFactory(redirectService: RedirectAuthService): () => Promis imports: [AuthRoutingModule, OAuthModule.forRoot()], providers: [ { provide: OAuthStorage, useExisting: StorageService }, - { provide: AuthenticationService}, + { provide: AuthenticationService }, { provide: AUTH_CONFIG, useFactory: authConfigFactory, @@ -58,11 +60,18 @@ export function loginFactory(redirectService: RedirectAuthService): () => Promis ] }) export class AuthModule { - static forRoot(config: AuthModuleConfig = { useHash: false }): ModuleWithProviders { + static forRoot(config: AuthModuleConfig = { useHash: false, overrideAuthStoragePrefix: false }): ModuleWithProviders { config.preventClearHashAfterLogin = config.preventClearHashAfterLogin ?? true; + const providers: (Provider | EnvironmentProviders)[] = [ + { provide: AUTH_MODULE_CONFIG, useValue: config } + ]; + if(config.overrideAuthStoragePrefix){ + providers.push({ provide: OAuthStorage, useClass: CustomAuthStorageService }); + providers.push({ provide: STORAGE_SERVICE, useExisting: OAuthStorage }); + } return { ngModule: AuthModule, - providers: [{ provide: AUTH_MODULE_CONFIG, useValue: config }] + providers }; } } diff --git a/lib/core/src/lib/auth/public-api.ts b/lib/core/src/lib/auth/public-api.ts index a457bd9530..9b8132cb88 100644 --- a/lib/core/src/lib/auth/public-api.ts +++ b/lib/core/src/lib/auth/public-api.ts @@ -30,6 +30,7 @@ export * from './services/identity-group.service'; export * from './services/jwt-helper.service'; export * from './services/oauth2.service'; export * from './services/user-access.service'; +export * from './services/custom-auth-storage.service'; export * from './basic-auth/basic-alfresco-auth.service'; export * from './basic-auth/process-auth'; diff --git a/lib/core/src/lib/auth/services/custom-auth-storage.service.spec.ts b/lib/core/src/lib/auth/services/custom-auth-storage.service.spec.ts new file mode 100644 index 0000000000..54cd78bb52 --- /dev/null +++ b/lib/core/src/lib/auth/services/custom-auth-storage.service.spec.ts @@ -0,0 +1,44 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestBed } from '@angular/core/testing'; + +import { of } from 'rxjs'; +import { CUSTOM_AUTH_STORAGE_PREFIX, CustomAuthStorageService } from './custom-auth-storage.service'; + +describe('CustomAuthStorageService', () => { + let service: CustomAuthStorageService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CustomAuthStorageService, + { provide: CUSTOM_AUTH_STORAGE_PREFIX, useValue: of('custom-prefix') } + ] + }); + service = TestBed.inject(CustomAuthStorageService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should set the prefix from the observable', () => { + const expectedPrefix = 'custom-prefix_'; + expect(service.prefix).toBe(expectedPrefix); + }); +}); diff --git a/lib/core/src/lib/auth/services/custom-auth-storage.service.ts b/lib/core/src/lib/auth/services/custom-auth-storage.service.ts new file mode 100644 index 0000000000..5e40563fe0 --- /dev/null +++ b/lib/core/src/lib/auth/services/custom-auth-storage.service.ts @@ -0,0 +1,33 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Inject, Injectable, InjectionToken } from '@angular/core'; +import { StorageService } from '../../common/services/storage.service'; +import { Observable } from 'rxjs'; + +export const CUSTOM_AUTH_STORAGE_PREFIX = new InjectionToken('CUSTOM_AUTH_STORAGE_PREFIX'); + +@Injectable() +export class CustomAuthStorageService extends StorageService { + + constructor(@Inject(CUSTOM_AUTH_STORAGE_PREFIX) customAuthStoragePrefix$: Observable) { + super(); + customAuthStoragePrefix$.subscribe(prefix => { + this.prefix = prefix; + }); + } +} diff --git a/lib/core/src/lib/auth/services/jwt-helper.service.spec.ts b/lib/core/src/lib/auth/services/jwt-helper.service.spec.ts index 0c56406fef..1d352b9595 100644 --- a/lib/core/src/lib/auth/services/jwt-helper.service.spec.ts +++ b/lib/core/src/lib/auth/services/jwt-helper.service.spec.ts @@ -18,6 +18,7 @@ import { JwtHelperService } from './jwt-helper.service'; import { mockToken } from '../mock/jwt-helper.service.spec'; import { TestBed } from '@angular/core/testing'; +import { STORAGE_SERVICE } from '../../common'; describe('JwtHelperService', () => { @@ -25,7 +26,10 @@ describe('JwtHelperService', () => { beforeEach(() => { TestBed.configureTestingModule({ - providers: [JwtHelperService] + providers: [ + JwtHelperService, + { provide: STORAGE_SERVICE, useValue: {} } + ] }); jwtHelperService = TestBed.inject(JwtHelperService); }); 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 d992f48278..7e3aa1c4e9 100644 --- a/lib/core/src/lib/auth/services/jwt-helper.service.ts +++ b/lib/core/src/lib/auth/services/jwt-helper.service.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { Injectable } from '@angular/core'; -import { StorageService } from '../../common/services/storage.service'; +import { Inject, Injectable } from '@angular/core'; +import { STORAGE_SERVICE, StorageServiceInterface } from '../../common/interface/storage-service.interface'; @Injectable({ providedIn: 'root' @@ -34,8 +34,7 @@ export class JwtHelperService { static USER_PREFERRED_USERNAME = 'preferred_username'; static HXP_AUTHORIZATION = 'hxp_authorization'; - constructor(private storageService: StorageService) { - } + constructor(@Inject(STORAGE_SERVICE) private storageService: StorageServiceInterface) {} /** * Decodes a JSON web token into a JS object. diff --git a/lib/core/src/lib/common/index.ts b/lib/core/src/lib/common/index.ts index 841c9136a2..0c0a36abeb 100644 --- a/lib/core/src/lib/common/index.ts +++ b/lib/core/src/lib/common/index.ts @@ -32,6 +32,7 @@ export * from './models/log-levels.model'; export * from './models/user-info-mode.enum'; export * from './interface/search-component.interface'; +export * from './interface/storage-service.interface'; export * from './mock/app-config.service.mock'; diff --git a/lib/core/src/lib/common/interface/storage-service.interface.ts b/lib/core/src/lib/common/interface/storage-service.interface.ts new file mode 100644 index 0000000000..18b6d9225e --- /dev/null +++ b/lib/core/src/lib/common/interface/storage-service.interface.ts @@ -0,0 +1,29 @@ +/*! + * @license + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InjectionToken } from '@angular/core'; + +export const STORAGE_SERVICE = new InjectionToken('STORAGE_SERVICE'); + +export interface StorageServiceInterface { + prefix: string; + getItem(key: string): string | null; + setItem(key: string, data: string): void; + clear(): void; + removeItem(key: string): void; + hasItem(key: string): boolean; +} diff --git a/lib/core/src/lib/common/services/storage.service.ts b/lib/core/src/lib/common/services/storage.service.ts index d1d768a6a6..2191b6cbdb 100644 --- a/lib/core/src/lib/common/services/storage.service.ts +++ b/lib/core/src/lib/common/services/storage.service.ts @@ -16,11 +16,12 @@ */ import { Injectable } from '@angular/core'; +import { StorageServiceInterface } from '../interface/storage-service.interface'; @Injectable({ providedIn: 'root' }) -export class StorageService { +export class StorageService implements StorageServiceInterface { private memoryStore: { [key: string]: any } = {}; private readonly useLocalStorage: boolean = false; private _prefix: string = ''; diff --git a/lib/core/src/lib/core.module.ts b/lib/core/src/lib/core.module.ts index eb417561a3..716241c9a7 100644 --- a/lib/core/src/lib/core.module.ts +++ b/lib/core/src/lib/core.module.ts @@ -50,6 +50,7 @@ import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar'; import { loadAppConfig } from './app-config/app-config.loader'; import { AppConfigService } from './app-config/app-config.service'; import { StorageService } from './common/services/storage.service'; +import { STORAGE_SERVICE } from './common/interface/storage-service.interface'; import { MomentDateAdapter } from './common/utils/moment-date-adapter'; import { AppConfigPipe, StoragePrefixFactory } from './app-config'; import { IconComponent } from './icon'; @@ -138,6 +139,7 @@ export class CoreModule { TranslateService, { provide: TranslateLoader, useClass: TranslateLoaderService }, MomentDateAdapter, + { provide: STORAGE_SERVICE, useExisting: StorageService }, StoragePrefixFactory, { provide: APP_INITIALIZER,