mirror of https://github.com/goharbor/harbor.git
319 lines
10 KiB
TypeScript
319 lines
10 KiB
TypeScript
// Copyright Project Harbor Authors
|
|
//
|
|
// Licensed 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 {
|
|
AfterViewChecked,
|
|
Component,
|
|
Input,
|
|
OnInit,
|
|
ViewChild,
|
|
} from '@angular/core';
|
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
import { NgForm } from '@angular/forms';
|
|
import { SessionService } from '../../shared/services/session.service';
|
|
import { SignUpComponent } from '../sign-up/sign-up.component';
|
|
import { ForgotPasswordComponent } from '../../base/password-setting/forgot-password/forgot-password.component';
|
|
import { AppConfigService } from '../../services/app-config.service';
|
|
import { AppConfig } from '../../services/app-config';
|
|
import { User } from '../../base/left-side-nav/user/user';
|
|
import { CookieOptions, CookieService } from 'ngx-cookie';
|
|
import { SkinableConfig } from '../../services/skinable-config.service';
|
|
import { ModalEvent } from '../../base/modal-event';
|
|
import { modalEvents } from '../../base/modal-events.const';
|
|
import { AboutDialogComponent } from '../../shared/components/about-dialog/about-dialog.component';
|
|
import {
|
|
CommonRoutes,
|
|
CONFIG_AUTH_MODE,
|
|
} from '../../shared/entities/shared.const';
|
|
import { SignInCredential } from './sign-in-credential';
|
|
import { UserPermissionService } from '../../shared/services';
|
|
import { finalize } from 'rxjs/operators';
|
|
|
|
// Define status flags for signing in states
|
|
export const signInStatusNormal = 0;
|
|
export const signInStatusOnGoing = 1;
|
|
export const signInStatusError = -1;
|
|
const remCookieKey = 'rem-username';
|
|
const expireDays = 10;
|
|
|
|
@Component({
|
|
selector: 'sign-in',
|
|
templateUrl: 'sign-in.component.html',
|
|
styleUrls: ['sign-in.component.scss'],
|
|
})
|
|
export class SignInComponent implements AfterViewChecked, OnInit {
|
|
showPwd: boolean = false;
|
|
redirectUrl: string = '';
|
|
// Remeber me indicator
|
|
rememberMe: boolean = false;
|
|
rememberedName: string = '';
|
|
|
|
customLoginBgImg: string;
|
|
customAppTitle: string;
|
|
// Form reference
|
|
signInForm: NgForm;
|
|
@ViewChild('signInForm', { static: true }) currentForm: NgForm;
|
|
@ViewChild('signupDialog') signUpDialog: SignUpComponent;
|
|
@ViewChild('forgotPwdDialog') forgotPwdDialog: ForgotPasswordComponent;
|
|
@ViewChild(AboutDialogComponent) aboutDialog: AboutDialogComponent;
|
|
|
|
// Status flag
|
|
signInStatus: number = signInStatusNormal;
|
|
|
|
// Initialize sign in credential
|
|
@Input() signInCredential: SignInCredential = {
|
|
principal: '',
|
|
password: '',
|
|
};
|
|
isCoreServiceAvailable: boolean = true;
|
|
steps: number = 1;
|
|
hasLoadedAppConfig: boolean = false;
|
|
constructor(
|
|
private router: Router,
|
|
private session: SessionService,
|
|
private route: ActivatedRoute,
|
|
private appConfigService: AppConfigService,
|
|
private cookie: CookieService,
|
|
private skinableConfig: SkinableConfig,
|
|
private userPermissionService: UserPermissionService
|
|
) {}
|
|
|
|
ngOnInit(): void {
|
|
// custom skin
|
|
let customSkinObj = this.skinableConfig.getSkinConfig();
|
|
if (customSkinObj) {
|
|
if (customSkinObj.loginBgImg) {
|
|
this.customLoginBgImg = customSkinObj.loginBgImg;
|
|
}
|
|
if (customSkinObj.loginTitle) {
|
|
this.customAppTitle = customSkinObj.loginTitle;
|
|
}
|
|
}
|
|
this.route.queryParams.subscribe(params => {
|
|
this.redirectUrl = params['redirect_url'] || '';
|
|
let isSignUp = params['sign_up'] || '';
|
|
if (isSignUp !== '') {
|
|
this.signUp(); // Open sign up
|
|
}
|
|
});
|
|
|
|
let remUsername = this.cookie.get(remCookieKey);
|
|
remUsername = remUsername ? remUsername.trim() : '';
|
|
if (remUsername) {
|
|
this.signInCredential.principal = remUsername;
|
|
this.rememberMe = true;
|
|
this.rememberedName = remUsername;
|
|
}
|
|
}
|
|
|
|
// App title
|
|
public get appTitle(): string {
|
|
return 'APP_TITLE.VMW_HARBOR';
|
|
}
|
|
|
|
// For template accessing
|
|
public get isError(): boolean {
|
|
return this.signInStatus === signInStatusError;
|
|
}
|
|
|
|
public get isOnGoing(): boolean {
|
|
return this.signInStatus === signInStatusOnGoing;
|
|
}
|
|
|
|
// Validate the related fields
|
|
public get isValid(): boolean {
|
|
return this.currentForm.form.valid;
|
|
}
|
|
|
|
// Whether show the 'sign up' link
|
|
public get selfSignUp(): boolean {
|
|
return (
|
|
this.appConfigService.getConfig() &&
|
|
this.appConfigService.getConfig().auth_mode ===
|
|
CONFIG_AUTH_MODE.DB_AUTH &&
|
|
this.appConfigService.getConfig().self_registration
|
|
);
|
|
}
|
|
public get isOidcLoginMode(): boolean {
|
|
return (
|
|
this.appConfigService.getConfig() &&
|
|
this.appConfigService.getConfig().auth_mode ===
|
|
CONFIG_AUTH_MODE.OIDC_AUTH
|
|
);
|
|
}
|
|
clickRememberMe($event: any): void {
|
|
if ($event && $event.target) {
|
|
this.rememberMe = $event.target.checked;
|
|
if (!this.rememberMe) {
|
|
// Remove cookie data
|
|
this.cookie.remove(remCookieKey);
|
|
this.rememberedName = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
remeberMe(): void {
|
|
if (this.rememberMe) {
|
|
if (this.rememberedName !== this.signInCredential.principal) {
|
|
// Set expire time
|
|
let expires: number = expireDays * 3600 * 24 * 1000;
|
|
let date = new Date(Date.now() + expires);
|
|
let cookieptions: CookieOptions = {
|
|
path: '/',
|
|
expires: date,
|
|
};
|
|
this.cookie.put(
|
|
remCookieKey,
|
|
this.signInCredential.principal,
|
|
cookieptions
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// General error handler
|
|
handleError(error: any) {
|
|
// Set error status
|
|
this.signInStatus = signInStatusError;
|
|
|
|
let message = error.status
|
|
? error.status + ':' + error.statusText
|
|
: error;
|
|
console.error('An error occurred when signing in:', message);
|
|
}
|
|
|
|
// Hande form values changes
|
|
formChanged() {
|
|
if (this.currentForm === this.signInForm) {
|
|
return;
|
|
}
|
|
this.signInForm = this.currentForm;
|
|
if (this.signInForm) {
|
|
this.signInForm.valueChanges.subscribe(data => {
|
|
this.updateState();
|
|
});
|
|
}
|
|
}
|
|
|
|
// Fill the new user info into the sign in form
|
|
handleUserCreation(user: User): void {
|
|
if (user) {
|
|
this.currentForm.setValue({
|
|
login_username: user.username,
|
|
login_password: '',
|
|
});
|
|
}
|
|
}
|
|
|
|
// Implement interface
|
|
// Watch the view change only when view is in error state
|
|
ngAfterViewChecked() {
|
|
if (this.signInStatus === signInStatusError) {
|
|
this.formChanged();
|
|
}
|
|
}
|
|
|
|
// Update the status if we have done some changes
|
|
updateState(): void {
|
|
if (this.signInStatus === signInStatusError) {
|
|
this.signInStatus = signInStatusNormal; // reset
|
|
}
|
|
}
|
|
|
|
// Trigger the signin action
|
|
signIn(): void {
|
|
// Should validate input firstly
|
|
if (!this.isValid) {
|
|
// Set error status
|
|
this.signInStatus = signInStatusError;
|
|
return;
|
|
}
|
|
|
|
if (this.isOnGoing) {
|
|
// Ongoing, directly return
|
|
return;
|
|
}
|
|
|
|
// Start signing in progress
|
|
this.signInStatus = signInStatusOnGoing;
|
|
|
|
// Call the service to send out the http request
|
|
this.session.signIn(this.signInCredential).subscribe(
|
|
() => {
|
|
// Set status
|
|
// Keep it ongoing to keep the button 'disabled'
|
|
// this.signInStatus = signInStatusNormal;
|
|
// clear permissions cache
|
|
this.userPermissionService.clearPermissionCache();
|
|
// Remeber me
|
|
this.remeberMe();
|
|
|
|
// Redirect to the right router-guard
|
|
if (
|
|
!this.redirectUrl ||
|
|
this.redirectUrl.startsWith(CommonRoutes.EMBEDDED_SIGN_IN)
|
|
) {
|
|
// Routing to the default location
|
|
this.router.navigateByUrl(CommonRoutes.HARBOR_DEFAULT);
|
|
} else {
|
|
this.router.navigateByUrl(this.redirectUrl);
|
|
}
|
|
this.isCoreServiceAvailable = true;
|
|
|
|
// after login successfully: Make sure the updated configuration can be loaded
|
|
this.appConfigService.load().subscribe();
|
|
},
|
|
error => {
|
|
// 403 oidc login no body;
|
|
if (this.isOidcLoginMode && error && error.status === 403) {
|
|
try {
|
|
let redirect_location = '';
|
|
redirect_location =
|
|
error.error && error.error.redirect_location
|
|
? error.error.redirect_location
|
|
: JSON.parse(error.error).redirect_location;
|
|
window.location.href = redirect_location;
|
|
return;
|
|
} catch (error) {}
|
|
}
|
|
// core service is not available for error code 5xx
|
|
if (error && /5[0-9][0-9]/.test(error.status)) {
|
|
this.isCoreServiceAvailable = false;
|
|
}
|
|
this.handleError(error);
|
|
}
|
|
);
|
|
}
|
|
|
|
// Open sign up dialog
|
|
signUp(): void {
|
|
this.signUpDialog.open();
|
|
}
|
|
|
|
// Open forgot password dialog
|
|
forgotPassword(): void {
|
|
this.forgotPwdDialog.open();
|
|
}
|
|
|
|
// Open modal dialog
|
|
openModal(event: ModalEvent): void {
|
|
switch (event.modalName) {
|
|
case modalEvents.ABOUT:
|
|
this.aboutDialog.open();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|