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:
committed by
GitHub
parent
b56bb19c02
commit
15f29c5fb1
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user