mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-23 09:08:26 +01:00
Merge pull request #5056 from pengpengshui/leftNav
Add function about admin reset user's password
This commit is contained in:
commit
b461e98fde
@ -0,0 +1,49 @@
|
||||
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalClosable]="false">
|
||||
<h3 class="modal-title">{{'RESET_PWD.TITLE' | translate}}</h3>
|
||||
<inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></inline-alert>
|
||||
<div class="modal-body" style="overflow-y: hidden;">
|
||||
<form #resetPwdForm="ngForm" class="form">
|
||||
<section class="form-block">
|
||||
<div class="form-group">
|
||||
<label for="newPassword" class="form-group-label-override">{{'CHANGE_PWD.NEW_PWD' | translate}}</label>
|
||||
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='!getValidationState("newPassword")'>
|
||||
<input type="password" id="newPassword" placeholder='{{"PLACEHOLDER.NEW_PWD" | translate}}'
|
||||
required
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="newPassword"
|
||||
[(ngModel)]="password"
|
||||
#newPassInput="ngModel"
|
||||
size="25"
|
||||
(input)='handleValidation("newPassword", false)'
|
||||
(blur)='handleValidation("newPassword", true)'>
|
||||
<span class="tooltip-content">
|
||||
{{'TOOLTIP.PASSWORD' | translate}}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reNewPassword" class="form-group-label-override">{{'CHANGE_PWD.CONFIRM_PWD' | translate}}</label>
|
||||
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-left" [class.invalid]='!getValidationState("reNewPassword")'>
|
||||
<input type="password" id="reNewPassword" placeholder='{{"PLACEHOLDER.CONFIRM_PWD" | translate}}'
|
||||
required
|
||||
pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{8,20}$"
|
||||
name="reNewPassword"
|
||||
[(ngModel)]="confirmPwd"
|
||||
#reNewPassInput
|
||||
size="25"
|
||||
(input)='handleValidation("reNewPassword", false)'
|
||||
(blur)='handleValidation("reNewPassword", true)'>
|
||||
<span class="tooltip-content">
|
||||
{{'TOOLTIP.CONFIRM_PWD' | translate}}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span class="spinner spinner-inline" style="top:8px;" [hidden]="showProgress === false"></span>
|
||||
<button type="button" class="btn btn-outline" (click)="close()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="send()">{{'BUTTON.OK' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
@ -0,0 +1,3 @@
|
||||
.form .form-block, form .form-block {
|
||||
margin: .5rem 0 2rem 0;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
<clr-dg-action-bar>
|
||||
<button type="button" class="btn btn-sm btn-secondary" (click)="addNewUser()" [disabled]="!canCreateUser"><clr-icon shape="plus" size="16"></clr-icon> {{'USER.ADD_ACTION' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="set-admin" [disabled]="!ifSameRole" (click)="changeAdminRole()" ><clr-icon shape="wrench" size="16"></clr-icon> {{ISADMNISTRATOR | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="changePwd" [disabled]="!(selectedRow.length==1)" (click)="openChangePwdModal()" ><clr-icon shape="edit" size="16"></clr-icon> {{'RESET_PWD.TITLE' | translate | uppercase}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" (click)="deleteUsers(selectedRow)" [disabled]="!selectedRow.length || onlySelf || !canCreateUser"><clr-icon shape="times" size="16"></clr-icon> {{'USER.DEL_ACTION' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column>{{'USER.COLUMN_NAME' | translate}}</clr-dg-column>
|
||||
@ -34,5 +35,6 @@
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
<new-user-modal (addNew)="addUserToList($event)"></new-user-modal>
|
||||
<change-password></change-password>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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 = [];
|
||||
|
@ -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: [
|
||||
|
@ -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<any> {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -135,7 +135,8 @@
|
||||
"DELETION_SUMMARY": "你确认删除用户 {{param}}?",
|
||||
"DELETE_SUCCESS": "成功删除用户。",
|
||||
"OF": "共计",
|
||||
"ITEMS": "条记录"
|
||||
"ITEMS": "条记录",
|
||||
"RESET_Ok": "成功修改用户密码"
|
||||
},
|
||||
"PROJECT": {
|
||||
"PROJECTS": "项目",
|
||||
|
Loading…
Reference in New Issue
Block a user