mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
implement password settings
This commit is contained in:
parent
59bea0e365
commit
b92e3b2e7e
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'forgot-password',
|
||||||
|
templateUrl: "forgot-password.component.html"
|
||||||
|
})
|
||||||
|
export class ForgotPasswordComponent {
|
||||||
|
// constructor(private router: Router){}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true">
|
||||||
|
<h3 class="modal-title">Change Password</h3>
|
||||||
|
<div class="modal-body" style="min-height: 250px; overflow-y: hidden;">
|
||||||
|
<form #changepwdForm="ngForm" class="form">
|
||||||
|
<section class="form-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="oldPassword">Current Password</label>
|
||||||
|
<label for="oldPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="oldPassInput.invalid && (oldPassInput.dirty || oldPassInput.touched)">
|
||||||
|
<input type="password" id="oldPassword" placeholder="Enter current password"
|
||||||
|
required
|
||||||
|
name="oldPassword"
|
||||||
|
[(ngModel)]="oldPwd"
|
||||||
|
#oldPassInput="ngModel" size="25">
|
||||||
|
<span class="tooltip-content">
|
||||||
|
Current password is Required.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="newPassword">New Password</label>
|
||||||
|
<label for="newPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="newPassInput.invalid && (newPassInput.dirty || newPassInput.touched)">
|
||||||
|
<input type="password" id="newPassword" placeholder="Enter new password"
|
||||||
|
required
|
||||||
|
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
|
||||||
|
name="newPassword"
|
||||||
|
[(ngModel)]="newPwd"
|
||||||
|
#newPassInput="ngModel" size="25">
|
||||||
|
<span class="tooltip-content">
|
||||||
|
Password should be at least 7 characters with 1 uppercase,lowercase letetr and 1 number
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="reNewPassword">Confirm Password</label>
|
||||||
|
<label for="reNewPassword" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]="(reNewPassInput.invalid && (reNewPassInput.dirty || reNewPassInput.touched)) || (!newPassInput.invalid && reNewPassInput.value != newPassInput.value)">
|
||||||
|
<input type="password" id="reNewPassword" placeholder="Confirm new password"
|
||||||
|
required
|
||||||
|
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{7,}$"
|
||||||
|
name="reNewPassword"
|
||||||
|
[(ngModel)]="reNewPwd"
|
||||||
|
#reNewPassInput="ngModel" size="25">
|
||||||
|
<span class="tooltip-content">
|
||||||
|
Password should be at least 7 characters with 1 uppercase,lowercase letetr and 1 number and consisted with new password
|
||||||
|
</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()">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" [disabled]="!isValid || showProgress" (click)="doOk()">Ok</button>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
@ -0,0 +1,104 @@
|
|||||||
|
import { Component, ViewChild, AfterViewChecked, Output, EventEmitter } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { NgForm } from '@angular/forms';
|
||||||
|
|
||||||
|
import { PasswordSettingService } from './password-setting.service';
|
||||||
|
import { SessionService } from '../../shared/session.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'password-setting',
|
||||||
|
templateUrl: "password-setting.component.html"
|
||||||
|
})
|
||||||
|
export class PasswordSettingComponent implements AfterViewChecked {
|
||||||
|
opened: boolean = false;
|
||||||
|
oldPwd: string = "";
|
||||||
|
newPwd: string = "";
|
||||||
|
reNewPwd: string = "";
|
||||||
|
|
||||||
|
private formValueChanged: boolean = false;
|
||||||
|
private onCalling: boolean = false;
|
||||||
|
|
||||||
|
pwdFormRef: NgForm;
|
||||||
|
@ViewChild("changepwdForm") pwdForm: NgForm;
|
||||||
|
|
||||||
|
@Output() private pwdChange = new EventEmitter<any>();
|
||||||
|
|
||||||
|
constructor(private passwordService: PasswordSettingService, private session: SessionService){}
|
||||||
|
|
||||||
|
//If form is valid
|
||||||
|
public get isValid(): boolean {
|
||||||
|
if (this.pwdForm && this.pwdForm.form.get("newPassword")) {
|
||||||
|
return this.pwdForm.valid &&
|
||||||
|
this.pwdForm.form.get("newPassword").value === this.pwdForm.form.get("reNewPassword").value;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get valueChanged(): boolean {
|
||||||
|
return this.formValueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get showProgress(): boolean {
|
||||||
|
return this.onCalling;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewChecked() {
|
||||||
|
if (this.pwdFormRef != this.pwdForm) {
|
||||||
|
this.pwdFormRef = this.pwdForm;
|
||||||
|
if (this.pwdFormRef) {
|
||||||
|
this.pwdFormRef.valueChanges.subscribe(data => {
|
||||||
|
this.formValueChanged = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Open modal dialog
|
||||||
|
open(): void {
|
||||||
|
this.opened = true;
|
||||||
|
this.pwdForm.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Close the moal dialog
|
||||||
|
close(): void {
|
||||||
|
this.opened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle the ok action
|
||||||
|
doOk(): void {
|
||||||
|
if (this.onCalling) {
|
||||||
|
return;//To avoid duplicate click events
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isValid) {
|
||||||
|
return;//Double confirm
|
||||||
|
}
|
||||||
|
|
||||||
|
//Double confirm session is valid
|
||||||
|
let cUser = this.session.getCurrentUser();
|
||||||
|
if(!cUser){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Call service
|
||||||
|
this.onCalling = true;
|
||||||
|
|
||||||
|
this.passwordService.changePassword(cUser.user_id,
|
||||||
|
{
|
||||||
|
new_password: this.pwdForm.value.newPassword,
|
||||||
|
old_password: this.pwdForm.value.oldPassword
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.onCalling = false;
|
||||||
|
//Tell shell to reset current view
|
||||||
|
this.pwdChange.emit(true);
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.onCalling = false;
|
||||||
|
console.error(error);//TODO:
|
||||||
|
});
|
||||||
|
//TODO:publish the successful message to general messae box
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Headers, Http, RequestOptions } from '@angular/http';
|
||||||
|
import 'rxjs/add/operator/toPromise';
|
||||||
|
|
||||||
|
import { PasswordSetting } from './password-setting';
|
||||||
|
|
||||||
|
const passwordChangeEndpoint = "/api/users/:user_id/password";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PasswordSettingService {
|
||||||
|
private headers: Headers = new Headers({
|
||||||
|
"Accept": 'application/json',
|
||||||
|
"Content-Type": 'application/json'
|
||||||
|
});
|
||||||
|
private options: RequestOptions = new RequestOptions({
|
||||||
|
'headers': this.headers
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(private http: Http) { }
|
||||||
|
|
||||||
|
changePassword(userId: number, setting: PasswordSetting): Promise<any> {
|
||||||
|
if(!setting || setting.new_password.trim()==="" || setting.old_password.trim()===""){
|
||||||
|
return Promise.reject("Invalid data");
|
||||||
|
}
|
||||||
|
|
||||||
|
let putUrl = passwordChangeEndpoint.replace(":user_id", userId+"");
|
||||||
|
return this.http.put(putUrl, JSON.stringify(setting), this.options)
|
||||||
|
.toPromise()
|
||||||
|
.then(() => null)
|
||||||
|
.catch(error=>{
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
harbor-app/src/app/account/password/password-setting.ts
Normal file
11
harbor-app/src/app/account/password/password-setting.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Struct for password change
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class PasswordSetting
|
||||||
|
*/
|
||||||
|
export class PasswordSetting {
|
||||||
|
old_password: string;
|
||||||
|
new_password: string;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<p>Placeholder for signup</p>
|
10
harbor-app/src/app/account/sign-up/sign-up.component.ts
Normal file
10
harbor-app/src/app/account/sign-up/sign-up.component.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'sign-up',
|
||||||
|
templateUrl: "sign-up.component.html"
|
||||||
|
})
|
||||||
|
export class SignUpComponent {
|
||||||
|
// constructor(private router: Router){}
|
||||||
|
}
|
23
harbor-app/src/app/base/base-routing-resolver.service.ts
Normal file
23
harbor-app/src/app/base/base-routing-resolver.service.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
Router, Resolve, ActivatedRouteSnapshot, RouterStateSnapshot
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
|
import { SessionService } from '../shared/session.service';
|
||||||
|
import { SessionUser } from '../shared/session-user';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BaseRoutingResolver implements Resolve<SessionUser> {
|
||||||
|
|
||||||
|
constructor(private session: SessionService, private router: Router) { }
|
||||||
|
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<SessionUser> {
|
||||||
|
return this.session.retrieveUser()
|
||||||
|
.then(sessionUser => {
|
||||||
|
return sessionUser;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.info("Anonymous user");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
2
harbor-app/src/app/base/modal-events.const.ts
Normal file
2
harbor-app/src/app/base/modal-events.const.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const modalAccountSettings= "account-settings";
|
||||||
|
export const modalPasswordSetting = "password-setting";
|
16
harbor-app/src/app/base/navigator/navigator.component.css
Normal file
16
harbor-app/src/app/base/navigator/navigator.component.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.sign-in-override {
|
||||||
|
padding-left: 0px !important;
|
||||||
|
padding-right: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sign-up-override {
|
||||||
|
padding-left: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-divider {
|
||||||
|
display: inline-block;
|
||||||
|
border-right: 2px inset snow;
|
||||||
|
padding: 2px 0px 2px 0px;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 24px;
|
||||||
|
}
|
9
harbor-app/src/app/shared/session-user.ts
Normal file
9
harbor-app/src/app/shared/session-user.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//Define the session user
|
||||||
|
export class SessionUser {
|
||||||
|
user_id: number;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
role_name: string;
|
||||||
|
role_id: number;
|
||||||
|
has_admin_role: number;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user