diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/login/state/access/access.effects.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/login/state/access/access.effects.ts index 46f87732bb2d..632e524b8ac0 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/login/state/access/access.effects.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/login/state/access/access.effects.ts @@ -29,6 +29,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Store } from '@ngrx/store'; import { NiFiState } from '../../../../state'; import { resetLoginFailure } from './access.actions'; +import { StorageService } from '../../../../service/storage.service'; @Injectable() export class AccessEffects { @@ -38,6 +39,7 @@ export class AccessEffects { private router = inject(Router); private dialog = inject(MatDialog); private errorHelper = inject(ErrorHelper); + private storageService = inject(StorageService); login$ = createEffect(() => this.actions$.pipe( @@ -59,7 +61,13 @@ export class AccessEffects { this.actions$.pipe( ofType(AccessActions.loginSuccess), tap(() => { - this.router.navigate(['/']); + const returnUrl = this.storageService.getReturnUrl(); + if (returnUrl) { + this.storageService.removeReturnUrl(); + this.router.navigateByUrl(returnUrl); + } else { + this.router.navigate(['/']); + } }) ), { dispatch: false } diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/client.service.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/client.service.ts index d9565d01f00d..3006acba4882 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/client.service.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/client.service.ts @@ -17,22 +17,22 @@ import { Injectable, inject } from '@angular/core'; import { v4 as uuidv4 } from 'uuid'; -import { SessionStorageService } from '@nifi/shared'; +import { StorageService } from './storage.service'; @Injectable({ providedIn: 'root' }) export class Client { - private sessionStorage = inject(SessionStorageService); + private storageService = inject(StorageService); private clientId: string; constructor() { - let clientId = this.sessionStorage.getItem('clientId'); + let clientId = this.storageService.getClientId(); if (clientId === null) { clientId = uuidv4(); - this.sessionStorage.setItem('clientId', clientId); + this.storageService.setClientId(clientId); } this.clientId = clientId; diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/guard/authentication.guard.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/guard/authentication.guard.ts index 33c54a59fee7..616df912f6d0 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/guard/authentication.guard.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/guard/authentication.guard.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { CanMatchFn } from '@angular/router'; +import { CanMatchFn, Router } from '@angular/router'; import { inject } from '@angular/core'; import { AuthService } from '../auth.service'; import { catchError, from, map, of, switchMap, take, tap } from 'rxjs'; @@ -29,12 +29,15 @@ import { fullScreenError } from '../../state/error/error.actions'; import { ErrorHelper } from '../error-helper.service'; import { selectLoginConfiguration } from '../../state/login-configuration/login-configuration.selectors'; import { loadLoginConfigurationSuccess } from '../../state/login-configuration/login-configuration.actions'; +import { StorageService } from '../storage.service'; export const authenticationGuard: CanMatchFn = () => { const authService: AuthService = inject(AuthService); const userService: CurrentUserService = inject(CurrentUserService); const errorHelper: ErrorHelper = inject(ErrorHelper); const store: Store = inject(Store); + const router: Router = inject(Router); + const storageService: StorageService = inject(StorageService); const getAuthenticationConfig = store.select(selectLoginConfiguration).pipe( take(1), @@ -77,6 +80,13 @@ export const authenticationGuard: CanMatchFn = () => { }), map(() => true), catchError((errorResponse: HttpErrorResponse) => { + if (errorResponse.status === 401) { + const currentUrl = router.url; + if (currentUrl && currentUrl !== '/login') { + storageService.setReturnUrl(currentUrl); + } + } + if (errorResponse.status !== 401 || authConfigResponse.loginSupported) { store.dispatch(errorHelper.fullScreenError(errorResponse)); } diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/storage.service.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/storage.service.ts new file mode 100644 index 000000000000..da695974ea65 --- /dev/null +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/storage.service.ts @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { Injectable, inject } from '@angular/core'; +import { SessionStorageService } from '@nifi/shared'; + +@Injectable({ + providedIn: 'root' +}) +export class StorageService { + private sessionStorageService = inject(SessionStorageService); + + private static readonly RETURN_URL = 'returnUrl'; + private static readonly CLIENT_ID = 'clientId'; + + public setReturnUrl(url: string): void { + this.sessionStorageService.setItem(StorageService.RETURN_URL, url); + } + + public getReturnUrl(): string | null { + return this.sessionStorageService.getItem(StorageService.RETURN_URL); + } + + public removeReturnUrl(): void { + this.sessionStorageService.removeItem(StorageService.RETURN_URL); + } + + public getClientId(): string | null { + return this.sessionStorageService.getItem(StorageService.CLIENT_ID); + } + + public setClientId(clientId: string): void { + this.sessionStorageService.setItem(StorageService.CLIENT_ID, clientId); + } +} diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/current-user/current-user.effects.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/current-user/current-user.effects.ts index 8bb50a541603..b446db127a9c 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/current-user/current-user.effects.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/current-user/current-user.effects.ts @@ -28,6 +28,7 @@ import { selectLogoutUri } from '../login-configuration/login-configuration.sele import { Router } from '@angular/router'; import { AuthService } from '../../service/auth.service'; import { HttpErrorResponse } from '@angular/common/http'; +import { StorageService } from '../../service/storage.service'; @Injectable() export class CurrentUserEffects { @@ -37,6 +38,7 @@ export class CurrentUserEffects { private userService = inject(CurrentUserService); private authService = inject(AuthService); private errorHelper = inject(ErrorHelper); + private storageService = inject(StorageService); loadCurrentUser$ = createEffect(() => this.actions$.pipe( @@ -75,6 +77,10 @@ export class CurrentUserEffects { this.actions$.pipe( ofType(UserActions.navigateToLogIn), tap(() => { + const currentUrl = window.location.hash.substring(1) || this.router.url; + if (currentUrl && currentUrl !== '/login') { + this.storageService.setReturnUrl(currentUrl); + } this.router.navigate(['/login']); }) ),