From 21dfba733013cf2d5b28881e802da305243ed8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E4=B8=96=E5=86=9B?= <30999793+AllForNothing@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:47:49 +0800 Subject: [PATCH] Redirect to sign-in page when user session timed out (#16005) Signed-off-by: AllForNothing --- .../app/account/sign-in/sign-in.service.ts | 1 + .../artifact-list-tab.component.ts | 3 +-- .../global-message/message.component.spec.ts | 11 ++------- .../global-message/message.component.ts | 23 ++++++++++++++++--- .../list-repository-ro.component.ts | 4 +--- .../member-guard-activate.service.ts | 18 +++++++++++---- 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/portal/src/app/account/sign-in/sign-in.service.ts b/src/portal/src/app/account/sign-in/sign-in.service.ts index 275501634..edda24730 100644 --- a/src/portal/src/app/account/sign-in/sign-in.service.ts +++ b/src/portal/src/app/account/sign-in/sign-in.service.ts @@ -51,3 +51,4 @@ export class SignInService { } export const UN_LOGGED_PARAM: string = 'publicAndNotLogged'; +export const YES: string = 'yes'; diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts index c5cb64235..7d5a36296 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts @@ -48,7 +48,7 @@ import { errorHandler } from "../../../../../../../shared/units/shared.utils"; import { ConfirmationDialogComponent } from "../../../../../../../shared/components/confirmation-dialog"; import { ConfirmationMessage } from "../../../../../../global-confirmation-dialog/confirmation-message"; import { ConfirmationAcknowledgement } from "../../../../../../global-confirmation-dialog/confirmation-state-message"; -import { UN_LOGGED_PARAM } from "../../../../../../../account/sign-in/sign-in.service"; +import { UN_LOGGED_PARAM, YES } from "../../../../../../../account/sign-in/sign-in.service"; import { Label } from "../../../../../../../../../ng-swagger-gen/models/label"; import { LabelService } from "../../../../../../../../../ng-swagger-gen/services/label.service"; import { EventService, HarborEvent } from "../../../../../../../services/event-service/event.service"; @@ -59,7 +59,6 @@ export interface LabelState { show: boolean; } export const AVAILABLE_TIME = '0001-01-01T00:00:00.000Z'; -const YES: string = 'yes'; const PAGE_SIZE: number = 100; @Component({ selector: 'artifact-list-tab', diff --git a/src/portal/src/app/shared/components/global-message/message.component.spec.ts b/src/portal/src/app/shared/components/global-message/message.component.spec.ts index df7c9201a..196d7033e 100644 --- a/src/portal/src/app/shared/components/global-message/message.component.spec.ts +++ b/src/portal/src/app/shared/components/global-message/message.component.spec.ts @@ -1,12 +1,9 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; import { ElementRef } from '@angular/core'; -import { RouterTestingModule } from '@angular/router/testing'; -import { TranslateModule, TranslateService } from '@ngx-translate/core'; -import { ClarityModule } from '@clr/angular'; import { Message } from './message'; -import { MessageService } from './message.service'; import { MessageComponent } from './message.component'; import { AlertType } from "../../entities/shared.const"; +import { SharedTestingModule } from "../../shared.module"; describe('MessageComponent', () => { let component: MessageComponent; @@ -16,14 +13,10 @@ describe('MessageComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - ClarityModule, - RouterTestingModule, - TranslateModule.forRoot() + SharedTestingModule ], declarations: [MessageComponent], providers: [ - MessageService, - TranslateService, {provide: ElementRef, useValue: fakeElementRef} ] }).compileComponents(); diff --git a/src/portal/src/app/shared/components/global-message/message.component.ts b/src/portal/src/app/shared/components/global-message/message.component.ts index 7fdc09b36..b6bd3bb6e 100644 --- a/src/portal/src/app/shared/components/global-message/message.component.ts +++ b/src/portal/src/app/shared/components/global-message/message.component.ts @@ -19,9 +19,9 @@ import { Message } from './message'; import { MessageService } from './message.service'; import { CommonRoutes, dismissInterval, httpStatusCode } from "../../entities/shared.const"; import { delUrlParam } from "../../units/utils"; -import { UN_LOGGED_PARAM } from "../../../account/sign-in/sign-in.service"; +import { UN_LOGGED_PARAM, YES } from "../../../account/sign-in/sign-in.service"; +import { SessionService } from "../../services/session.service"; -const YES: string = 'yes'; @Component({ selector: 'global-message', templateUrl: 'message.component.html', @@ -44,7 +44,8 @@ export class MessageComponent implements OnInit, OnDestroy { private messageService: MessageService, private router: Router, private route: ActivatedRoute, - private translate: TranslateService) { } + private translate: TranslateService, + private session: SessionService) { } ngOnInit(): void { // Only subscribe application level message @@ -53,6 +54,7 @@ export class MessageComponent implements OnInit, OnDestroy { message => { this.globalMessageOpened = true; this.globalMessage = message; + this.checkLoginStatus(); this.messageText = message.message; this.translateMessage(message); @@ -64,6 +66,7 @@ export class MessageComponent implements OnInit, OnDestroy { message => { this.globalMessageOpened = true; this.globalMessage = message; + this.checkLoginStatus(); this.messageText = message.message; this.translateMessage(message); @@ -148,4 +151,18 @@ export class MessageComponent implements OnInit, OnDestroy { isFromGlobalSearch(): boolean { return this.route.snapshot.queryParams[UN_LOGGED_PARAM] === YES; } + checkLoginStatus() { + if (this.globalMessage.statusCode === httpStatusCode.Unauthorized) { + // User session timed out, then redirect to sign-in page + if (this.session.getCurrentUser() && !this.isSignInUrl() && this.route.snapshot.queryParams[UN_LOGGED_PARAM] !== YES) { + const url = delUrlParam(this.router.url, UN_LOGGED_PARAM); + this.session.clear(); // because of SignInGuard, must clear user session before navigating to sign-in page + this.router.navigate([ CommonRoutes.EMBEDDED_SIGN_IN ], {queryParams: {redirect_url: url}}); + } + } + } + isSignInUrl(): boolean { + const url: string = this.router.url?.indexOf('?') === -1 ? this.router.url : this.router.url?.split('?')[0]; + return url === CommonRoutes.EMBEDDED_SIGN_IN; + } } diff --git a/src/portal/src/app/shared/components/list-repository-ro/list-repository-ro.component.ts b/src/portal/src/app/shared/components/list-repository-ro/list-repository-ro.component.ts index bb045b8c0..07c114b21 100644 --- a/src/portal/src/app/shared/components/list-repository-ro/list-repository-ro.component.ts +++ b/src/portal/src/app/shared/components/list-repository-ro/list-repository-ro.component.ts @@ -16,9 +16,7 @@ import { Router } from '@angular/router'; import { Repository } from '../../../../../ng-swagger-gen/models/repository'; import { SearchTriggerService } from '../global-search/search-trigger.service'; import { SessionService } from "../../services/session.service"; -import { UN_LOGGED_PARAM } from "../../../account/sign-in/sign-in.service"; - -const YES: string = 'yes'; +import { UN_LOGGED_PARAM, YES } from "../../../account/sign-in/sign-in.service"; @Component({ selector: 'list-repository-ro', diff --git a/src/portal/src/app/shared/router-guard/member-guard-activate.service.ts b/src/portal/src/app/shared/router-guard/member-guard-activate.service.ts index c22cb2c96..080448f25 100644 --- a/src/portal/src/app/shared/router-guard/member-guard-activate.service.ts +++ b/src/portal/src/app/shared/router-guard/member-guard-activate.service.ts @@ -23,6 +23,9 @@ import { Observable, of } from 'rxjs'; import { map, catchError } from 'rxjs/operators'; import { ProjectService } from "../services"; import { CommonRoutes } from "../entities/shared.const"; +import { HttpStatusCode } from '@angular/common/http'; +import { delUrlParam } from "../units/utils"; +import { UN_LOGGED_PARAM, YES } from 'src/app/account/sign-in/sign-in.service'; @Injectable({ providedIn: 'root', @@ -39,12 +42,12 @@ export class MemberGuard implements CanActivate, CanActivateChild { const user = this.sessionService.getCurrentUser(); if (user !== null) { - return this.hasProjectPerm(state.url, projectId); + return this.hasProjectPerm(state.url, projectId, route); } return this.sessionService.retrieveUser().pipe( () => { - return this.hasProjectPerm(state.url, projectId); + return this.hasProjectPerm(state.url, projectId, route); }, catchError(err => { this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); @@ -57,14 +60,21 @@ export class MemberGuard implements CanActivate, CanActivateChild { return this.canActivate(route, state); } - hasProjectPerm(url: string, projectId: number): Observable { + hasProjectPerm(url: string, projectId: number, route: ActivatedRouteSnapshot): Observable { // Note: current user will have the permission to visit the project when the user can get response from GET /projects/:id API. return this.projectService.getProject(projectId).pipe( map(() => { return true; }), catchError(err => { - this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + // User session timed out, then redirect to sign-in page + if (err.status === HttpStatusCode.Unauthorized && route.queryParams[UN_LOGGED_PARAM] !== YES) { + this.sessionService.clear(); // because of SignInGuard, must clear user session before navigating to sign-in page + this.router.navigate([ CommonRoutes.EMBEDDED_SIGN_IN ], + {queryParams: {redirect_url: delUrlParam(this.router.url, UN_LOGGED_PARAM)}}); + } else { + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + } return of(false); }) );