add reset password to UI (#2341)

This commit is contained in:
Matt Hill
2023-07-11 17:23:40 -06:00
committed by GitHub
parent 1d3d70e8d6
commit 4676f0595c
5 changed files with 158 additions and 19 deletions

View File

@@ -24,6 +24,9 @@ import {
import { ConfigService } from 'src/app/services/config.service'
import { DOCUMENT } from '@angular/common'
import { getServerInfo } from 'src/app/util/get-server-info'
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
import { ConfigSpec } from 'src/app/pkg-config/config-types'
import * as argon2 from '@start9labs/argon2'
@Component({
selector: 'server-show',
@@ -82,6 +85,102 @@ export class ServerShowPage {
await modal.present()
}
async presentAlertResetPassword() {
const alert = await this.alertCtrl.create({
header: 'Warning',
message:
'You will still need your current password to decrypt existing backups!',
buttons: [
{
text: 'Cancel',
role: 'cancel',
},
{
text: 'Continue',
handler: () => this.presentModalResetPassword(),
cssClass: 'enter-click',
},
],
cssClass: 'alert-warning-message',
})
await alert.present()
}
async presentModalResetPassword(): Promise<void> {
const modal = await this.modalCtrl.create({
component: GenericFormPage,
componentProps: {
title: 'Change Master Password',
spec: PasswordSpec,
buttons: [
{
text: 'Save',
handler: (value: any) => {
return this.resetPassword(value)
},
isSubmit: true,
},
],
},
})
await modal.present()
}
private async resetPassword(value: {
currPass: string
newPass: string
newPass2: string
}): Promise<boolean> {
let err = ''
if (value.newPass !== value.newPass2) {
err = 'New passwords do not match'
} else if (value.newPass.length < 12) {
err = 'New password must be 12 characters or greater'
} else if (value.newPass.length > 64) {
err = 'New password must be less than 65 characters'
}
// confirm current password is correct
const { 'password-hash': passwordHash } = await getServerInfo(this.patch)
try {
argon2.verify(passwordHash, value.currPass)
} catch (e) {
err = 'Current password is invalid'
}
if (err) {
this.errToast.present(err)
return false
}
const loader = await this.loadingCtrl.create({
message: 'Changing master password...',
})
await loader.present()
try {
await this.embassyApi.resetPassword({
'old-password': value.currPass,
'new-password': value.newPass,
})
const toast = await this.toastCtrl.create({
header: 'Password changed!',
position: 'bottom',
duration: 2000,
})
toast.present()
return true
} catch (e: any) {
this.errToast.present(e)
return false
} finally {
loader.dismiss()
}
}
async updateEos(): Promise<void> {
const modal = await this.modalCtrl.create({
component: OSUpdatePage,
@@ -436,6 +535,14 @@ export class ServerShowPage {
detail: true,
disabled$: of(false),
},
{
title: 'Change Master Password',
description: `Change your StartOS master password`,
icon: 'key-outline',
action: () => this.presentAlertResetPassword(),
detail: false,
disabled$: of(!this.secure),
},
{
title: 'Experimental Features',
description: 'Try out new and potentially unstable new features',
@@ -530,11 +637,7 @@ export class ServerShowPage {
description: 'Get help from the Start9 team and community',
icon: 'chatbubbles-outline',
action: () =>
window.open(
'https://start9.com/contact',
'_blank',
'noreferrer',
),
window.open('https://start9.com/contact', '_blank', 'noreferrer'),
detail: true,
disabled$: of(false),
},
@@ -636,3 +739,30 @@ interface SettingBtn {
detail: boolean
disabled$: Observable<boolean>
}
const PasswordSpec: ConfigSpec = {
currPass: {
type: 'string',
name: 'Current Password',
placeholder: 'CurrentPass',
nullable: false,
masked: true,
copyable: false,
},
newPass: {
type: 'string',
name: 'New Password',
placeholder: 'NewPass',
nullable: false,
masked: true,
copyable: false,
},
newPass2: {
type: 'string',
name: 'Retype New Password',
placeholder: 'NewPass',
nullable: false,
masked: true,
copyable: false,
},
}

View File

@@ -30,6 +30,12 @@ export module RR {
export type LogoutReq = {} // auth.logout
export type LogoutRes = null
export type ResetPasswordReq = {
'old-password': string
'new-password': string
} // auth.reset-password
export type ResetPasswordRes = null
// server
export type EchoReq = { message: string } // server.echo
@@ -84,11 +90,6 @@ export module RR {
export type KillSessionsReq = { ids: string[] } // sessions.kill
export type KillSessionsRes = null
// password
export type UpdatePasswordReq = { password: string } // password.set
export type UpdatePasswordRes = null
// notification
export type GetNotificationsReq = {

View File

@@ -51,6 +51,10 @@ export abstract class ApiService {
abstract killSessions(params: RR.KillSessionsReq): Promise<RR.KillSessionsRes>
abstract resetPassword(
params: RR.ResetPasswordReq,
): Promise<RR.ResetPasswordRes>
// server
abstract echo(params: RR.EchoReq): Promise<RR.EchoRes>
@@ -126,9 +130,6 @@ export abstract class ApiService {
abstract getEos(): Promise<RR.GetMarketplaceEosRes>
// password
// abstract updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes>
// notification
abstract getNotifications(

View File

@@ -94,6 +94,12 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'auth.session.kill', params })
}
async resetPassword(
params: RR.ResetPasswordReq,
): Promise<RR.ResetPasswordRes> {
return this.rpcRequest({ method: 'auth.reset-password', params })
}
// server
async echo(params: RR.EchoReq): Promise<RR.EchoRes> {

View File

@@ -156,6 +156,13 @@ export class MockApiService extends ApiService {
return null
}
async resetPassword(
params: RR.ResetPasswordReq,
): Promise<RR.ResetPasswordRes> {
await pauseFor(2000)
return null
}
// server
async echo(params: RR.EchoReq): Promise<RR.EchoRes> {
@@ -377,12 +384,6 @@ export class MockApiService extends ApiService {
return Mock.MarketplaceEos
}
// password
// async updatePassword (params: RR.UpdatePasswordReq): Promise<RR.UpdatePasswordRes> {
// await pauseFor(2000)
// return null
// }
// notification
async getNotifications(