mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
fix: fix wrong password messaging
This commit is contained in:
94
web/package-lock.json
generated
94
web/package-lock.json
generated
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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')
|
||||||
}
|
}
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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!)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user