Skip to content

Commit

Permalink
[AC-1492] Split export service (#7462)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
aj-rosado authored Jan 29, 2024
1 parent 0530536 commit d5de9cb
Show file tree
Hide file tree
Showing 17 changed files with 786 additions and 493 deletions.
22 changes: 21 additions & 1 deletion apps/browser/src/background/main.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
22 changes: 21 additions & 1 deletion apps/cli/src/bw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -146,6 +150,8 @@ export class Main {
importService: ImportServiceAbstraction;
importApiService: ImportApiServiceAbstraction;
exportService: VaultExportServiceAbstraction;
individualExportService: IndividualVaultExportServiceAbstraction;
organizationExportService: OrganizationVaultExportServiceAbstraction;
searchService: SearchService;
cryptoFunctionService: NodeCryptoFunctionService;
encryptService: EncryptServiceImplementation;
Expand Down Expand Up @@ -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);
Expand Down
34 changes: 19 additions & 15 deletions apps/cli/src/tools/export.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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() {
Expand Down
26 changes: 14 additions & 12 deletions apps/web/src/app/tools/vault-export/export.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ <h1 bitTypography="h1">{{ "exportVault" | i18n }}</h1>
*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>
Expand Down
5 changes: 1 addition & 4 deletions apps/web/src/app/tools/vault-export/export.component.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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,
Expand Down
24 changes: 22 additions & 2 deletions libs/angular/src/services/jslib-services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import {
VaultExportService,
VaultExportServiceAbstraction,
OrganizationVaultExportService,
OrganizationVaultExportServiceAbstraction,
IndividualVaultExportService,
IndividualVaultExportServiceAbstraction,
} from "@bitwarden/exporter/vault-export";
import {
ImportApiService,
Expand Down Expand Up @@ -537,17 +541,33 @@ import { ModalService } from "./modal.service";
],
},
{
provide: VaultExportServiceAbstraction,
useClass: VaultExportService,
provide: IndividualVaultExportServiceAbstraction,
useClass: IndividualVaultExportService,
deps: [
FolderServiceAbstraction,
CipherServiceAbstraction,
CryptoServiceAbstraction,
CryptoFunctionServiceAbstraction,
StateServiceAbstraction,
],
},
{
provide: OrganizationVaultExportServiceAbstraction,
useClass: OrganizationVaultExportService,
deps: [
CipherServiceAbstraction,
ApiServiceAbstraction,
CryptoServiceAbstraction,
CryptoFunctionServiceAbstraction,
StateServiceAbstraction,
CollectionServiceAbstraction,
],
},
{
provide: VaultExportServiceAbstraction,
useClass: VaultExportService,
deps: [IndividualVaultExportServiceAbstraction, OrganizationVaultExportServiceAbstraction],
},
{
provide: SearchServiceAbstraction,
useClass: SearchService,
Expand Down
Loading

0 comments on commit d5de9cb

Please sign in to comment.