1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[AC-1492] Split export service (#7462)

* Split export service into vault and org export service

* Changed CLI logic to use split export logic

* correct unit tests

* Created individual export service, export service making the calls for org and ind vault

* Improved code readability

* Merged PasswordProtectedExport with Export methods to simplify calls

* Some small refactor

* [AC-1492] Managed collections export (#7556)

* Added managed collections export method
Added logic to show orgs on export that the user can export from

* Merge branch 'tools/AC-1492/split-export-services' into tools/AC-1492/export-flexible-collections

# Conflicts:
#	apps/web/src/app/admin-console/organizations/tools/vault-export/org-vault-export.component.ts
#	apps/web/src/app/tools/vault-export/export.component.ts

* Change export to use new organization.flexiblecollection flag

* Little refactor changing parameter names and reduzing the size of export.component.ts ngOnInit

* Removed unused service from export constructor and removed unnecessary default value from org export service parameter

* Simplified organizations selection for vault export to only verify if it has flexiblecollections

* removed unecessary services from ExportComponent constructor on popup

* Fixed possible race condition on managed export
This commit is contained in:
aj-rosado
2024-01-29 09:38:16 +00:00
committed by GitHub
parent 053053624f
commit d5de9cbeb2
17 changed files with 789 additions and 496 deletions

View File

@@ -123,6 +123,10 @@ import { SyncNotifierService } from "@bitwarden/common/vault/services/sync/sync-
import { SyncService } from "@bitwarden/common/vault/services/sync/sync.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import {
IndividualVaultExportService,
IndividualVaultExportServiceAbstraction,
OrganizationVaultExportService,
OrganizationVaultExportServiceAbstraction,
VaultExportService,
VaultExportServiceAbstraction,
} from "@bitwarden/exporter/vault-export";
@@ -253,6 +257,8 @@ export default class MainBackground {
derivedStateProvider: DerivedStateProvider;
stateProvider: StateProvider;
fido2Service: Fido2ServiceAbstraction;
individualVaultExportService: IndividualVaultExportServiceAbstraction;
organizationVaultExportService: OrganizationVaultExportServiceAbstraction;
// Passed to the popup for Safari to workaround issues with theming, downloading, etc.
backgroundWindow = window;
@@ -635,14 +641,28 @@ export default class MainBackground {
this.cryptoService,
);
this.exportService = new VaultExportService(
this.individualVaultExportService = new IndividualVaultExportService(
this.folderService,
this.cipherService,
this.cryptoService,
this.cryptoFunctionService,
this.stateService,
);
this.organizationVaultExportService = new OrganizationVaultExportService(
this.cipherService,
this.apiService,
this.cryptoService,
this.cryptoFunctionService,
this.stateService,
this.collectionService,
);
this.exportService = new VaultExportService(
this.individualVaultExportService,
this.organizationVaultExportService,
);
this.notificationsService = new NotificationsService(
this.logService,
this.syncService,

View File

@@ -88,6 +88,10 @@ import { SyncNotifierService } from "@bitwarden/common/vault/services/sync/sync-
import { SyncService } from "@bitwarden/common/vault/services/sync/sync.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import {
IndividualVaultExportService,
IndividualVaultExportServiceAbstraction,
OrganizationVaultExportService,
OrganizationVaultExportServiceAbstraction,
VaultExportService,
VaultExportServiceAbstraction,
} from "@bitwarden/exporter/vault-export";
@@ -146,6 +150,8 @@ export class Main {
importService: ImportServiceAbstraction;
importApiService: ImportApiServiceAbstraction;
exportService: VaultExportServiceAbstraction;
individualExportService: IndividualVaultExportServiceAbstraction;
organizationExportService: OrganizationVaultExportServiceAbstraction;
searchService: SearchService;
cryptoFunctionService: NodeCryptoFunctionService;
encryptService: EncryptServiceImplementation;
@@ -509,13 +515,27 @@ export class Main {
this.collectionService,
this.cryptoService,
);
this.exportService = new VaultExportService(
this.individualExportService = new IndividualVaultExportService(
this.folderService,
this.cipherService,
this.cryptoService,
this.cryptoFunctionService,
this.stateService,
);
this.organizationExportService = new OrganizationVaultExportService(
this.cipherService,
this.apiService,
this.cryptoService,
this.cryptoFunctionService,
this.stateService,
this.collectionService,
);
this.exportService = new VaultExportService(
this.individualExportService,
this.organizationExportService,
);
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);

View File

@@ -32,7 +32,14 @@ export class ExportCommand {
);
}
const format = options.format ?? "csv";
let password = options.password;
// has password and format is 'json' => should have the same behaviour as 'encrypted_json'
// format is 'undefined' => Defaults to 'csv'
// Any other case => returns the options.format
const format =
password && options.format == "json" ? "encrypted_json" : options.format ?? "csv";
if (!this.isSupportedExportFormat(format)) {
return Response.badRequest(
`'${format}' is not a supported export format. Supported formats: ${EXPORT_FORMATS.join(
@@ -47,10 +54,18 @@ export class ExportCommand {
let exportContent: string = null;
try {
if (format === "encrypted_json") {
password = await this.promptPassword(password);
}
exportContent =
format === "encrypted_json"
? await this.getProtectedExport(options.password, options.organizationid)
: await this.getUnprotectedExport(format, options.organizationid);
options.organizationid == null
? await this.exportService.getExport(format, password)
: await this.exportService.getOrganizationExport(
options.organizationid,
format,
password,
);
const eventType = options.organizationid
? EventType.Organization_ClientExportedVault
@@ -62,17 +77,6 @@ export class ExportCommand {
return await this.saveFile(exportContent, options, format);
}
private async getProtectedExport(passwordOption: string | boolean, organizationId?: string) {
const password = await this.promptPassword(passwordOption);
return password == null
? await this.exportService.getExport("encrypted_json", organizationId)
: await this.exportService.getPasswordProtectedExport(password, organizationId);
}
private async getUnprotectedExport(format: ExportFormat, organizationId?: string) {
return this.exportService.getExport(format, organizationId);
}
private async saveFile(
exportContent: string,
options: program.OptionValues,

View File

@@ -1,7 +1,6 @@
import { Component } from "@angular/core";
import { UntypedFormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { map, switchMap } from "rxjs";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -62,20 +61,15 @@ export class OrganizationVaultExportComponent extends ExportComponent {
this.organizationId = params.organizationId;
});
this.flexibleCollectionsEnabled$ = this.route.parent.parent.params.pipe(
switchMap((params) => this.organizationService.get$(params.organizationId)),
map((organization) => organization.flexibleCollections),
);
await super.ngOnInit();
}
getExportData() {
if (this.isFileEncryptedExport) {
return this.exportService.getPasswordProtectedExport(this.filePassword, this.organizationId);
} else {
return this.exportService.getOrganizationExport(this.organizationId, this.format);
}
return this.exportService.getOrganizationExport(
this.organizationId,
this.format,
this.filePassword,
);
}
getFileName() {

View File

@@ -15,18 +15,20 @@
*ngIf="!disabledByPolicy"
></app-export-scope-callout>
<bit-form-field *ngIf="flexibleCollectionsEnabled$ | async">
<bit-label>{{ "exportFrom" | i18n }}</bit-label>
<bit-select formControlName="vaultSelector">
<bit-option [label]="'myVault' | i18n" value="myVault" icon="bwi-user" />
<bit-option
*ngFor="let o of organizations$ | async"
[value]="o.id"
[label]="o.name"
icon="bwi-business"
/>
</bit-select>
</bit-form-field>
<ng-container *ngIf="organizations$ | async as organizations">
<bit-form-field *ngIf="organizations.length > 0">
<bit-label>{{ "exportFrom" | i18n }}</bit-label>
<bit-select formControlName="vaultSelector">
<bit-option [label]="'myVault' | i18n" value="myVault" icon="bwi-user" />
<bit-option
*ngFor="let o of organizations$ | async"
[value]="o.id"
[label]="o.name"
icon="bwi-business"
/>
</bit-select>
</bit-form-field>
</ng-container>
<bit-form-field>
<bit-label>{{ "fileFormat" | i18n }}</bit-label>

View File

@@ -1,6 +1,6 @@
import { Component } from "@angular/core";
import { UntypedFormBuilder } from "@angular/forms";
import { Observable, firstValueFrom } from "rxjs";
import { firstValueFrom } from "rxjs";
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
@@ -25,9 +25,6 @@ export class ExportComponent extends BaseExportComponent {
encryptedExportType = EncryptedExportType;
protected showFilePassword: boolean;
// Used in the OrganizationVaultExport subclass
protected flexibleCollectionsEnabled$ = new Observable<boolean>();
constructor(
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,