1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00

[PM-3040] [BEEEP] Extend json-export to include passwordhistory and vault item dates (created, updated, deleted) (#5917)

* Add password history to json exports

Change callout to not mention missing password history any longer

* Added item meta dates to json exports

Added vault items creation-/revision-/deleted-dates to json exports

* Removed unnecessary promises

* Add bitwarden-json-export types

Define types
Use types in vault-export-service
Move existing password-protected type to export-types

* Use bitwarden-json-export types in bitwarden-json-importer

* Clean up passwordHistory if needed

* Define and use bitwarden-csv-export-types
This commit is contained in:
Daniel James Smith
2023-08-15 20:32:40 +02:00
committed by GitHub
parent b56bb19c02
commit 15f29c5fb1
11 changed files with 264 additions and 83 deletions

View File

@@ -313,6 +313,9 @@ export abstract class BaseImporter {
if (cipher.fields != null && cipher.fields.length === 0) {
cipher.fields = null;
}
if (cipher.passwordHistory != null && cipher.passwordHistory.length === 0) {
cipher.passwordHistory = null;
}
}
protected processKvp(

View File

@@ -6,13 +6,21 @@ import {
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import {
BitwardenEncryptedIndividualJsonExport,
BitwardenEncryptedOrgJsonExport,
BitwardenJsonExport,
BitwardenUnEncryptedIndividualJsonExport,
BitwardenUnEncryptedOrgJsonExport,
} from "@bitwarden/exporter/vault-export/bitwarden-json-export-types";
import { ImportResult } from "../../models/import-result";
import { BaseImporter } from "../base-importer";
import { Importer } from "../importer";
export class BitwardenJsonImporter extends BaseImporter implements Importer {
private results: any;
private result: ImportResult;
protected constructor(
@@ -24,25 +32,27 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
async parse(data: string): Promise<ImportResult> {
this.result = new ImportResult();
this.results = JSON.parse(data);
if (this.results == null || this.results.items == null) {
const results: BitwardenJsonExport = JSON.parse(data);
if (results == null || results.items == null) {
this.result.success = false;
return this.result;
}
if (this.results.encrypted) {
await this.parseEncrypted();
if (results.encrypted) {
await this.parseEncrypted(results as any);
} else {
this.parseDecrypted();
await this.parseDecrypted(results as any);
}
return this.result;
}
private async parseEncrypted() {
if (this.results.encKeyValidation_DO_NOT_EDIT != null) {
private async parseEncrypted(
results: BitwardenEncryptedIndividualJsonExport | BitwardenEncryptedOrgJsonExport
) {
if (results.encKeyValidation_DO_NOT_EDIT != null) {
const orgKey = await this.cryptoService.getOrgKey(this.organizationId);
const encKeyValidation = new EncString(this.results.encKeyValidation_DO_NOT_EDIT);
const encKeyValidation = new EncString(results.encKeyValidation_DO_NOT_EDIT);
const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8(
encKeyValidation,
orgKey
@@ -54,30 +64,11 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
}
}
const groupingsMap = new Map<string, number>();
const groupingsMap = this.organization
? await this.parseCollections(results as BitwardenEncryptedOrgJsonExport)
: await this.parseFolders(results as BitwardenEncryptedIndividualJsonExport);
if (this.organization && this.results.collections != null) {
for (const c of this.results.collections as CollectionWithIdExport[]) {
const collection = CollectionWithIdExport.toDomain(c);
if (collection != null) {
collection.organizationId = this.organizationId;
const view = await collection.decrypt();
groupingsMap.set(c.id, this.result.collections.length);
this.result.collections.push(view);
}
}
} else if (!this.organization && this.results.folders != null) {
for (const f of this.results.folders as FolderWithIdExport[]) {
const folder = FolderWithIdExport.toDomain(f);
if (folder != null) {
const view = await folder.decrypt();
groupingsMap.set(f.id, this.result.folders.length);
this.result.folders.push(view);
}
}
}
for (const c of this.results.items as CipherWithIdExport[]) {
for (const c of results.items) {
const cipher = CipherWithIdExport.toDomain(c);
// reset ids incase they were set for some reason
cipher.id = null;
@@ -113,28 +104,14 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
this.result.success = true;
}
private parseDecrypted() {
const groupingsMap = new Map<string, number>();
if (this.organization && this.results.collections != null) {
this.results.collections.forEach((c: CollectionWithIdExport) => {
const collection = CollectionWithIdExport.toView(c);
if (collection != null) {
collection.organizationId = null;
groupingsMap.set(c.id, this.result.collections.length);
this.result.collections.push(collection);
}
});
} else if (!this.organization && this.results.folders != null) {
this.results.folders.forEach((f: FolderWithIdExport) => {
const folder = FolderWithIdExport.toView(f);
if (folder != null) {
groupingsMap.set(f.id, this.result.folders.length);
this.result.folders.push(folder);
}
});
}
private async parseDecrypted(
results: BitwardenUnEncryptedIndividualJsonExport | BitwardenUnEncryptedOrgJsonExport
) {
const groupingsMap = this.organization
? await this.parseCollections(results as BitwardenUnEncryptedOrgJsonExport)
: await this.parseFolders(results as BitwardenUnEncryptedIndividualJsonExport);
this.results.items.forEach((c: CipherWithIdExport) => {
results.items.forEach((c) => {
const cipher = CipherWithIdExport.toView(c);
// reset ids incase they were set for some reason
cipher.id = null;
@@ -168,4 +145,60 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
this.result.success = true;
}
private async parseFolders(
data: BitwardenUnEncryptedIndividualJsonExport | BitwardenEncryptedIndividualJsonExport
): Promise<Map<string, number>> | null {
if (data.folders == null) {
return null;
}
const groupingsMap = new Map<string, number>();
for (const f of data.folders) {
let folderView: FolderView;
if (data.encrypted) {
const folder = FolderWithIdExport.toDomain(f);
if (folder != null) {
folderView = await folder.decrypt();
}
} else {
folderView = FolderWithIdExport.toView(f);
}
if (folderView != null) {
groupingsMap.set(f.id, this.result.folders.length);
this.result.folders.push(folderView);
}
}
return groupingsMap;
}
private async parseCollections(
data: BitwardenUnEncryptedOrgJsonExport | BitwardenEncryptedOrgJsonExport
): Promise<Map<string, number>> | null {
if (data.collections == null) {
return null;
}
const groupingsMap = new Map<string, number>();
for (const c of data.collections) {
let collectionView: CollectionView;
if (data.encrypted) {
const collection = CollectionWithIdExport.toDomain(c);
collection.organizationId = this.organizationId;
collectionView = await collection.decrypt();
} else {
collectionView = CollectionWithIdExport.toView(c);
collectionView.organizationId = null;
}
if (collectionView != null) {
groupingsMap.set(c.id, this.result.collections.length);
this.result.collections.push(collectionView);
}
}
return groupingsMap;
}
}

View File

@@ -4,7 +4,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { BitwardenPasswordProtectedFileFormat } from "@bitwarden/exporter/vault-export/bitwarden-password-protected-types";
import { BitwardenPasswordProtectedFileFormat } from "@bitwarden/exporter/vault-export/bitwarden-json-export-types";
import { ImportResult } from "../../models/import-result";
import { Importer } from "../importer";