1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00

[PM-328] Move exporter to tools (#5070)

* Create and register new libs/exporter

Create package.json
Create tsconfig
Create jest.config
Extend shared and root tsconfig and jest.configs
Register with eslint

* Migrate exportService to libs/exporter

Move exportService (abstraction and impl) into libs/exporter
Refactored exportService to be split into vault-export and event-export
Created barrel-files for both exports
Moved export.service.spec.ts into vault-export
Created an export-helper, which helps build the filename (extract method refactor from ExportService)

* Move components in libs/angular into tools-subfolder

Moved components
Updated imports in jslib-services.module and jslib.module

* Register libs/exporter with browser and fix imports

Move export.component into tools-subfolder

* Register libs/exporter with cli and fix imports

Move export.command into tools-subfolder

* Register libs/exporter with desktop and fix imports

Move export.component into tools-subfolder

* Move export models to libs/exporter

* Update web imports

* Update package-lock.json

* Move export models back as it would create circular dependency

Reponse models in common rely on export models which are in libs/exporter, which relies on common

* Fix up web for event-export

* Update CODEOWNERS

* Add export-models to team-tools-dev

* Simplify domain import

* Moving EventExport into web
This commit is contained in:
Daniel James Smith
2023-04-19 11:30:46 +02:00
committed by GitHub
parent 830af7b06d
commit 192bb5a7b3
53 changed files with 266 additions and 133 deletions

View File

@@ -1,5 +0,0 @@
<ng-container *ngIf="show">
<app-callout type="info" title="{{ scopeConfig.title | i18n }}">
{{ scopeConfig.description | i18n : scopeConfig.scopeIdentifier }}
</app-callout>
</ng-container>

View File

@@ -1,43 +0,0 @@
import { Component, Input, OnInit } from "@angular/core";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@Component({
selector: "app-export-scope-callout",
templateUrl: "export-scope-callout.component.html",
})
export class ExportScopeCalloutComponent implements OnInit {
@Input() organizationId: string = null;
show = false;
scopeConfig: {
title: string;
description: string;
scopeIdentifier: string;
};
constructor(
protected organizationService: OrganizationService,
protected stateService: StateService
) {}
async ngOnInit(): Promise<void> {
if (!this.organizationService.hasOrganizations()) {
return;
}
this.scopeConfig =
this.organizationId != null
? {
title: "exportingOrganizationVaultTitle",
description: "exportingOrganizationVaultDescription",
scopeIdentifier: this.organizationService.get(this.organizationId).name,
}
: {
title: "exportingPersonalVaultTitle",
description: "exportingPersonalVaultDescription",
scopeIdentifier: await this.stateService.getEmail(),
};
this.show = true;
}
}

View File

@@ -1,220 +0,0 @@
import { Directive, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core";
import { UntypedFormBuilder, Validators } from "@angular/forms";
import { merge, takeUntil, Subject, startWith } from "rxjs";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { ExportService } from "@bitwarden/common/abstractions/export.service";
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { EncryptedExportType, EventType } from "@bitwarden/common/enums";
@Directive()
export class ExportComponent implements OnInit, OnDestroy {
@Output() onSaved = new EventEmitter();
formPromise: Promise<string>;
disabledByPolicy = false;
exportForm = this.formBuilder.group({
format: ["json"],
secret: [""],
filePassword: ["", Validators.required],
confirmFilePassword: ["", Validators.required],
fileEncryptionType: [EncryptedExportType.AccountEncrypted],
});
formatOptions = [
{ name: ".json", value: "json" },
{ name: ".csv", value: "csv" },
{ name: ".json (Encrypted)", value: "encrypted_json" },
];
private destroy$ = new Subject<void>();
constructor(
protected cryptoService: CryptoService,
protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService,
protected exportService: ExportService,
protected eventCollectionService: EventCollectionService,
private policyService: PolicyService,
protected win: Window,
private logService: LogService,
private userVerificationService: UserVerificationService,
private formBuilder: UntypedFormBuilder,
protected fileDownloadService: FileDownloadService
) {}
async ngOnInit() {
this.policyService
.policyAppliesToActiveUser$(PolicyType.DisablePersonalVaultExport)
.pipe(takeUntil(this.destroy$))
.subscribe((policyAppliesToActiveUser) => {
this.disabledByPolicy = policyAppliesToActiveUser;
});
await this.checkExportDisabled();
merge(
this.exportForm.get("format").valueChanges,
this.exportForm.get("fileEncryptionType").valueChanges
)
.pipe(takeUntil(this.destroy$))
.pipe(startWith(0))
.subscribe(() => this.adjustValidators());
}
ngOnDestroy(): void {
this.destroy$.next();
}
async checkExportDisabled() {
if (this.disabledByPolicy) {
this.exportForm.disable();
}
}
get encryptedFormat() {
return this.format === "encrypted_json";
}
protected async doExport() {
try {
this.formPromise = this.getExportData();
const data = await this.formPromise;
this.downloadFile(data);
this.saved();
await this.collectEvent();
this.exportForm.get("secret").setValue("");
this.exportForm.clearValidators();
} catch (e) {
this.logService.error(e);
}
}
async submit() {
if (this.disabledByPolicy) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("personalVaultExportPolicyInEffect")
);
return;
}
const acceptedWarning = await this.warningDialog();
if (!acceptedWarning) {
return;
}
const secret = this.exportForm.get("secret").value;
try {
await this.userVerificationService.verifyUser(secret);
} catch (e) {
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message);
return;
}
this.doExport();
}
async warningDialog() {
if (this.encryptedFormat) {
return await this.platformUtilsService.showDialog(
"<p>" +
this.i18nService.t("encExportKeyWarningDesc") +
"<p>" +
this.i18nService.t("encExportAccountWarningDesc"),
this.i18nService.t("confirmVaultExport"),
this.i18nService.t("exportVault"),
this.i18nService.t("cancel"),
"warning",
true
);
} else {
return await this.platformUtilsService.showDialog(
this.i18nService.t("exportWarningDesc"),
this.i18nService.t("confirmVaultExport"),
this.i18nService.t("exportVault"),
this.i18nService.t("cancel"),
"warning"
);
}
}
protected saved() {
this.onSaved.emit();
}
protected getExportData() {
if (
this.format === "encrypted_json" &&
this.fileEncryptionType === EncryptedExportType.FileEncrypted
) {
return this.exportService.getPasswordProtectedExport(this.filePassword);
} else {
return this.exportService.getExport(this.format, null);
}
}
protected getFileName(prefix?: string) {
let extension = this.format;
if (this.format === "encrypted_json") {
if (prefix == null) {
prefix = "encrypted";
} else {
prefix = "encrypted_" + prefix;
}
extension = "json";
}
return this.exportService.getFileName(prefix, extension);
}
protected async collectEvent(): Promise<void> {
await this.eventCollectionService.collect(EventType.User_ClientExportedVault);
}
get format() {
return this.exportForm.get("format").value;
}
get filePassword() {
return this.exportForm.get("filePassword").value;
}
get confirmFilePassword() {
return this.exportForm.get("confirmFilePassword").value;
}
get fileEncryptionType() {
return this.exportForm.get("fileEncryptionType").value;
}
adjustValidators() {
this.exportForm.get("confirmFilePassword").reset();
this.exportForm.get("filePassword").reset();
if (this.encryptedFormat && this.fileEncryptionType == EncryptedExportType.FileEncrypted) {
this.exportForm.controls.filePassword.enable();
this.exportForm.controls.confirmFilePassword.enable();
} else {
this.exportForm.controls.filePassword.disable();
this.exportForm.controls.confirmFilePassword.disable();
}
}
private downloadFile(csv: string): void {
const fileName = this.getFileName();
this.fileDownloadService.download({
fileName: fileName,
blobData: csv,
blobOptions: { type: "text/plain" },
});
}
}