fix: fix wrong password messaging

This commit is contained in:
waterplea
2024-07-12 11:14:45 +05:00
parent d235ebaac9
commit 0f5cec0a60
9 changed files with 170 additions and 115 deletions

94
web/package-lock.json generated
View File

@@ -28,12 +28,12 @@
"@start9labs/argon2": "^0.2.2", "@start9labs/argon2": "^0.2.2",
"@start9labs/emver": "^0.1.5", "@start9labs/emver": "^0.1.5",
"@start9labs/start-sdk": "file:../sdk/dist", "@start9labs/start-sdk": "file:../sdk/dist",
"@taiga-ui/addon-charts": "3.84.0", "@taiga-ui/addon-charts": "3.86.0",
"@taiga-ui/cdk": "3.84.0", "@taiga-ui/cdk": "3.86.0",
"@taiga-ui/core": "3.84.0", "@taiga-ui/core": "3.86.0",
"@taiga-ui/experimental": "3.84.0", "@taiga-ui/experimental": "3.86.0",
"@taiga-ui/icons": "3.84.0", "@taiga-ui/icons": "3.86.0",
"@taiga-ui/kit": "3.84.0", "@taiga-ui/kit": "3.86.0",
"@tinkoff/ng-dompurify": "4.0.0", "@tinkoff/ng-dompurify": "4.0.0",
"@tinkoff/ng-event-plugins": "3.2.0", "@tinkoff/ng-event-plugins": "3.2.0",
"angular-svg-round-progressbar": "^9.0.0", "angular-svg-round-progressbar": "^9.0.0",
@@ -4128,9 +4128,9 @@
} }
}, },
"node_modules/@taiga-ui/addon-charts": { "node_modules/@taiga-ui/addon-charts": {
"version": "3.84.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.84.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.86.0.tgz",
"integrity": "sha512-XR7UFywnrv4NRLHOCbba63gXDYYDL4Rt0MbjnF54p5U2EXnbt2of7VbjlB6cPx40XkQqfqa3CNayYxWZP82Ijg==", "integrity": "sha512-Du/85qqaj8hpFSI6hPuFeIhtE93Z6WSkYZLt0gvnsaCb2qSAg8D4oHSogrtF1rsWGGoM+fvXjD7UEUw9GzFIPg==",
"dependencies": { "dependencies": {
"tslib": "^2.6.2" "tslib": "^2.6.2"
}, },
@@ -4138,15 +4138,15 @@
"@angular/common": ">=12.0.0", "@angular/common": ">=12.0.0",
"@angular/core": ">=12.0.0", "@angular/core": ">=12.0.0",
"@ng-web-apis/common": "^3.0.6", "@ng-web-apis/common": "^3.0.6",
"@taiga-ui/cdk": "^3.84.0", "@taiga-ui/cdk": "^3.86.0",
"@taiga-ui/core": "^3.84.0", "@taiga-ui/core": "^3.86.0",
"@tinkoff/ng-polymorpheus": "^4.3.0" "@tinkoff/ng-polymorpheus": "^4.3.0"
} }
}, },
"node_modules/@taiga-ui/addon-commerce": { "node_modules/@taiga-ui/addon-commerce": {
"version": "3.84.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-3.84.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-3.86.0.tgz",
"integrity": "sha512-1zqLwnZLAYYcHvjH89d7JmtV2+QeZ2YnSJ3YWEMNLjGPzpev4RvQXtDfglIyu0LCyTxqpXmuzes9v/cgq2P5TQ==", "integrity": "sha512-8QSB490ckI4jnU+1sQ3x8os2GVE162hbvzPVYIZ0TruoeXl076dAz6PT2WRaFwjcaCAIGsuaQgQ4Cv02NjkiYQ==",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.6.2" "tslib": "^2.6.2"
@@ -4159,18 +4159,18 @@
"@maskito/core": "^1.9.0", "@maskito/core": "^1.9.0",
"@maskito/kit": "^1.9.0", "@maskito/kit": "^1.9.0",
"@ng-web-apis/common": "^3.0.6", "@ng-web-apis/common": "^3.0.6",
"@taiga-ui/cdk": "^3.84.0", "@taiga-ui/cdk": "^3.86.0",
"@taiga-ui/core": "^3.84.0", "@taiga-ui/core": "^3.86.0",
"@taiga-ui/i18n": "^3.84.0", "@taiga-ui/i18n": "^3.86.0",
"@taiga-ui/kit": "^3.84.0", "@taiga-ui/kit": "^3.86.0",
"@tinkoff/ng-polymorpheus": "^4.3.0", "@tinkoff/ng-polymorpheus": "^4.3.0",
"rxjs": ">=6.0.0" "rxjs": ">=6.0.0"
} }
}, },
"node_modules/@taiga-ui/cdk": { "node_modules/@taiga-ui/cdk": {
"version": "3.84.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.84.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.86.0.tgz",
"integrity": "sha512-0umw/CUmYNEYOCUNQVTQS53zXzxZsH/6+lj1mFVzocvfJFJWAUT6ltCH9QvxYmxSDDGWwNGg16AaVo2K+aGL0w==", "integrity": "sha512-aVbnW01Oh0Er1sHKVGHP8W05mOSKxjSzFE3Qx4iF4T6KW7Rlz9HZoNx5ADMg0TATYChtWh9Kwjo8I4LSVj2ZUw==",
"dependencies": { "dependencies": {
"@ng-web-apis/common": "3.0.6", "@ng-web-apis/common": "3.0.6",
"@ng-web-apis/mutation-observer": "3.1.0", "@ng-web-apis/mutation-observer": "3.1.0",
@@ -4221,11 +4221,11 @@
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
}, },
"node_modules/@taiga-ui/core": { "node_modules/@taiga-ui/core": {
"version": "3.84.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.84.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.86.0.tgz",
"integrity": "sha512-FZy77z0E4qjYcszVcp+qPFkPwJPl8qXZb7t2P+juUtJvSmSn2foQHHdyhbIYN808H26tqCdgkTMG1BWQxVuDSg==", "integrity": "sha512-diQKOnPtDDfxPOMk6wLRq8nyDVfNSPSNy+1TeyqzUgOvJ6XAjfaBXGsL3iuR7AN8+sz/b3rJmBce+vdw6FjMLQ==",
"dependencies": { "dependencies": {
"@taiga-ui/i18n": "^3.84.0", "@taiga-ui/i18n": "^3.86.0",
"tslib": "^2.6.2" "tslib": "^2.6.2"
}, },
"peerDependencies": { "peerDependencies": {
@@ -4237,35 +4237,35 @@
"@angular/router": ">=12.0.0", "@angular/router": ">=12.0.0",
"@ng-web-apis/common": "^3.0.6", "@ng-web-apis/common": "^3.0.6",
"@ng-web-apis/mutation-observer": "^3.1.0", "@ng-web-apis/mutation-observer": "^3.1.0",
"@taiga-ui/cdk": "^3.84.0", "@taiga-ui/cdk": "^3.86.0",
"@taiga-ui/i18n": "^3.84.0", "@taiga-ui/i18n": "^3.86.0",
"@tinkoff/ng-event-plugins": "^3.2.0", "@tinkoff/ng-event-plugins": "^3.2.0",
"@tinkoff/ng-polymorpheus": "^4.3.0", "@tinkoff/ng-polymorpheus": "^4.3.0",
"rxjs": ">=6.0.0" "rxjs": ">=6.0.0"
} }
}, },
"node_modules/@taiga-ui/experimental": { "node_modules/@taiga-ui/experimental": {
"version": "3.84.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-3.84.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-3.86.0.tgz",
"integrity": "sha512-q0hNVy+EmywCG8hpZlg/+haKIFhnmxicQiSeV/D1P7CHO10safjGo0ptT6e1hYMFa5/cJZOM4OwDPen2xs17Wg==", "integrity": "sha512-ACjoRVeX5MgsNJsiu2ukliXLD2mfEWm8Vtmk78vqcnkyPUmy1ZWK4sG3p5ybFN8AdIMHkblVq0l+x2qAwr/+LQ==",
"dependencies": { "dependencies": {
"tslib": "^2.6.2" "tslib": "^2.6.2"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": ">=12.0.0", "@angular/common": ">=12.0.0",
"@angular/core": ">=12.0.0", "@angular/core": ">=12.0.0",
"@taiga-ui/addon-commerce": "^3.84.0", "@taiga-ui/addon-commerce": "^3.86.0",
"@taiga-ui/cdk": "^3.84.0", "@taiga-ui/cdk": "^3.86.0",
"@taiga-ui/core": "^3.84.0", "@taiga-ui/core": "^3.86.0",
"@taiga-ui/kit": "^3.84.0", "@taiga-ui/kit": "^3.86.0",
"@tinkoff/ng-polymorpheus": "^4.3.0", "@tinkoff/ng-polymorpheus": "^4.3.0",
"rxjs": ">=6.0.0" "rxjs": ">=6.0.0"
} }
}, },
"node_modules/@taiga-ui/i18n": { "node_modules/@taiga-ui/i18n": {
"version": "3.85.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.85.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.86.0.tgz",
"integrity": "sha512-CGoxfq9WY+psX5ZOfWmuQZ6OA/0CAPYJTlbHkw5sRKAyhEQ3NM/Wbx3xcwrcYRRJDnt9yOlfibz+3a+WDF2bFA==", "integrity": "sha512-8zkNhMo/QtxZ2Zp6EP/nxo4SOLwaIrX+P3X/Wt+1cjFNZUYWWfdvfHLLdNviKFPVl4RAOxvkhDfza/wkrwv+iQ==",
"dependencies": { "dependencies": {
"tslib": "^2.6.2" "tslib": "^2.6.2"
}, },
@@ -4276,20 +4276,20 @@
} }
}, },
"node_modules/@taiga-ui/icons": { "node_modules/@taiga-ui/icons": {
"version": "3.84.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.84.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.86.0.tgz",
"integrity": "sha512-KiH7BJRZ6wbkOHlJAS0XHq2gYnQTpRgdEogKW+GoD0da/4trCdM66vhDk2j0DwDFdBGq5U0inHJCjnskBI1nSQ==", "integrity": "sha512-jVBEbvE/r9JG+knmXMTn/l/js3JjYi8nSGbrLCryJZZoS2izRnQARN2txABieUJm8H463CoF0rcdXlHKRuA4Ew==",
"dependencies": { "dependencies": {
"tslib": "^2.6.2" "tslib": "^2.6.2"
}, },
"peerDependencies": { "peerDependencies": {
"@taiga-ui/cdk": "^3.84.0" "@taiga-ui/cdk": "^3.86.0"
} }
}, },
"node_modules/@taiga-ui/kit": { "node_modules/@taiga-ui/kit": {
"version": "3.84.0", "version": "3.86.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.84.0.tgz", "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.86.0.tgz",
"integrity": "sha512-lSUPDco5FeBYK3ESnXeEPLCdMCmNXwcdHNK/we+0ZoH4VPx/OGg2hpEP0Fej7jfGHwXFTzDbufQD0hT6WlfTAw==", "integrity": "sha512-naAy4pyhCaQ9+vWxqSMjbV+9KwnMxT5ybrw+MAJgMn2evzRq0FjqzyFZFog7oiRbRvgVdoWPQfBNKaaLhJcpsw==",
"dependencies": { "dependencies": {
"@maskito/angular": "1.9.0", "@maskito/angular": "1.9.0",
"@maskito/core": "1.9.0", "@maskito/core": "1.9.0",
@@ -4306,9 +4306,9 @@
"@ng-web-apis/common": "3.0.6", "@ng-web-apis/common": "3.0.6",
"@ng-web-apis/mutation-observer": "^3.1.0", "@ng-web-apis/mutation-observer": "^3.1.0",
"@ng-web-apis/resize-observer": "^3.0.6", "@ng-web-apis/resize-observer": "^3.0.6",
"@taiga-ui/cdk": "^3.84.0", "@taiga-ui/cdk": "^3.86.0",
"@taiga-ui/core": "^3.84.0", "@taiga-ui/core": "^3.86.0",
"@taiga-ui/i18n": "^3.84.0", "@taiga-ui/i18n": "^3.86.0",
"@tinkoff/ng-polymorpheus": "^4.3.0", "@tinkoff/ng-polymorpheus": "^4.3.0",
"rxjs": ">=6.0.0" "rxjs": ">=6.0.0"
} }

View File

@@ -51,12 +51,12 @@
"@start9labs/argon2": "^0.2.2", "@start9labs/argon2": "^0.2.2",
"@start9labs/emver": "^0.1.5", "@start9labs/emver": "^0.1.5",
"@start9labs/start-sdk": "file:../sdk/dist", "@start9labs/start-sdk": "file:../sdk/dist",
"@taiga-ui/addon-charts": "3.84.0", "@taiga-ui/addon-charts": "3.86.0",
"@taiga-ui/cdk": "3.84.0", "@taiga-ui/cdk": "3.86.0",
"@taiga-ui/core": "3.84.0", "@taiga-ui/core": "3.86.0",
"@taiga-ui/experimental": "3.84.0", "@taiga-ui/experimental": "3.86.0",
"@taiga-ui/icons": "3.84.0", "@taiga-ui/icons": "3.86.0",
"@taiga-ui/kit": "3.84.0", "@taiga-ui/kit": "3.86.0",
"@tinkoff/ng-dompurify": "4.0.0", "@tinkoff/ng-dompurify": "4.0.0",
"@tinkoff/ng-event-plugins": "3.2.0", "@tinkoff/ng-event-plugins": "3.2.0",
"angular-svg-round-progressbar": "^9.0.0", "angular-svg-round-progressbar": "^9.0.0",

View File

@@ -15,7 +15,6 @@ export class ErrorService extends ErrorHandler {
this.alerts this.alerts
.open(getErrorMessage(error, link), { .open(getErrorMessage(error, link), {
label: 'Error', label: 'Error',
autoClose: false,
status: TuiNotification.Error, status: TuiNotification.Error,
}) })
.subscribe() .subscribe()

View File

@@ -32,7 +32,7 @@ export class BadgeMenuComponent {
constructor( constructor(
private readonly splitPane: SplitPaneTracker, private readonly splitPane: SplitPaneTracker,
private readonly patch: PatchDB<DataModel>, private readonly patch: PatchDB<DataModel>,
private readonly dialog: TuiDialogService, private readonly dialogs: TuiDialogService,
private readonly clientStorageService: ClientStorageService, private readonly clientStorageService: ClientStorageService,
) {} ) {}
@@ -44,6 +44,6 @@ export class BadgeMenuComponent {
} }
onWidgets() { onWidgets() {
this.dialog.open(WIDGETS_COMPONENT, { label: 'Widgets' }).subscribe() this.dialogs.open(WIDGETS_COMPONENT, { label: 'Widgets' }).subscribe()
} }
} }

View File

@@ -6,6 +6,10 @@ import {
LoadingService, LoadingService,
StartOSDiskInfo, StartOSDiskInfo,
} from '@start9labs/shared' } from '@start9labs/shared'
import {
PasswordPromptComponent,
PromptOptions,
} from 'src/app/modals/password-prompt.component'
import { import {
BackupInfo, BackupInfo,
CifsBackupTarget, CifsBackupTarget,
@@ -14,7 +18,6 @@ import {
import { ApiService } from 'src/app/services/api/embassy-api.service' import { ApiService } from 'src/app/services/api/embassy-api.service'
import { MappedBackupTarget } from 'src/app/types/mapped-backup-target' import { MappedBackupTarget } from 'src/app/types/mapped-backup-target'
import { AppRecoverSelectPage } from '../app-recover-select/app-recover-select.page' import { AppRecoverSelectPage } from '../app-recover-select/app-recover-select.page'
import { PasswordPromptModal } from './password-prompt.modal'
@Component({ @Component({
selector: 'backup-server-select', selector: 'backup-server-select',
@@ -38,24 +41,35 @@ export class BackupServerSelectModal {
async presentModalPassword( async presentModalPassword(
serverId: string, serverId: string,
server: StartOSDiskInfo, { passwordHash }: StartOSDiskInfo,
): Promise<void> { ): Promise<void> {
const options: PromptOptions = {
title: 'Password Required',
message:
'Enter the password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.',
label: 'Decrypt Backup',
placeholder: 'Enter password',
buttonText: 'Next',
}
const modal = await this.modalCtrl.create({ const modal = await this.modalCtrl.create({
component: PasswordPromptModal, component: PasswordPromptComponent,
componentProps: { options },
canDismiss: async password => {
if (password === null) {
return true
}
try {
argon2.verify(passwordHash!, password)
await this.restoreFromBackup(serverId, password)
return true
} catch (e: any) {
this.errorService.handleError(e)
return false
}
},
}) })
modal.present() modal.present()
const { data, role } = await modal.onWillDismiss()
if (role === 'confirm') {
try {
// @TODO Alex if invalid password, we should tell the user "Invalid password" and halt execution of this function. The modal should remain so the user can try again. Correct password is asdfasdf
argon2.verify(server.passwordHash!, data)
await this.restoreFromBackup(serverId, data)
} catch (e: any) {
this.errorService.handleError(e)
}
}
} }
private async restoreFromBackup( private async restoreFromBackup(

View File

@@ -1,14 +1,29 @@
import { Component } from '@angular/core' import {
AfterViewInit,
Component,
ElementRef,
Input,
ViewChild,
} from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { IonicModule, ModalController } from '@ionic/angular' import { IonicModule, ModalController } from '@ionic/angular'
import { TuiTextfieldComponent } from '@taiga-ui/core'
import { TuiInputPasswordModule } from '@taiga-ui/kit' import { TuiInputPasswordModule } from '@taiga-ui/kit'
export interface PromptOptions {
title: string
message: string
label: string
placeholder: string
buttonText: string
}
@Component({ @Component({
standalone: true, standalone: true,
template: ` template: `
<ion-header> <ion-header>
<ion-toolbar> <ion-toolbar>
<ion-title>Decrypt Backup</ion-title> <ion-title>{{ options.title }}</ion-title>
<ion-buttons slot="end"> <ion-buttons slot="end">
<ion-button (click)="cancel()"> <ion-button (click)="cancel()">
<ion-icon slot="icon-only" name="close"></ion-icon> <ion-icon slot="icon-only" name="close"></ion-icon>
@@ -18,13 +33,11 @@ import { TuiInputPasswordModule } from '@taiga-ui/kit'
</ion-header> </ion-header>
<ion-content class="ion-padding"> <ion-content class="ion-padding">
<p>{{ options.message }}</p>
<p> <p>
Enter the password that was used to encrypt this backup. On the next <tui-input-password [(ngModel)]="password" (keydown.enter)="confirm()">
screen, you will select the individual services you want to restore. {{ options.label }}
</p> <input tuiTextfield [placeholder]="options.placeholder" />
<p>
<tui-input-password [(ngModel)]="password">
Enter password
</tui-input-password> </tui-input-password>
</p> </p>
</ion-content> </ion-content>
@@ -47,18 +60,30 @@ import { TuiInputPasswordModule } from '@taiga-ui/kit'
[disabled]="!password" [disabled]="!password"
(click)="confirm()" (click)="confirm()"
> >
Next {{ options.buttonText }}
</ion-button> </ion-button>
</ion-toolbar> </ion-toolbar>
</ion-footer> </ion-footer>
`, `,
imports: [IonicModule, FormsModule, TuiInputPasswordModule], imports: [IonicModule, FormsModule, TuiInputPasswordModule],
}) })
export class PasswordPromptModal { export class PasswordPromptComponent implements AfterViewInit {
@ViewChild(TuiTextfieldComponent, { read: ElementRef })
input?: ElementRef<HTMLInputElement>
@Input()
options!: PromptOptions
password = '' password = ''
constructor(private modalCtrl: ModalController) {} constructor(private modalCtrl: ModalController) {}
ngAfterViewInit() {
setTimeout(() => {
this.input?.nativeElement.focus({ preventScroll: true })
}, 300)
}
cancel() { cancel() {
return this.modalCtrl.dismiss(null, 'cancel') return this.modalCtrl.dismiss(null, 'cancel')
} }

View File

@@ -1,11 +1,13 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { ModalController, NavController } from '@ionic/angular' import { ModalController, NavController } from '@ionic/angular'
import { LoadingService } from '@start9labs/shared' import { ErrorService, LoadingService } from '@start9labs/shared'
import { TuiDialogService } from '@taiga-ui/core' import {
import { PROMPT, PromptOptions } from 'src/app/modals/prompt.component' PasswordPromptComponent,
PromptOptions,
} from 'src/app/modals/password-prompt.component'
import { ApiService } from 'src/app/services/api/embassy-api.service' import { ApiService } from 'src/app/services/api/embassy-api.service'
import { PatchDB } from 'patch-db-client' import { PatchDB } from 'patch-db-client'
import { skip, take, takeUntil } from 'rxjs/operators' import { skip, takeUntil } from 'rxjs/operators'
import { MappedBackupTarget } from 'src/app/types/mapped-backup-target' import { MappedBackupTarget } from 'src/app/types/mapped-backup-target'
import * as argon2 from '@start9labs/argon2' import * as argon2 from '@start9labs/argon2'
import { TuiDestroyService } from '@taiga-ui/cdk' import { TuiDestroyService } from '@taiga-ui/cdk'
@@ -22,7 +24,6 @@ import { BackupService } from 'src/app/components/backup-drives/backup.service'
@Component({ @Component({
selector: 'server-backup', selector: 'server-backup',
templateUrl: './server-backup.page.html', templateUrl: './server-backup.page.html',
styleUrls: ['./server-backup.page.scss'],
providers: [TuiDestroyService], providers: [TuiDestroyService],
}) })
export class ServerBackupPage { export class ServerBackupPage {
@@ -31,8 +32,8 @@ export class ServerBackupPage {
readonly backingUp$ = this.eosService.backingUp$ readonly backingUp$ = this.eosService.backingUp$
constructor( constructor(
private readonly errorService: ErrorService,
private readonly loader: LoadingService, private readonly loader: LoadingService,
private readonly dialogs: TuiDialogService,
private readonly modalCtrl: ModalController, private readonly modalCtrl: ModalController,
private readonly embassyApi: ApiService, private readonly embassyApi: ApiService,
private readonly navCtrl: NavController, private readonly navCtrl: NavController,
@@ -74,29 +75,35 @@ export class ServerBackupPage {
target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>, target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>,
): Promise<void> { ): Promise<void> {
const options: PromptOptions = { const options: PromptOptions = {
title: 'Master Password Needed',
message: 'Enter your master password to encrypt this backup.', message: 'Enter your master password to encrypt this backup.',
label: 'Master Password', label: 'Master Password',
placeholder: 'Enter master password', placeholder: 'Enter master password',
useMask: true,
buttonText: 'Create Backup', buttonText: 'Create Backup',
} }
this.dialogs const modal = await this.modalCtrl.create({
.open<string>(PROMPT, { component: PasswordPromptComponent,
label: 'Master Password Needed', componentProps: { options },
data: options, canDismiss: async password => {
}) if (password === null) {
.pipe(take(1)) return true
.subscribe(async (password: string) => { }
const { passwordHash, id } = await getServerInfo(this.patch) const { passwordHash, id } = await getServerInfo(this.patch)
// @TODO Alex if invalid password, we should tell the user "Invalid password" and halt execution of this function. The modal should remain so the user can try again. Correct password is asdfasdf
// confirm password matches current master password // confirm password matches current master password
argon2.verify(passwordHash, password) try {
argon2.verify(passwordHash, password)
} catch (e: any) {
this.errorService.handleError(e)
return false
}
// first time backup // first time backup
if (!this.backupService.hasThisBackup(target.entry, id)) { if (!this.backupService.hasThisBackup(target.entry, id)) {
await this.createBackup(target, password) this.createBackup(target, password)
return true
// existing backup // existing backup
} else { } else {
try { try {
@@ -106,39 +113,49 @@ export class ServerBackupPage {
() => this.presentModalOldPassword(target, password), () => this.presentModalOldPassword(target, password),
250, 250,
) )
return return true
} }
await this.createBackup(target, password) await this.createBackup(target, password)
return true
} }
}) },
})
modal.present()
} }
private async presentModalOldPassword( private async presentModalOldPassword(
target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>, target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>,
password: string, password: string,
): Promise<void> { ): Promise<void> {
const { id } = await getServerInfo(this.patch)
const options: PromptOptions = { const options: PromptOptions = {
title: 'Original Password Needed',
message: message:
'This backup was created with a different password. Enter the ORIGINAL password that was used to encrypt this backup.', 'This backup was created with a different password. Enter the ORIGINAL password that was used to encrypt this backup.',
label: 'Original Password', label: 'Original Password',
placeholder: 'Enter original password', placeholder: 'Enter original password',
useMask: true,
buttonText: 'Create Backup', buttonText: 'Create Backup',
} }
const { id } = await getServerInfo(this.patch) const modal = await this.modalCtrl.create({
component: PasswordPromptComponent,
componentProps: { options },
canDismiss: async oldPassword => {
if (oldPassword === null) {
return true
}
this.dialogs try {
.open<string>(PROMPT, { argon2.verify(target.entry.startOs[id].passwordHash!, oldPassword)
label: 'Original Password Needed', await this.createBackup(target, password, oldPassword)
data: options, return true
}) } catch (e: any) {
.pipe(take(1)) this.errorService.handleError(e)
.subscribe(async (oldPassword: string) => { return false
// @TODO Alex if invalid password, we should tell the user "Invalid password" and halt execution of this function. The modal should remain so the user can try again. Correct password is asdfasdf }
argon2.verify(target.entry.startOs[id].passwordHash!, oldPassword) },
await this.createBackup(target, password, oldPassword) })
}) modal.present()
} }
private async createBackup( private async createBackup(

View File

@@ -56,7 +56,7 @@ export class WidgetsPage {
@Optional() @Optional()
@Inject(POLYMORPHEUS_CONTEXT) @Inject(POLYMORPHEUS_CONTEXT)
readonly context: TuiDialogContext | null, readonly context: TuiDialogContext | null,
private readonly dialog: TuiDialogService, private readonly dialogs: TuiDialogService,
private readonly patch: PatchDB<DataModel>, private readonly patch: PatchDB<DataModel>,
private readonly cdr: ChangeDetectorRef, private readonly cdr: ChangeDetectorRef,
private readonly api: ApiService, private readonly api: ApiService,
@@ -83,7 +83,7 @@ export class WidgetsPage {
} }
add() { add() {
this.dialog.open(ADD_WIDGET, { label: 'Add widget' }).subscribe(widget => { this.dialogs.open(ADD_WIDGET, { label: 'Add widget' }).subscribe(widget => {
this.addWidget(widget!) this.addWidget(widget!)
}) })
} }