Skip to content

Commit

Permalink
fix: fix permission issues in edge cases by... (onecx#252)
Browse files Browse the repository at this point in the history
* fix: move initializeModuleGuard to angular-integration-interface to fix permission issues in edge cases

* fix: better deprecation info  message

* fix: add missing exports in index ts

---------

Co-authored-by: kim.tran <[email protected]>
  • Loading branch information
2 people authored and anninowak committed Jun 4, 2024
1 parent 57ed308 commit 7452e4b
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 73 deletions.
2 changes: 2 additions & 0 deletions libs/angular-integration-interface/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './lib/services/user.service'
export * from './lib/services/portal-message.service'
export * from './lib/services/theme.service'
export * from './lib/services/remote-components.service'
export * from './lib/services/initialize-module-guard.service'

// models
export * from './lib/model/config-key.model'
Expand All @@ -14,5 +15,6 @@ export * from './lib/api/iauth.service'
export * from './lib/api/injection-tokens'

// utils
export * from './lib/utils/add-initialize-module-guard.utils'

export { MfeInfo, Theme } from '@onecx/integration-interface'
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export interface LibConfig {
}
export const APP_CONFIG = new InjectionToken<LibConfig>('APP_CONFIG')

/**
* @deprecated
* Please do not inject the auth service at all.
* There are better ways to achieve what you want. Please use permission service or user service.
*/
export const AUTH_SERVICE = new InjectionToken<IAuthService>('AUTH_SERVICE')

export const SANITY_CHECK = new InjectionToken<string>('OCXSANITY_CHECK')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Injectable } from '@angular/core'
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router'
import { TranslateService } from '@ngx-translate/core'
import { filter, from, isObservable, map, mergeMap, Observable, of, tap, zip } from 'rxjs'
import { AppStateService } from './app-state.service';
import { ConfigurationService } from './configuration.service';
import { UserService } from './user.service';

