[PM-2517] [PM-8299] Add password protected export on desktop/Export managed collections (#9286)
* Move/replace submit and userVerification logic from web into the BaseExportComponent Add "@bitwarden/auth" as dependency to the vault-export-ui package New submit logic also checks for password-encrypted exports which will be need for future UI updates on browser and desktop * Create export-desktop component using shared recipe Create new export component that uses the shared export.component from @bitwarden/vault-export-ui * Update imports within AppModule Remove old ExportComponent Remove ExportScopeCalloutComponent as it's part of the BaseExportComponent * Open new component when clicking on Export Vault menu item * Add missing entries to messages.json * Delete old export.component * Remove duplicate verifyUser-method * Change placeholder example --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
parent
e318ac0ddf
commit
df193dd869
|
@ -57,7 +57,7 @@ import { PremiumComponent } from "../vault/app/accounts/premium.component";
|
||||||
import { FolderAddEditComponent } from "../vault/app/vault/folder-add-edit.component";
|
import { FolderAddEditComponent } from "../vault/app/vault/folder-add-edit.component";
|
||||||
|
|
||||||
import { SettingsComponent } from "./accounts/settings.component";
|
import { SettingsComponent } from "./accounts/settings.component";
|
||||||
import { ExportComponent } from "./tools/export/export.component";
|
import { ExportDesktopComponent } from "./tools/export/export-desktop.component";
|
||||||
import { GeneratorComponent } from "./tools/generator.component";
|
import { GeneratorComponent } from "./tools/generator.component";
|
||||||
import { ImportDesktopComponent } from "./tools/import/import-desktop.component";
|
import { ImportDesktopComponent } from "./tools/import/import-desktop.component";
|
||||||
import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component";
|
import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component";
|
||||||
|
@ -366,7 +366,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
await this.dialogService.open(ImportDesktopComponent);
|
await this.dialogService.open(ImportDesktopComponent);
|
||||||
break;
|
break;
|
||||||
case "exportVault":
|
case "exportVault":
|
||||||
await this.openExportVault();
|
await this.dialogService.open(ExportDesktopComponent);
|
||||||
break;
|
break;
|
||||||
case "newLogin":
|
case "newLogin":
|
||||||
this.routeToVault("add", CipherType.Login);
|
this.routeToVault("add", CipherType.Login);
|
||||||
|
@ -463,26 +463,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openExportVault() {
|
|
||||||
this.modalService.closeAll();
|
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
|
||||||
ExportComponent,
|
|
||||||
this.exportVaultModalRef,
|
|
||||||
);
|
|
||||||
this.modal = modal;
|
|
||||||
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
||||||
childComponent.onSaved.subscribe(() => {
|
|
||||||
this.modal.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
||||||
this.modal.onClosed.subscribe(() => {
|
|
||||||
this.modal = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async addFolder() {
|
async addFolder() {
|
||||||
this.modalService.closeAll();
|
this.modalService.closeAll();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { NgModule } from "@angular/core";
|
||||||
import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe";
|
import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe";
|
||||||
import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe";
|
import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe";
|
||||||
import { DialogModule, CalloutModule } from "@bitwarden/components";
|
import { DialogModule, CalloutModule } from "@bitwarden/components";
|
||||||
import { ExportScopeCalloutComponent } from "@bitwarden/vault-export-ui";
|
|
||||||
|
|
||||||
import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component";
|
import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component";
|
||||||
import { DeleteAccountComponent } from "../auth/delete-account.component";
|
import { DeleteAccountComponent } from "../auth/delete-account.component";
|
||||||
|
@ -47,7 +46,6 @@ import { HeaderComponent } from "./layout/header.component";
|
||||||
import { NavComponent } from "./layout/nav.component";
|
import { NavComponent } from "./layout/nav.component";
|
||||||
import { SearchComponent } from "./layout/search/search.component";
|
import { SearchComponent } from "./layout/search/search.component";
|
||||||
import { SharedModule } from "./shared/shared.module";
|
import { SharedModule } from "./shared/shared.module";
|
||||||
import { ExportComponent } from "./tools/export/export.component";
|
|
||||||
import { GeneratorComponent } from "./tools/generator.component";
|
import { GeneratorComponent } from "./tools/generator.component";
|
||||||
import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component";
|
import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component";
|
||||||
import { AddEditComponent as SendAddEditComponent } from "./tools/send/add-edit.component";
|
import { AddEditComponent as SendAddEditComponent } from "./tools/send/add-edit.component";
|
||||||
|
@ -63,7 +61,6 @@ import { SendComponent } from "./tools/send/send.component";
|
||||||
CalloutModule,
|
CalloutModule,
|
||||||
DeleteAccountComponent,
|
DeleteAccountComponent,
|
||||||
UserVerificationComponent,
|
UserVerificationComponent,
|
||||||
ExportScopeCalloutComponent,
|
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AccessibilityCookieComponent,
|
AccessibilityCookieComponent,
|
||||||
|
@ -77,7 +74,6 @@ import { SendComponent } from "./tools/send/send.component";
|
||||||
ColorPasswordPipe,
|
ColorPasswordPipe,
|
||||||
ColorPasswordCountPipe,
|
ColorPasswordCountPipe,
|
||||||
EnvironmentComponent,
|
EnvironmentComponent,
|
||||||
ExportComponent,
|
|
||||||
FolderAddEditComponent,
|
FolderAddEditComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
HintComponent,
|
HintComponent,
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<bit-dialog #dialog dialogSize="large">
|
||||||
|
<span bitDialogTitle>{{ "exportVault" | i18n }}</span>
|
||||||
|
<ng-container bitDialogContent>
|
||||||
|
<tools-export
|
||||||
|
(formLoading)="this.loading = $event"
|
||||||
|
(formDisabled)="this.disabled = $event"
|
||||||
|
(onSuccessfulExport)="this.onSuccessfulExport($event)"
|
||||||
|
></tools-export>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<button
|
||||||
|
[disabled]="disabled"
|
||||||
|
[loading]="loading"
|
||||||
|
form="export_form_exportForm"
|
||||||
|
bitButton
|
||||||
|
type="submit"
|
||||||
|
bitFormButton
|
||||||
|
buttonType="primary"
|
||||||
|
>
|
||||||
|
{{ "exportVault" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-dialog>
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { DialogRef } from "@angular/cdk/dialog";
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components";
|
||||||
|
import { ExportComponent } from "@bitwarden/vault-export-ui";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "export-desktop.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
ExportComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ExportDesktopComponent {
|
||||||
|
protected disabled = false;
|
||||||
|
protected loading = false;
|
||||||
|
|
||||||
|
constructor(public dialogRef: DialogRef) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that is called after a successful export.
|
||||||
|
*/
|
||||||
|
protected async onSuccessfulExport(organizationId: string): Promise<void> {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +0,0 @@
|
||||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="exportTitle">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<form class="modal-content" #form (ngSubmit)="submit()" [formGroup]="exportForm">
|
|
||||||
<div class="modal-body">
|
|
||||||
<bit-callout
|
|
||||||
type="warning"
|
|
||||||
title="{{ 'vaultExportDisabled' | i18n }}"
|
|
||||||
*ngIf="disabledByPolicy"
|
|
||||||
>
|
|
||||||
{{ "personalVaultExportPolicyInEffect" | i18n }}
|
|
||||||
</bit-callout>
|
|
||||||
<tools-export-scope-callout *ngIf="!disabledByPolicy"></tools-export-scope-callout>
|
|
||||||
<div class="box">
|
|
||||||
<h1 class="box-header" id="exportTitle">
|
|
||||||
{{ "exportVault" | i18n }}
|
|
||||||
</h1>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row" appBoxRow>
|
|
||||||
<label for="format">{{ "fileFormat" | i18n }}</label>
|
|
||||||
<select class="form-control" id="format" name="Format" formControlName="format">
|
|
||||||
<option *ngFor="let f of formatOptions" [value]="f.value">{{ f.name }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="primary"
|
|
||||||
appA11yTitle="{{ 'submit' | i18n }}"
|
|
||||||
[disabled]="disabledByPolicy"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-download bwi-lg bwi-fw" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
import { UntypedFormBuilder } from "@angular/forms";
|
|
||||||
|
|
||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
|
||||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { DialogService } from "@bitwarden/components";
|
|
||||||
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
|
|
||||||
import { ExportComponent as BaseExportComponent } from "@bitwarden/vault-export-ui";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-export",
|
|
||||||
templateUrl: "export.component.html",
|
|
||||||
})
|
|
||||||
export class ExportComponent extends BaseExportComponent implements OnInit {
|
|
||||||
constructor(
|
|
||||||
i18nService: I18nService,
|
|
||||||
platformUtilsService: PlatformUtilsService,
|
|
||||||
exportService: VaultExportServiceAbstraction,
|
|
||||||
eventCollectionService: EventCollectionService,
|
|
||||||
policyService: PolicyService,
|
|
||||||
formBuilder: UntypedFormBuilder,
|
|
||||||
logService: LogService,
|
|
||||||
fileDownloadService: FileDownloadService,
|
|
||||||
dialogService: DialogService,
|
|
||||||
organizationService: OrganizationService,
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
i18nService,
|
|
||||||
platformUtilsService,
|
|
||||||
exportService,
|
|
||||||
eventCollectionService,
|
|
||||||
policyService,
|
|
||||||
logService,
|
|
||||||
formBuilder,
|
|
||||||
fileDownloadService,
|
|
||||||
dialogService,
|
|
||||||
organizationService,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1299,12 +1299,42 @@
|
||||||
"message": "Password updated",
|
"message": "Password updated",
|
||||||
"description": "ex. Date this password was updated"
|
"description": "ex. Date this password was updated"
|
||||||
},
|
},
|
||||||
|
"exportFrom": {
|
||||||
|
"message": "Export from"
|
||||||
|
},
|
||||||
"exportVault": {
|
"exportVault": {
|
||||||
"message": "Export vault"
|
"message": "Export vault"
|
||||||
},
|
},
|
||||||
"fileFormat": {
|
"fileFormat": {
|
||||||
"message": "File format"
|
"message": "File format"
|
||||||
},
|
},
|
||||||
|
"fileEncryptedExportWarningDesc": {
|
||||||
|
"message": "This file export will be password protected and require the file password to decrypt."
|
||||||
|
},
|
||||||
|
"filePassword": {
|
||||||
|
"message": "File password"
|
||||||
|
},
|
||||||
|
"exportPasswordDescription": {
|
||||||
|
"message": "This password will be used to export and import this file"
|
||||||
|
},
|
||||||
|
"accountRestrictedOptionDescription": {
|
||||||
|
"message": "Use your account encryption key, derived from your account's username and Master Password, to encrypt the export and restrict import to only the current Bitwarden account."
|
||||||
|
},
|
||||||
|
"passwordProtected": {
|
||||||
|
"message": "Password protected"
|
||||||
|
},
|
||||||
|
"passwordProtectedOptionDescription": {
|
||||||
|
"message": "Set a file password to encrypt the export and import it to any Bitwarden account using the password for decryption."
|
||||||
|
},
|
||||||
|
"exportTypeHeading": {
|
||||||
|
"message": "Export type"
|
||||||
|
},
|
||||||
|
"accountRestricted": {
|
||||||
|
"message": "Account restricted"
|
||||||
|
},
|
||||||
|
"filePasswordAndConfirmFilePasswordDoNotMatch": {
|
||||||
|
"message": "“File password” and “Confirm file password“ do not match."
|
||||||
|
},
|
||||||
"hCaptchaUrl": {
|
"hCaptchaUrl": {
|
||||||
"message": "hCaptcha Url",
|
"message": "hCaptcha Url",
|
||||||
"description": "hCaptcha is the name of a website, should not be translated"
|
"description": "hCaptcha is the name of a website, should not be translated"
|
||||||
|
@ -2071,6 +2101,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exportingOrganizationVaultTitle": {
|
||||||
|
"message": "Exporting organization vault"
|
||||||
|
},
|
||||||
|
"exportingOrganizationVaultDesc": {
|
||||||
|
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "ACME Moving Co."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"message": "Locked"
|
"message": "Locked"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue