diff --git a/src/ui_ng/src/app/user/change-password/change-password.component.html b/src/ui_ng/src/app/user/change-password/change-password.component.html new file mode 100644 index 000000000..85afee8e0 --- /dev/null +++ b/src/ui_ng/src/app/user/change-password/change-password.component.html @@ -0,0 +1,49 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/user/change-password/change-password.component.scss b/src/ui_ng/src/app/user/change-password/change-password.component.scss new file mode 100644 index 000000000..8e56ad0e7 --- /dev/null +++ b/src/ui_ng/src/app/user/change-password/change-password.component.scss @@ -0,0 +1,3 @@ +.form .form-block, form .form-block { + margin: .5rem 0 2rem 0; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/user/change-password/change-password.component.ts b/src/ui_ng/src/app/user/change-password/change-password.component.ts new file mode 100644 index 000000000..a961f3ba2 --- /dev/null +++ b/src/ui_ng/src/app/user/change-password/change-password.component.ts @@ -0,0 +1,149 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// 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 {Component, ViewChild, ChangeDetectorRef} from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; +import {UserService} from "../user.service"; + +@Component({ + selector: 'change-password', + templateUrl: "change-password.component.html", + styleUrls: ['./change-password.component.scss'] +}) +export class ChangePasswordComponent { + opened: boolean = false; + onGoing: boolean = false; + password: string = ""; + private validationState: any = { + "newPassword": true, + "reNewPassword": true + }; + confirmPwd: string = ""; + userId: number; + + @ViewChild("resetPwdForm") resetPwdForm: NgForm; + @ViewChild(InlineAlertComponent) + inlineAlert: InlineAlertComponent; + + constructor( + private userService: UserService, + private msgHandler: MessageHandlerService, + private ref: ChangeDetectorRef) { } + + public get showProgress(): boolean { + return this.onGoing; + } + + public get isValid(): boolean { + return this.resetPwdForm && this.resetPwdForm.valid && this.samePassword(); + } + + public getValidationState(key: string): boolean { + return this.validationState && + this.validationState[key]; + } + + confirmCancel(event: boolean): void { + this.opened = false; + } + + public open(userId: number): void { + this.onGoing = false; + this.validationState = { + "newPassword": true, + "reNewPassword": true + }; + this.resetPwdForm.resetForm(); + this.inlineAlert.close(); + this.userId = userId; + this.opened = true; + } + + public close(): void { + if (this.password || this.confirmPwd) { + // Need user confirmation + this.inlineAlert.showInlineConfirmation({ + message: "ALERT.FORM_CHANGE_CONFIRMATION" + }); + }else { + this.opened = false; + } + + } + + public send(): void { + // Double confirm to avoid improper situations + if (!this.password || !this.confirmPwd) { + return; + } + + if (!this.isValid) { + return; + } + + this.onGoing = true; + this.userService.changePassword(this.userId, this.password, this.confirmPwd) + .then(() => { + this.onGoing = false; + this.opened = false; + this.msgHandler.showSuccess("USER.RESET_Ok"); + + let hnd = setInterval(() => this.ref.markForCheck(), 100); + setTimeout(() => clearInterval(hnd), 2000); + }) + .catch(error => { + this.onGoing = false; + if (this.msgHandler.isAppLevel(error)) { + this.msgHandler.handleError(error); + this.opened = false; + } else { + this.inlineAlert.showInlineError(error); + } + }); + } + + public handleValidation(key: string, flag: boolean): void { + if (!flag) { + this.validationState[key] = true; + } else { + this.validationState[key] = this.getControlValidationState(key); + if (this.validationState[key]) { + this.validationState["reNewPassword"] = this.samePassword(); + } + } + } + + getControlValidationState(key: string): boolean { + if (this.resetPwdForm) { + let control = this.resetPwdForm.controls[key]; + if (control) { + return control.valid; + } + } + return false; + } + + samePassword(): boolean { + if (this.resetPwdForm) { + let control1 = this.resetPwdForm.controls["newPassword"]; + let control2 = this.resetPwdForm.controls["reNewPassword"]; + if (control1 && control2) { + return control1.value === control2.value; + } + } + return false; + } +} diff --git a/src/ui_ng/src/app/user/user.component.html b/src/ui_ng/src/app/user/user.component.html index 3f7d56fe5..efa19be65 100644 --- a/src/ui_ng/src/app/user/user.component.html +++ b/src/ui_ng/src/app/user/user.component.html @@ -13,6 +13,7 @@ + {{'USER.COLUMN_NAME' | translate}} @@ -34,5 +35,6 @@ + - \ No newline at end of file + diff --git a/src/ui_ng/src/app/user/user.component.ts b/src/ui_ng/src/app/user/user.component.ts index f0a5cf00a..d6924112d 100644 --- a/src/ui_ng/src/app/user/user.component.ts +++ b/src/ui_ng/src/app/user/user.component.ts @@ -26,6 +26,7 @@ import { AppConfigService } from '../app-config.service'; import { NewUserModalComponent } from './new-user-modal.component'; import { UserService } from './user.service'; import { User } from './user'; +import {ChangePasswordComponent} from "./change-password/change-password.component"; import {operateChanges, OperateInfo, OperationService, OperationState} from "harbor-ui"; /** * NOTES: @@ -62,6 +63,8 @@ export class UserComponent implements OnInit, OnDestroy { private deletionSubscription: Subscription; @ViewChild(NewUserModalComponent) newUserDialog: NewUserModalComponent; + @ViewChild(ChangePasswordComponent) + changePwdDialog: ChangePasswordComponent; constructor( private userService: UserService, @@ -163,6 +166,13 @@ export class UserComponent implements OnInit, OnDestroy { } } + openChangePwdModal(): void { + if (this.selectedRow.length === 1) { + this.changePwdDialog.open(this.selectedRow[0].user_id); + } + + } + // Filter items by keywords doFilter(terms: string): void { this.selectedRow = []; diff --git a/src/ui_ng/src/app/user/user.module.ts b/src/ui_ng/src/app/user/user.module.ts index 790d7b2b2..e1e4dcb7c 100644 --- a/src/ui_ng/src/app/user/user.module.ts +++ b/src/ui_ng/src/app/user/user.module.ts @@ -16,6 +16,7 @@ import { SharedModule } from '../shared/shared.module'; import { UserComponent } from './user.component'; import { NewUserModalComponent } from './new-user-modal.component'; import { UserService } from './user.service'; +import {ChangePasswordComponent} from "./change-password/change-password.component"; @NgModule({ imports: [ @@ -23,6 +24,7 @@ import { UserService } from './user.service'; ], declarations: [ UserComponent, + ChangePasswordComponent, NewUserModalComponent ], exports: [ diff --git a/src/ui_ng/src/app/user/user.service.ts b/src/ui_ng/src/app/user/user.service.ts index 2128b3f03..95c56bff0 100644 --- a/src/ui_ng/src/app/user/user.service.ts +++ b/src/ui_ng/src/app/user/user.service.ts @@ -73,4 +73,19 @@ export class UserService { .then(() => null) .catch(error => this.handleError(error)); } + + // admin change normal user pwd + changePassword(uid: number, newPassword: string, confirmPwd: string): Promise { + if (!uid || !newPassword) { + return Promise.reject("Invalid change uid or password"); + } + + return this.http.put(userMgmtEndpoint + '/' + uid + '/password', + {"old_password": newPassword, 'new_password': confirmPwd}, HTTP_JSON_OPTIONS) + .toPromise() + .then(response => response) + .catch(error => { + return Promise.reject(error); + }); + } } diff --git a/src/ui_ng/src/i18n/lang/en-us-lang.json b/src/ui_ng/src/i18n/lang/en-us-lang.json index 989a15ad5..e5ab02b83 100644 --- a/src/ui_ng/src/i18n/lang/en-us-lang.json +++ b/src/ui_ng/src/i18n/lang/en-us-lang.json @@ -135,7 +135,8 @@ "DELETION_SUMMARY": "Do you want to delete user {{param}}?", "DELETE_SUCCESS": "Users deleted successfully.", "ITEMS": "items", - "OF": "of" + "OF": "of", + "RESET_Ok": "Users password reset successfully" }, "PROJECT": { "PROJECTS": "Projects", diff --git a/src/ui_ng/src/i18n/lang/es-es-lang.json b/src/ui_ng/src/i18n/lang/es-es-lang.json index 1e427ee97..892ccf8a5 100644 --- a/src/ui_ng/src/i18n/lang/es-es-lang.json +++ b/src/ui_ng/src/i18n/lang/es-es-lang.json @@ -135,7 +135,8 @@ "DELETION_SUMMARY": "¿Quiere eliminar el usuario {{param}}?", "DELETE_SUCCESS": "Usuario eliminado satisfactoriamente.", "ITEMS": "elementos", - "OF": "of" + "OF": "of", + "RESET_Ok": "Users password reset successfully" }, "PROJECT": { "PROJECTS": "Proyectos", diff --git a/src/ui_ng/src/i18n/lang/fr-fr-lang.json b/src/ui_ng/src/i18n/lang/fr-fr-lang.json index d779dd12a..aaed5b55e 100644 --- a/src/ui_ng/src/i18n/lang/fr-fr-lang.json +++ b/src/ui_ng/src/i18n/lang/fr-fr-lang.json @@ -118,7 +118,8 @@ "DELETION_SUMMARY": "Voules-vous supprimer l'utilisateur {{param}}?", "DELETE_SUCCESS": "Utilisateur supprimé avec succès.", "ITEMS": "items", - "OF": "de" + "OF": "de", + "RESET_Ok": "Users password reset successfully" }, "PROJECT": { "PROJECTS": "Projets", diff --git a/src/ui_ng/src/i18n/lang/zh-cn-lang.json b/src/ui_ng/src/i18n/lang/zh-cn-lang.json index 68c1584f4..f3675cd27 100644 --- a/src/ui_ng/src/i18n/lang/zh-cn-lang.json +++ b/src/ui_ng/src/i18n/lang/zh-cn-lang.json @@ -135,7 +135,8 @@ "DELETION_SUMMARY": "你确认删除用户 {{param}}?", "DELETE_SUCCESS": "成功删除用户。", "OF": "共计", - "ITEMS": "条记录" + "ITEMS": "条记录", + "RESET_Ok": "成功修改用户密码" }, "PROJECT": { "PROJECTS": "项目",