mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 12:15:20 +01:00
Redirect to sign-in page when user session timed out (#16005)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
0a845d6369
commit
21dfba7330
@ -51,3 +51,4 @@ export class SignInService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const UN_LOGGED_PARAM: string = 'publicAndNotLogged';
|
export const UN_LOGGED_PARAM: string = 'publicAndNotLogged';
|
||||||
|
export const YES: string = 'yes';
|
||||||
|
@ -48,7 +48,7 @@ import { errorHandler } from "../../../../../../../shared/units/shared.utils";
|
|||||||
import { ConfirmationDialogComponent } from "../../../../../../../shared/components/confirmation-dialog";
|
import { ConfirmationDialogComponent } from "../../../../../../../shared/components/confirmation-dialog";
|
||||||
import { ConfirmationMessage } from "../../../../../../global-confirmation-dialog/confirmation-message";
|
import { ConfirmationMessage } from "../../../../../../global-confirmation-dialog/confirmation-message";
|
||||||
import { ConfirmationAcknowledgement } from "../../../../../../global-confirmation-dialog/confirmation-state-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 { Label } from "../../../../../../../../../ng-swagger-gen/models/label";
|
||||||
import { LabelService } from "../../../../../../../../../ng-swagger-gen/services/label.service";
|
import { LabelService } from "../../../../../../../../../ng-swagger-gen/services/label.service";
|
||||||
import { EventService, HarborEvent } from "../../../../../../../services/event-service/event.service";
|
import { EventService, HarborEvent } from "../../../../../../../services/event-service/event.service";
|
||||||
@ -59,7 +59,6 @@ export interface LabelState {
|
|||||||
show: boolean;
|
show: boolean;
|
||||||
}
|
}
|
||||||
export const AVAILABLE_TIME = '0001-01-01T00:00:00.000Z';
|
export const AVAILABLE_TIME = '0001-01-01T00:00:00.000Z';
|
||||||
const YES: string = 'yes';
|
|
||||||
const PAGE_SIZE: number = 100;
|
const PAGE_SIZE: number = 100;
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'artifact-list-tab',
|
selector: 'artifact-list-tab',
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { ElementRef } from '@angular/core';
|
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 { Message } from './message';
|
||||||
import { MessageService } from './message.service';
|
|
||||||
import { MessageComponent } from './message.component';
|
import { MessageComponent } from './message.component';
|
||||||
import { AlertType } from "../../entities/shared.const";
|
import { AlertType } from "../../entities/shared.const";
|
||||||
|
import { SharedTestingModule } from "../../shared.module";
|
||||||
|
|
||||||
describe('MessageComponent', () => {
|
describe('MessageComponent', () => {
|
||||||
let component: MessageComponent;
|
let component: MessageComponent;
|
||||||
@ -16,14 +13,10 @@ describe('MessageComponent', () => {
|
|||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
ClarityModule,
|
SharedTestingModule
|
||||||
RouterTestingModule,
|
|
||||||
TranslateModule.forRoot()
|
|
||||||
],
|
],
|
||||||
declarations: [MessageComponent],
|
declarations: [MessageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
MessageService,
|
|
||||||
TranslateService,
|
|
||||||
{provide: ElementRef, useValue: fakeElementRef}
|
{provide: ElementRef, useValue: fakeElementRef}
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@ -19,9 +19,9 @@ import { Message } from './message';
|
|||||||
import { MessageService } from './message.service';
|
import { MessageService } from './message.service';
|
||||||
import { CommonRoutes, dismissInterval, httpStatusCode } from "../../entities/shared.const";
|
import { CommonRoutes, dismissInterval, httpStatusCode } from "../../entities/shared.const";
|
||||||
import { delUrlParam } from "../../units/utils";
|
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({
|
@Component({
|
||||||
selector: 'global-message',
|
selector: 'global-message',
|
||||||
templateUrl: 'message.component.html',
|
templateUrl: 'message.component.html',
|
||||||
@ -44,7 +44,8 @@ export class MessageComponent implements OnInit, OnDestroy {
|
|||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private translate: TranslateService) { }
|
private translate: TranslateService,
|
||||||
|
private session: SessionService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Only subscribe application level message
|
// Only subscribe application level message
|
||||||
@ -53,6 +54,7 @@ export class MessageComponent implements OnInit, OnDestroy {
|
|||||||
message => {
|
message => {
|
||||||
this.globalMessageOpened = true;
|
this.globalMessageOpened = true;
|
||||||
this.globalMessage = message;
|
this.globalMessage = message;
|
||||||
|
this.checkLoginStatus();
|
||||||
this.messageText = message.message;
|
this.messageText = message.message;
|
||||||
|
|
||||||
this.translateMessage(message);
|
this.translateMessage(message);
|
||||||
@ -64,6 +66,7 @@ export class MessageComponent implements OnInit, OnDestroy {
|
|||||||
message => {
|
message => {
|
||||||
this.globalMessageOpened = true;
|
this.globalMessageOpened = true;
|
||||||
this.globalMessage = message;
|
this.globalMessage = message;
|
||||||
|
this.checkLoginStatus();
|
||||||
this.messageText = message.message;
|
this.messageText = message.message;
|
||||||
|
|
||||||
this.translateMessage(message);
|
this.translateMessage(message);
|
||||||
@ -148,4 +151,18 @@ export class MessageComponent implements OnInit, OnDestroy {
|
|||||||
isFromGlobalSearch(): boolean {
|
isFromGlobalSearch(): boolean {
|
||||||
return this.route.snapshot.queryParams[UN_LOGGED_PARAM] === YES;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,7 @@ import { Router } from '@angular/router';
|
|||||||
import { Repository } from '../../../../../ng-swagger-gen/models/repository';
|
import { Repository } from '../../../../../ng-swagger-gen/models/repository';
|
||||||
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||||
import { SessionService } from "../../services/session.service";
|
import { SessionService } from "../../services/session.service";
|
||||||
import { UN_LOGGED_PARAM } from "../../../account/sign-in/sign-in.service";
|
import { UN_LOGGED_PARAM, YES } from "../../../account/sign-in/sign-in.service";
|
||||||
|
|
||||||
const YES: string = 'yes';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'list-repository-ro',
|
selector: 'list-repository-ro',
|
||||||
|
@ -23,6 +23,9 @@ import { Observable, of } from 'rxjs';
|
|||||||
import { map, catchError } from 'rxjs/operators';
|
import { map, catchError } from 'rxjs/operators';
|
||||||
import { ProjectService } from "../services";
|
import { ProjectService } from "../services";
|
||||||
import { CommonRoutes } from "../entities/shared.const";
|
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({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -39,12 +42,12 @@ export class MemberGuard implements CanActivate, CanActivateChild {
|
|||||||
|
|
||||||
const user = this.sessionService.getCurrentUser();
|
const user = this.sessionService.getCurrentUser();
|
||||||
if (user !== null) {
|
if (user !== null) {
|
||||||
return this.hasProjectPerm(state.url, projectId);
|
return this.hasProjectPerm(state.url, projectId, route);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sessionService.retrieveUser().pipe(
|
return this.sessionService.retrieveUser().pipe(
|
||||||
() => {
|
() => {
|
||||||
return this.hasProjectPerm(state.url, projectId);
|
return this.hasProjectPerm(state.url, projectId, route);
|
||||||
},
|
},
|
||||||
catchError(err => {
|
catchError(err => {
|
||||||
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
||||||
@ -57,14 +60,21 @@ export class MemberGuard implements CanActivate, CanActivateChild {
|
|||||||
return this.canActivate(route, state);
|
return this.canActivate(route, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasProjectPerm(url: string, projectId: number): Observable<boolean> {
|
hasProjectPerm(url: string, projectId: number, route: ActivatedRouteSnapshot): Observable<boolean> {
|
||||||
// Note: current user will have the permission to visit the project when the user can get response from GET /projects/:id API.
|
// 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(
|
return this.projectService.getProject(projectId).pipe(
|
||||||
map(() => {
|
map(() => {
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
catchError(err => {
|
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);
|
return of(false);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user