@Injectable({ providedIn: 'any' })
export class InitializeModuleGuard implements CanActivate {
private SUPPORTED_LANGS = ['en', 'de']
private DEFAULT_LANG = 'en'
constructor(
protected translateService: TranslateService,
protected configService: ConfigurationService,
protected appStateService: AppStateService,
protected userService: UserService
) {}

canActivate(
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
console.time('InitializeModuleGuard')
return zip([
this.loadTranslations(),
from(this.configService.isInitialized),
from(this.userService.isInitialized),
from(this.appStateService.currentWorkspace$.isInitialized),
]).pipe(
tap(() => {
console.timeEnd('InitializeModuleGuard')
}),
map(() => true)
)
}

getBestMatchLanguage(lang: string) {
if (this.SUPPORTED_LANGS.includes(lang)) {
return lang
} else {
console.log(`${lang} is not supported. Using ${this.DEFAULT_LANG} as a fallback.`)
}
return this.DEFAULT_LANG
}

loadTranslations(): Observable<boolean> {
return this.userService.lang$.pipe(
filter((v) => v !== undefined),
mergeMap((lang) => {
const bestMatchLang = this.getBestMatchLanguage(lang as string)
return this.translateService.use(bestMatchLang)
}),
mergeMap(() => of(true))
)
}

protected toObservable(
canActivateResult: Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
): Observable<boolean | UrlTree> {
if (isObservable(canActivateResult)) {
return canActivateResult
}
return from(Promise.resolve(canActivateResult))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import { TranslateService } from '@ngx-translate/core'
import { AppStateService } from '../services/app-state.service';
import { UserService } from '../services/user.service';
import { ConfigurationService } from '../services/configuration.service';
import { InitializeModuleGuard } from '../services/initialize-module-guard.service'
import { addInitializeModuleGuard } from './add-initialize-module-guard.utils'

class MockInitializeModuleGuard extends InitializeModuleGuard {
constructor(
translateService: TranslateService,
configService: ConfigurationService,
appStateService: AppStateService,
userService: UserService
) {
super(translateService, configService, appStateService, userService)
}
}

describe('AddInitializeGuard', () => {
it('should add canActivate array with InitializeModuleGuard to routes without canActivate and redirectTo properties', () => {
const testRoutesNoCanActivate = [
{
path: 'testPathAddInitializerModuleGuard1',
},
{
path: 'testPathAddInitializerModuleGuard2',
},
]

const expectedRoutes = [
{
path: 'testPathAddInitializerModuleGuard1',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPathAddInitializerModuleGuard2',
canActivate: [InitializeModuleGuard],
},
]
const modifiedRoutes = addInitializeModuleGuard(testRoutesNoCanActivate)

expect(modifiedRoutes).toEqual(expectedRoutes)
expect(testRoutesNoCanActivate).not.toEqual(expectedRoutes)
})

it('should add InitializeModuleGuard to canActivate array which already has some other guard', () => {
const testRoutesWithMockInitializeModuleGuard = [
{
path: 'testPathHasMockInitializeModueGuard1',
canActivate: [MockInitializeModuleGuard],
},
{
path: 'testPath',
},
{
path: 'testPathHasMockInitializeModueGuard2',
canActivate: [MockInitializeModuleGuard],
},
]

const expectedRoutes = [
{
path: 'testPathHasMockInitializeModueGuard1',
canActivate: [MockInitializeModuleGuard, InitializeModuleGuard],
},
{
path: 'testPath',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPathHasMockInitializeModueGuard2',
canActivate: [MockInitializeModuleGuard, InitializeModuleGuard],
},
]
const modifiedRoutes = addInitializeModuleGuard(testRoutesWithMockInitializeModuleGuard)

expect(modifiedRoutes).toEqual(expectedRoutes)
expect(testRoutesWithMockInitializeModuleGuard).not.toEqual(expectedRoutes)
})

it('should not add another InitializeModuleGuard to canActivate array which already has an InitializeModuleGuard', () => {
const testRoutesWithInitializeModuleGuard = [
{
path: 'testPath1',
},
{
path: 'testPathHasInitializeModueGuard1',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPathHasInitializeModueGuard2',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPath2',
},
{
path: 'testPathHasInitializeModueGuard3',
canActivate: [InitializeModuleGuard],
},
]

const expectedRoutes = [
{
path: 'testPath1',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPathHasInitializeModueGuard1',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPathHasInitializeModueGuard2',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPath2',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPathHasInitializeModueGuard3',
canActivate: [InitializeModuleGuard],
},
]
const modifiedRoutes = addInitializeModuleGuard(testRoutesWithInitializeModuleGuard)

expect(modifiedRoutes).toEqual(expectedRoutes)
expect(testRoutesWithInitializeModuleGuard).not.toEqual(expectedRoutes)
})

it('should not add InitializeModuleGuard if a route has the redirectTo property', () => {
const testRoutesWithRedirectTo = [
{
path: 'testPathHasRedirectTo1',
redirectTo: 'redirectToPath1',
},
{
path: 'someTestPath1',
},
{
path: 'testPathHasRedirectTo2',
redirectTo: 'redirectToPath2',
},
{
path: 'someTestPath2',
},
]

const expectedRoutes = [
{
path: 'testPathHasRedirectTo1',
redirectTo: 'redirectToPath1',
},
{
path: 'someTestPath1',
canActivate: [InitializeModuleGuard],
},
{
path: 'testPathHasRedirectTo2',
redirectTo: 'redirectToPath2',
},
{
path: 'someTestPath2',
canActivate: [InitializeModuleGuard],
},
]
const modifiedRoutes = addInitializeModuleGuard(testRoutesWithRedirectTo)

expect(modifiedRoutes).toEqual(expectedRoutes)
expect(testRoutesWithRedirectTo).not.toEqual(expectedRoutes)
})

it('should add canActivate array with MockInitializeModuleGuard to routes without canActivate and redirectTo properties', () => {
const testRoutesNoCanActivate = [
{
path: 'testPathAddMockInitializerModuleGuard1',
},
{
path: 'testPathAddMockInitializerModuleGuard2',
},
{
path: 'testPathAddMockInitializerModuleGuard3',
},
]

const expectedRoutes = [
{
path: 'testPathAddMockInitializerModuleGuard1',
canActivate: [MockInitializeModuleGuard],
},
{
path: 'testPathAddMockInitializerModuleGuard2',
canActivate: [MockInitializeModuleGuard],
},
{
path: 'testPathAddMockInitializerModuleGuard3',
canActivate: [MockInitializeModuleGuard],
},
]
const modifiedRoutes = addInitializeModuleGuard(testRoutesNoCanActivate, MockInitializeModuleGuard)

expect(modifiedRoutes).toEqual(expectedRoutes)
expect(testRoutesNoCanActivate).not.toEqual(expectedRoutes)
})

it('should add MockInitializeModuleGuard to canActivate array', () => {
const testRoutesWithMockInitializeModuleGuard = [
{
path: 'testPathHasMockInitializeModueGuard1',
canActivate: [MockInitializeModuleGuard],
},
{
path: 'somePath',
},
{
path: 'testPathHasMockInitializeModueGuard2',
canActivate: [InitializeModuleGuard],
},
]

const expectedRoutes = [
{
path: 'testPathHasMockInitializeModueGuard1',
canActivate: [MockInitializeModuleGuard],
},
{
path: 'somePath',
canActivate: [MockInitializeModuleGuard],
},
{
path: 'testPathHasMockInitializeModueGuard2',
canActivate: [InitializeModuleGuard, MockInitializeModuleGuard],
},
]
const modifiedRoutes = addInitializeModuleGuard(testRoutesWithMockInitializeModuleGuard, MockInitializeModuleGuard)

expect(modifiedRoutes).toEqual(expectedRoutes)
expect(testRoutesWithMockInitializeModuleGuard).not.toEqual(expectedRoutes)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { CanActivateFn, Route } from '@angular/router'
import { InitializeModuleGuard } from '../services/initialize-module-guard.service'

export function addInitializeModuleGuard(
routes: Route[],
initializeModuleGuard: typeof InitializeModuleGuard | CanActivateFn = InitializeModuleGuard
): Route[] {
return routes.map((r) => {
if (r.redirectTo) {
return r
}
const route = {
canActivate: [],
...r,
}
if (!route.canActivate.includes(initializeModuleGuard)) {
route.canActivate.push(initializeModuleGuard)
}
return route
})
}
18 changes: 17 additions & 1 deletion libs/portal-integration-angular/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,20 @@ export * from './lib/core/utils/translate-service-initializer.utils'
export * from './lib/core/utils/portal-api-configuration.utils'

export * from '@onecx/angular-accelerator'
export * from '@onecx/angular-integration-interface'
export {
AppStateService,
ConfigurationService,
UserService,
PortalMessageService,
ThemeService,
RemoteComponentsService,
CONFIG_KEY,
IAuthService,
LibConfig,
MfeInfo,
Theme,
APP_CONFIG,
AUTH_SERVICE,
SANITY_CHECK,
APPLICATION_NAME
} from '@onecx/angular-integration-interface'
Loading

0 comments on commit 7452e4b

Please sign in to comment.