mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
modernize reset password flow for TUI
This commit is contained in:
11099
frontend/package-lock.json
generated
11099
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,5 @@
|
|||||||
import { Component, Inject } from '@angular/core'
|
import { Component, Inject } from '@angular/core'
|
||||||
import {
|
import { NavController } from '@ionic/angular'
|
||||||
AlertController,
|
|
||||||
LoadingController,
|
|
||||||
ModalController,
|
|
||||||
NavController,
|
|
||||||
ToastController,
|
|
||||||
} from '@ionic/angular'
|
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
@@ -17,6 +11,11 @@ import { OSUpdatePage } from './os-update/os-update.page'
|
|||||||
import { getAllPackages } from 'src/app/util/get-package-data'
|
import { getAllPackages } from 'src/app/util/get-package-data'
|
||||||
import { AuthService } from 'src/app/services/auth.service'
|
import { AuthService } from 'src/app/services/auth.service'
|
||||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
|
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||||
|
import { FormPage } from '../../../modals/form/form.page'
|
||||||
|
import { Config } from '@start9labs/start-sdk/lib/config/builder/config'
|
||||||
|
import { Value } from '@start9labs/start-sdk/lib/config/builder/value'
|
||||||
|
import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec'
|
||||||
import { ConfigService } from 'src/app/services/config.service'
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
import { TuiAlertService, TuiDialogService } from '@taiga-ui/core'
|
import { TuiAlertService, TuiDialogService } from '@taiga-ui/core'
|
||||||
import { PROMPT } from 'src/app/apps/ui/modals/prompt/prompt.component'
|
import { PROMPT } from 'src/app/apps/ui/modals/prompt/prompt.component'
|
||||||
@@ -56,6 +55,7 @@ export class ServerShowPage {
|
|||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly alerts: TuiAlertService,
|
private readonly alerts: TuiAlertService,
|
||||||
private readonly config: ConfigService,
|
private readonly config: ConfigService,
|
||||||
|
private readonly formDialog: FormDialogService,
|
||||||
@Inject(DOCUMENT) private readonly document: Document,
|
@Inject(DOCUMENT) private readonly document: Document,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -98,109 +98,80 @@ export class ServerShowPage {
|
|||||||
this.dialogs.open(new PolymorpheusComponent(OSUpdatePage)).subscribe()
|
this.dialogs.open(new PolymorpheusComponent(OSUpdatePage)).subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentAlertResetPassword() {
|
private presentAlertResetPassword() {
|
||||||
const alert = await this.alertCtrl.create({
|
this.dialogs
|
||||||
header: 'Warning',
|
.open(TUI_PROMPT, {
|
||||||
message:
|
label: 'Warning',
|
||||||
'You will still need your current password to decrypt existing backups!',
|
size: 's',
|
||||||
buttons: [
|
data: {
|
||||||
{
|
content:
|
||||||
text: 'Cancel',
|
'You will still need your current password to decrypt existing backups!',
|
||||||
role: 'cancel',
|
yes: 'Continue',
|
||||||
|
no: 'Cancel',
|
||||||
},
|
},
|
||||||
{
|
})
|
||||||
text: 'Continue',
|
.pipe(filter(Boolean))
|
||||||
handler: () => this.presentModalResetPassword(),
|
.subscribe(() => this.presentModalResetPassword())
|
||||||
cssClass: 'enter-click',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
cssClass: 'alert-warning-message',
|
|
||||||
})
|
|
||||||
|
|
||||||
await alert.present()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentModalResetPassword(): Promise<void> {
|
async presentModalResetPassword(): Promise<void> {
|
||||||
const modal = await this.modalCtrl.create({
|
this.formDialog.open(FormPage, {
|
||||||
component: GenericFormPage,
|
label: 'Change Master Password',
|
||||||
componentProps: {
|
data: {
|
||||||
title: 'Change Master Password',
|
spec: await configBuilderToSpec(passwordSpec),
|
||||||
spec: PasswordSpec,
|
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'Save',
|
text: 'Save',
|
||||||
handler: (value: any) => {
|
handler: (value: PasswordSpec) => this.resetPassword(value),
|
||||||
return this.resetPassword(value)
|
|
||||||
},
|
|
||||||
isSubmit: true,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await modal.present()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async resetPassword(value: {
|
private async resetPassword(value: PasswordSpec): Promise<boolean> {
|
||||||
currPass: string
|
|
||||||
newPass: string
|
|
||||||
newPass2: string
|
|
||||||
}): Promise<boolean> {
|
|
||||||
let err = ''
|
let err = ''
|
||||||
|
|
||||||
if (value.newPass !== value.newPass2) {
|
if (value.newPassword1 !== value.newPassword2) {
|
||||||
err = 'New passwords do not match'
|
err = 'New passwords do not match'
|
||||||
} else if (value.newPass.length < 12) {
|
} else if (value.newPassword1.length < 12) {
|
||||||
err = 'New password must be 12 characters or greater'
|
err = 'New password must be 12 characters or greater'
|
||||||
} else if (value.newPass.length > 64) {
|
} else if (value.newPassword1.length > 64) {
|
||||||
err = 'New password must be less than 65 characters'
|
err = 'New password must be less than 65 characters'
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirm current password is correct
|
// confirm current password is correct
|
||||||
const { 'password-hash': passwordHash } = await getServerInfo(this.patch)
|
const { 'password-hash': passwordHash } = await getServerInfo(this.patch)
|
||||||
try {
|
try {
|
||||||
argon2.verify(passwordHash, value.currPass)
|
argon2.verify(passwordHash, value.currentPassword)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = 'Current password is invalid'
|
err = 'Current password is invalid'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
this.errToast.present(err)
|
this.errorService.handleError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const loader = await this.loadingCtrl.create({
|
const loader = this.loader.open('Saving...').subscribe()
|
||||||
message: 'Changing master password...',
|
|
||||||
})
|
|
||||||
await loader.present()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.embassyApi.resetPassword({
|
await this.embassyApi.resetPassword({
|
||||||
'old-password': value.currPass,
|
'old-password': value.currentPassword,
|
||||||
'new-password': value.newPass,
|
'new-password': value.newPassword1,
|
||||||
})
|
|
||||||
const toast = await this.toastCtrl.create({
|
|
||||||
header: 'Password changed!',
|
|
||||||
position: 'bottom',
|
|
||||||
duration: 2000,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
toast.present()
|
this.alerts.open('Password changed!').subscribe()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.errToast.present(e)
|
this.errorService.handleError(e)
|
||||||
return false
|
return false
|
||||||
} finally {
|
} finally {
|
||||||
loader.dismiss()
|
loader.unsubscribe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEos(): Promise<void> {
|
|
||||||
const modal = await this.modalCtrl.create({
|
|
||||||
component: OSUpdatePage,
|
|
||||||
})
|
|
||||||
modal.present()
|
|
||||||
}
|
|
||||||
|
|
||||||
private presentAlertLogout() {
|
private presentAlertLogout() {
|
||||||
this.dialogs
|
this.dialogs
|
||||||
.open(TUI_PROMPT, {
|
.open(TUI_PROMPT, {
|
||||||
@@ -685,29 +656,28 @@ interface SettingBtn {
|
|||||||
disabled$: Observable<boolean>
|
disabled$: Observable<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
const PasswordSpec: ConfigSpec = {
|
export const passwordSpec = Config.of({
|
||||||
currPass: {
|
currentPassword: Value.text({
|
||||||
type: 'string',
|
|
||||||
name: 'Current Password',
|
name: 'Current Password',
|
||||||
placeholder: 'CurrentPass',
|
required: {
|
||||||
nullable: false,
|
default: null,
|
||||||
|
},
|
||||||
masked: true,
|
masked: true,
|
||||||
copyable: false,
|
}),
|
||||||
},
|
newPassword1: Value.text({
|
||||||
newPass: {
|
|
||||||
type: 'string',
|
|
||||||
name: 'New Password',
|
name: 'New Password',
|
||||||
placeholder: 'NewPass',
|
required: {
|
||||||
nullable: false,
|
default: null,
|
||||||
|
},
|
||||||
masked: true,
|
masked: true,
|
||||||
copyable: false,
|
}),
|
||||||
},
|
newPassword2: Value.text({
|
||||||
newPass2: {
|
|
||||||
type: 'string',
|
|
||||||
name: 'Retype New Password',
|
name: 'Retype New Password',
|
||||||
placeholder: 'NewPass',
|
required: {
|
||||||
nullable: false,
|
default: null,
|
||||||
|
},
|
||||||
masked: true,
|
masked: true,
|
||||||
copyable: false,
|
}),
|
||||||
},
|
})
|
||||||
}
|
|
||||||
|
type PasswordSpec = typeof passwordSpec.validator._TYPE
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ export const mockPatchData: DataModel = {
|
|||||||
password: '',
|
password: '',
|
||||||
tls: true,
|
tls: true,
|
||||||
},
|
},
|
||||||
|
'password-hash':
|
||||||
|
'$argon2d$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$Ceev1I901G6UwU+hY0sHrFZ56D+o+LNJ',
|
||||||
},
|
},
|
||||||
'package-data': {
|
'package-data': {
|
||||||
bitcoind: Mock.bitcoind,
|
bitcoind: Mock.bitcoind,
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export interface ServerInfo {
|
|||||||
'system-start-time': string
|
'system-start-time': string
|
||||||
zram: boolean
|
zram: boolean
|
||||||
smtp: typeof customSmtp.validator._TYPE
|
smtp: typeof customSmtp.validator._TYPE
|
||||||
|
'password-hash': string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StartOsUiInfo = {
|
export type StartOsUiInfo = {
|
||||||
|
|||||||
Reference in New Issue
Block a user