From 97fb0987a05ea71dd016d7535414c3eac90e15bd Mon Sep 17 00:00:00 2001 From: RIHTARSIC Joze <joze.rihtarsic@ext.ec.europa.eu> Date: Mon, 22 May 2023 08:13:07 +0200 Subject: [PATCH] Fix logout issue when session is expired on user settings pages. --- smp-angular/src/app/app.module.ts | 2 ++ .../credential-dialog.component.ts | 20 +++++++++++--- .../error/http-error-handler.service.ts | 26 ++++++++++++++++++ .../src/app/guards/authentication.guard.ts | 5 +--- .../src/app/security/security.service.ts | 3 ++- .../admin-users/admin-user.component.ts | 14 ++++++++++ .../app/system-settings/user/user.service.ts | 27 +++++++++++++++++++ .../user-certificates.component.ts | 26 +++++++++++------- .../sidenav/navigation-model.service.ts | 5 ++++ .../smp/ui/external/TruststoreController.java | 2 +- 10 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 smp-angular/src/app/common/error/http-error-handler.service.ts diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index 3dfcec073..c89ba0dca 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -146,6 +146,7 @@ import {SubresourceDocumentPanelComponent} from "./edit/edit-resources/subresour import {SubresourceDocumentWizardComponent} from "./edit/edit-resources/subresource-document-wizard-dialog/subresource-document-wizard.component"; import {SmpWarningPanelComponent} from "./common/components/smp-warning-panel/smp-warning-panel.component"; import {ManageMembersDialogComponent} from "./common/dialogs/manage-members-dialog/manage-members-dialog.component"; +import {HttpErrorHandlerService} from "./common/error/http-error-handler.service"; @NgModule({ @@ -293,6 +294,7 @@ import {ManageMembersDialogComponent} from "./common/dialogs/manage-members-dial EditDomainService, EditGroupService, EditResourceService, + HttpErrorHandlerService, ExtensionService, GlobalLookups, HttpEventService, diff --git a/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts b/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts index 02add5ab8..c8dfdb244 100644 --- a/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts +++ b/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts @@ -1,4 +1,4 @@ -import {Component, Inject, Output} from '@angular/core'; +import {Component, Inject} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; import {SmpConstants} from "../../../smp.constants"; @@ -7,6 +7,7 @@ import {UserService} from "../../../system-settings/user/user.service"; import {CredentialRo} from "../../../security/credential.model"; import {CertificateRo} from "../../../system-settings/user/certificate-ro.model"; import {CertificateService} from "../../../system-settings/user/certificate.service"; +import {HttpErrorHandlerService} from "../../error/http-error-handler.service"; @Component({ @@ -34,6 +35,7 @@ export class CredentialDialogComponent { constructor(@Inject(MAT_DIALOG_DATA) public data: any, private userService: UserService, + private httpErrorHandlerService: HttpErrorHandlerService, private certificateService: CertificateService, public dialogRef: MatDialogRef<CredentialDialogComponent>, private formBuilder: FormBuilder @@ -138,6 +140,7 @@ export class CredentialDialogComponent { }); if (res.invalid) { this.showErrorMessage(res.invalidReason); + } else { this.clearAlert() } @@ -154,6 +157,10 @@ export class CredentialDialogComponent { }, err => { this.clearCertificateData() + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(err)){ + this.closeDialog(); + return; + } this.showErrorMessage("Error uploading certificate file [" + file.name + "]." + err.error?.errorDescription) } ); @@ -175,6 +182,10 @@ export class CredentialDialogComponent { this.userService.notifyAccessTokenUpdated(response.credential); this.setDisabled(true); }, (err) => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(err)){ + this.closeDialog(); + return; + } this.showErrorMessage(err.error.errorDescription); }); } @@ -208,16 +219,17 @@ export class CredentialDialogComponent { } return null; } + get minSelectableDate(): Date { - return this.credentialType == CredentialDialogComponent.ACCESS_TOKEN_TYPE? new Date():null; + return this.credentialType == CredentialDialogComponent.ACCESS_TOKEN_TYPE ? new Date() : null; } - showSuccessMessage(value:string) { + showSuccessMessage(value: string) { this.message = value; this.messageType = "success"; } - showErrorMessage(value:string) { + showErrorMessage(value: string) { this.message = value; this.messageType = "error"; } diff --git a/smp-angular/src/app/common/error/http-error-handler.service.ts b/smp-angular/src/app/common/error/http-error-handler.service.ts new file mode 100644 index 000000000..217cbe8e4 --- /dev/null +++ b/smp-angular/src/app/common/error/http-error-handler.service.ts @@ -0,0 +1,26 @@ +import {Injectable} from '@angular/core'; +import {Router, NavigationStart, NavigationEnd} from '@angular/router'; +import {Observable, Subject} from 'rxjs'; +import {HttpErrorResponse} from "@angular/common/http"; +import {NavigationService} from "../../window/sidenav/navigation-model.service"; +import {AlertMessageService} from "../alert-message/alert-message.service"; + +@Injectable() +export class HttpErrorHandlerService { + + constructor (private navigationService: NavigationService, + private alertMessageService: AlertMessageService,) { + + } + + public logoutOnInvalidSessionError(err: any): boolean { + if (err instanceof HttpErrorResponse) { + if (err.status === 401) { + this.navigationService.navigateToLogin(); + this.alertMessageService.error(err.error?.errorDescription) + return true; + } + } + return false; + } +} diff --git a/smp-angular/src/app/guards/authentication.guard.ts b/smp-angular/src/app/guards/authentication.guard.ts index d9bfd8769..08ac0dc0d 100644 --- a/smp-angular/src/app/guards/authentication.guard.ts +++ b/smp-angular/src/app/guards/authentication.guard.ts @@ -8,7 +8,6 @@ export const authenticationGuard = () => { const navigationService = inject(NavigationService); const securityService = inject(SecurityService); const alertService = inject(AlertMessageService); - const router = inject(Router); // test if logged in securityService.isAuthenticated(true).subscribe((isAuthenticated: boolean) => { @@ -17,9 +16,7 @@ export const authenticationGuard = () => { } else { alertService.error('You have been logged out because of inactivity or missing access permissions.', true); // Redirect to the login page - navigationService.reset(); - router.navigate(['/login'], {queryParams: {returnUrl: router.url}}); - router.parseUrl('/login'); + navigationService.navigateToLogin(); } }); }; diff --git a/smp-angular/src/app/security/security.service.ts b/smp-angular/src/app/security/security.service.ts index 2a14888d9..464a3ecfc 100644 --- a/smp-angular/src/app/security/security.service.ts +++ b/smp-angular/src/app/security/security.service.ts @@ -2,7 +2,7 @@ import {Observable, ReplaySubject} from 'rxjs'; import {User} from './user.model'; import {SecurityEventService} from './security-event.service'; -import {HttpClient, HttpHeaders} from '@angular/common/http'; +import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http'; import {SmpConstants} from "../smp.constants"; import {Authority} from "./authority.model"; import {AlertMessageService} from "../common/alert-message/alert-message.service"; @@ -169,4 +169,5 @@ export class SecurityService { private clearLocalStorage() { localStorage.removeItem(this.LOCAL_STORAGE_KEY_CURRENT_USER); } + } diff --git a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts index 5ccabc9a1..e7cf2b366 100644 --- a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts +++ b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts @@ -16,6 +16,7 @@ import { } from "../../common/dialogs/password-change-dialog/password-change-dialog.component"; import {UserDetailsDialogMode} from "../user/user-details-dialog/user-details-dialog.component"; import {ApplicationRoleEnum} from "../../common/enums/application-role.enum"; +import {HttpErrorHandlerService} from "../../common/error/http-error-handler.service"; @Component({ @@ -39,6 +40,7 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { @ViewChild(MatPaginator) paginator: MatPaginator; constructor(private adminUserService: AdminUserService, + private httpErrorHandlerService: HttpErrorHandlerService, private securityService: SecurityService, private alertService: AlertMessageService, private dialog: MatDialog) { @@ -127,6 +129,9 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { this.selected = selectUser; } }, (error) => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -149,6 +154,9 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { this.alertService.success("User [" + user.username + "] updated!"); } }, (error) => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -163,6 +171,9 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { this.alertService.success("User [" + user.username + "] created!"); } }, (error) => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -192,6 +203,9 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { this.alertService.success("User [" + user.username + "] deleted!"); } }, (error) => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + return; + } this.alertService.error(error.error?.errorDescription) }); diff --git a/smp-angular/src/app/system-settings/user/user.service.ts b/smp-angular/src/app/system-settings/user/user.service.ts index c18c5d345..e57165373 100644 --- a/smp-angular/src/app/system-settings/user/user.service.ts +++ b/smp-angular/src/app/system-settings/user/user.service.ts @@ -7,6 +7,7 @@ import {SecurityService} from "../../security/security.service"; import {Observable, Subject} from "rxjs"; import {CredentialRo} from "../../security/credential.model"; import {AccessTokenRo} from "../../common/dialogs/access-token-generation-dialog/access-token-ro.model"; +import {HttpErrorHandlerService} from "../../common/error/http-error-handler.service"; /** * Class handle current user settings such-as profile, credentials, DomiSMP settings... , @@ -27,6 +28,7 @@ export class UserService { constructor( private http: HttpClient, + private httpErrorHandlerService: HttpErrorHandlerService, private securityService: SecurityService, private alertService: AlertMessageService, ) { @@ -53,6 +55,9 @@ export class UserService { .subscribe((response: CredentialRo) => { this.notifyPwdStatusUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -67,6 +72,9 @@ export class UserService { .subscribe((response: CredentialRo[]) => { this.notifyAccessTokensUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -82,6 +90,9 @@ export class UserService { .subscribe((response: CredentialRo) => { this.notifyAccessTokenUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -97,6 +108,9 @@ export class UserService { .subscribe((response: CredentialRo) => { this.notifyAccessTokenUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -112,6 +126,9 @@ export class UserService { .subscribe((response: CredentialRo) => { this.notifyCertificateUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -127,6 +144,9 @@ export class UserService { .subscribe((response: CredentialRo) => { this.notifyCertificateUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) }); } @@ -153,7 +173,11 @@ export class UserService { .subscribe((response: CredentialRo) => { this.notifyCertificateUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) + }); } @@ -170,6 +194,9 @@ export class UserService { .subscribe((response: CredentialRo[]) => { this.notifyCertificatesUpdated(response) }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)){ + return; + } this.alertService.error(error.error?.errorDescription) }); } diff --git a/smp-angular/src/app/user-settings/user-certificates/user-certificates.component.ts b/smp-angular/src/app/user-settings/user-certificates/user-certificates.component.ts index 8cf924c27..bd09fd9e6 100644 --- a/smp-angular/src/app/user-settings/user-certificates/user-certificates.component.ts +++ b/smp-angular/src/app/user-settings/user-certificates/user-certificates.component.ts @@ -9,6 +9,7 @@ import {CertificateDialogComponent} from "../../common/dialogs/certificate-dialo import {CredentialDialogComponent} from "../../common/dialogs/credential-dialog/credential-dialog.component"; import {BeforeLeaveGuard} from "../../window/sidenav/navigation-on-leave-guard"; import {UserCertificatePanelComponent} from "./user-certificate-panel/user-certificate-panel.component"; +import {HttpErrorHandlerService} from "../../common/error/http-error-handler.service"; @Component({ @@ -23,6 +24,7 @@ export class UserCertificatesComponent implements BeforeLeaveGuard { userCertificateCredentialComponents: QueryList<UserCertificatePanelComponent>; constructor(private securityService: SecurityService, + private httpErrorHandlerService: HttpErrorHandlerService, private userService: UserService, public dialog: MatDialog) { @@ -78,21 +80,27 @@ export class UserCertificatesComponent implements BeforeLeaveGuard { } public createNew() { - this.dialog.open(CredentialDialogComponent,{ - data:{ + this.dialog.open(CredentialDialogComponent, { + data: { credentialType: CredentialDialogComponent.CERTIFICATE_TYPE, formTitle: "Import certificate dialog" } - } ).afterClosed(); + }).afterClosed(); } + public onShowItemClicked(credential: CredentialRo) { - this.userService.getUserCertificateCredentialObservable(credential).subscribe((response: CredentialRo) => { - this.dialog.open(CertificateDialogComponent, { - data: {row: response.certificate} + this.userService.getUserCertificateCredentialObservable(credential) + .subscribe((response: CredentialRo) => { + this.dialog.open(CertificateDialogComponent, { + data: {row: response.certificate} + }); + + }, error => { + if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + return; + } }); - - }); } @@ -119,7 +127,7 @@ export class UserCertificatesComponent implements BeforeLeaveGuard { } isDirty(): boolean { - let dirtyComp = !this.userCertificateCredentialComponents?null: this.userCertificateCredentialComponents.find(cmp => cmp.isDirty()) + let dirtyComp = !this.userCertificateCredentialComponents ? null : this.userCertificateCredentialComponents.find(cmp => cmp.isDirty()) return !!dirtyComp; } } diff --git a/smp-angular/src/app/window/sidenav/navigation-model.service.ts b/smp-angular/src/app/window/sidenav/navigation-model.service.ts index a249df5d8..4ed4f2dd3 100644 --- a/smp-angular/src/app/window/sidenav/navigation-model.service.ts +++ b/smp-angular/src/app/window/sidenav/navigation-model.service.ts @@ -317,4 +317,9 @@ export class NavigationService extends MatTreeNestedDataSource<NavigationNode> { return false; } + public navigateToLogin(): void { + this.reset(); + this.router.navigate(['/login'], {queryParams: {returnUrl: this.router.url}}); + this.router.parseUrl('/login'); + } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreController.java index a54f28b18..7d442e03b 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreController.java @@ -30,7 +30,7 @@ public class TruststoreController { this.payloadValidatorService = payloadValidatorService; } - @PreAuthorize("@smpAuthorizationService.systemAdministrator || @smpAuthorizationService.isCurrentlyLoggedIn(#userId)") + @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userId)") @PostMapping(path = "/{user-id}/validate-certificate", consumes = MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE) public CertificateRO validateCertificate(@PathVariable("user-id") String userId, @RequestBody byte[] data) { LOG.info("Got certificate data size: {}", data.length); -- GitLab