mirror of
https://github.com/goharbor/harbor.git
synced 2024-10-03 07:47:54 +02: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>
|
<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" (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="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>
|
<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-action-bar>
|
||||||
<clr-dg-column>{{'USER.COLUMN_NAME' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'USER.COLUMN_NAME' | translate}}</clr-dg-column>
|
||||||
@ -34,5 +35,6 @@
|
|||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
</div>
|
</div>
|
||||||
<new-user-modal (addNew)="addUserToList($event)"></new-user-modal>
|
<new-user-modal (addNew)="addUserToList($event)"></new-user-modal>
|
||||||
|
<change-password></change-password>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -26,6 +26,7 @@ import { AppConfigService } from '../app-config.service';
|
|||||||
import { NewUserModalComponent } from './new-user-modal.component';
|
import { NewUserModalComponent } from './new-user-modal.component';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
import { User } from './user';
|
import { User } from './user';
|
||||||
|
import {ChangePasswordComponent} from "./change-password/change-password.component";
|
||||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "harbor-ui";
|
import {operateChanges, OperateInfo, OperationService, OperationState} from "harbor-ui";
|
||||||
/**
|
/**
|
||||||
* NOTES:
|
* NOTES:
|
||||||
@ -62,6 +63,8 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
private deletionSubscription: Subscription;
|
private deletionSubscription: Subscription;
|
||||||
@ViewChild(NewUserModalComponent)
|
@ViewChild(NewUserModalComponent)
|
||||||
newUserDialog: NewUserModalComponent;
|
newUserDialog: NewUserModalComponent;
|
||||||
|
@ViewChild(ChangePasswordComponent)
|
||||||
|
changePwdDialog: ChangePasswordComponent;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private userService: UserService,
|
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
|
// Filter items by keywords
|
||||||
doFilter(terms: string): void {
|
doFilter(terms: string): void {
|
||||||
this.selectedRow = [];
|
this.selectedRow = [];
|
||||||
|
@ -16,6 +16,7 @@ import { SharedModule } from '../shared/shared.module';
|
|||||||
import { UserComponent } from './user.component';
|
import { UserComponent } from './user.component';
|
||||||
import { NewUserModalComponent } from './new-user-modal.component';
|
import { NewUserModalComponent } from './new-user-modal.component';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
|
import {ChangePasswordComponent} from "./change-password/change-password.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -23,6 +24,7 @@ import { UserService } from './user.service';
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
UserComponent,
|
UserComponent,
|
||||||
|
ChangePasswordComponent,
|
||||||
NewUserModalComponent
|
NewUserModalComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -73,4 +73,19 @@ export class UserService {
|
|||||||
.then(() => null)
|
.then(() => null)
|
||||||
.catch(error => this.handleError(error));
|
.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}}?",
|
"DELETION_SUMMARY": "Do you want to delete user {{param}}?",
|
||||||
"DELETE_SUCCESS": "Users deleted successfully.",
|
"DELETE_SUCCESS": "Users deleted successfully.",
|
||||||
"ITEMS": "items",
|
"ITEMS": "items",
|
||||||
"OF": "of"
|
"OF": "of",
|
||||||
|
"RESET_Ok": "Users password reset successfully"
|
||||||
},
|
},
|
||||||
"PROJECT": {
|
"PROJECT": {
|
||||||
"PROJECTS": "Projects",
|
"PROJECTS": "Projects",
|
||||||
|
@ -135,7 +135,8 @@
|
|||||||
"DELETION_SUMMARY": "¿Quiere eliminar el usuario {{param}}?",
|
"DELETION_SUMMARY": "¿Quiere eliminar el usuario {{param}}?",
|
||||||
"DELETE_SUCCESS": "Usuario eliminado satisfactoriamente.",
|
"DELETE_SUCCESS": "Usuario eliminado satisfactoriamente.",
|
||||||
"ITEMS": "elementos",
|
"ITEMS": "elementos",
|
||||||
"OF": "of"
|
"OF": "of",
|
||||||
|
"RESET_Ok": "Users password reset successfully"
|
||||||
},
|
},
|
||||||
"PROJECT": {
|
"PROJECT": {
|
||||||
"PROJECTS": "Proyectos",
|
"PROJECTS": "Proyectos",
|
||||||
|
@ -118,7 +118,8 @@
|
|||||||
"DELETION_SUMMARY": "Voules-vous supprimer l'utilisateur {{param}}?",
|
"DELETION_SUMMARY": "Voules-vous supprimer l'utilisateur {{param}}?",
|
||||||
"DELETE_SUCCESS": "Utilisateur supprimé avec succès.",
|
"DELETE_SUCCESS": "Utilisateur supprimé avec succès.",
|
||||||
"ITEMS": "items",
|
"ITEMS": "items",
|
||||||
"OF": "de"
|
"OF": "de",
|
||||||
|
"RESET_Ok": "Users password reset successfully"
|
||||||
},
|
},
|
||||||
"PROJECT": {
|
"PROJECT": {
|
||||||
"PROJECTS": "Projets",
|
"PROJECTS": "Projets",
|
||||||
|
@ -135,7 +135,8 @@
|
|||||||
"DELETION_SUMMARY": "你确认删除用户 {{param}}?",
|
"DELETION_SUMMARY": "你确认删除用户 {{param}}?",
|
||||||
"DELETE_SUCCESS": "成功删除用户。",
|
"DELETE_SUCCESS": "成功删除用户。",
|
||||||
"OF": "共计",
|
"OF": "共计",
|
||||||
"ITEMS": "条记录"
|
"ITEMS": "条记录",
|
||||||
|
"RESET_Ok": "成功修改用户密码"
|
||||||
},
|
},
|
||||||
"PROJECT": {
|
"PROJECT": {
|
||||||
"PROJECTS": "项目",
|
"PROJECTS": "项目",
|
||||||
|
Loading…
Reference in New Issue
Block a user