2018-12-17 19:21:06 +01:00
|
|
|
import { BaseImporter } from './baseImporter';
|
|
|
|
import { Importer } from './importer';
|
|
|
|
|
2021-05-13 02:58:59 +02:00
|
|
|
import { EncString } from '../models/domain/encString';
|
2018-12-17 19:21:06 +01:00
|
|
|
import { ImportResult } from '../models/domain/importResult';
|
|
|
|
|
|
|
|
import { CipherWithIds } from '../models/export/cipherWithIds';
|
|
|
|
import { CollectionWithId } from '../models/export/collectionWithId';
|
|
|
|
import { FolderWithId } from '../models/export/folderWithId';
|
|
|
|
|
2021-05-13 02:58:59 +02:00
|
|
|
import { CryptoService } from '../abstractions/crypto.service';
|
|
|
|
import { I18nService } from '../abstractions/i18n.service';
|
|
|
|
|
2018-12-17 19:21:06 +01:00
|
|
|
export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
2020-12-05 03:05:11 +01:00
|
|
|
private results: any;
|
|
|
|
private result: ImportResult;
|
|
|
|
|
2021-05-13 02:58:59 +02:00
|
|
|
constructor(private cryptoService: CryptoService, private i18nService: I18nService) {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
2020-12-05 03:05:11 +01:00
|
|
|
async parse(data: string): Promise<ImportResult> {
|
|
|
|
this.result = new ImportResult();
|
|
|
|
this.results = JSON.parse(data);
|
|
|
|
if (this.results == null || this.results.items == null || this.results.items.length === 0) {
|
|
|
|
this.result.success = false;
|
|
|
|
return this.result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.results.encrypted) {
|
|
|
|
await this.parseEncrypted();
|
|
|
|
} else {
|
|
|
|
this.parseDecrypted();
|
2018-12-17 19:21:06 +01:00
|
|
|
}
|
|
|
|
|
2020-12-05 03:05:11 +01:00
|
|
|
return this.result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async parseEncrypted() {
|
2021-05-13 02:58:59 +02:00
|
|
|
if (this.results.encKeyValidation_DO_NOT_EDIT != null) {
|
2021-05-27 22:48:10 +02:00
|
|
|
const orgKey = await this.cryptoService.getOrgKey(this.organizationId);
|
2021-05-13 02:58:59 +02:00
|
|
|
const encKeyValidation = new EncString(this.results.encKeyValidation_DO_NOT_EDIT);
|
2021-05-27 22:48:10 +02:00
|
|
|
const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8(encKeyValidation, orgKey);
|
2021-05-13 02:58:59 +02:00
|
|
|
if (encKeyValidationDecrypt === null) {
|
|
|
|
this.result.success = false;
|
|
|
|
this.result.errorMessage = this.i18nService.t('importEncKeyError');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 19:21:06 +01:00
|
|
|
const groupingsMap = new Map<string, number>();
|
2020-12-05 03:05:11 +01:00
|
|
|
|
|
|
|
if (this.organization && this.results.collections != null) {
|
|
|
|
for (const c of this.results.collections as CollectionWithId[]) {
|
|
|
|
const collection = CollectionWithId.toDomain(c);
|
|
|
|
if (collection != null) {
|
|
|
|
collection.id = 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 FolderWithId[]) {
|
|
|
|
const folder = FolderWithId.toDomain(f);
|
|
|
|
if (folder != null) {
|
|
|
|
folder.id = 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 CipherWithIds[]) {
|
|
|
|
const cipher = CipherWithIds.toDomain(c);
|
|
|
|
// reset ids incase they were set for some reason
|
|
|
|
cipher.id = null;
|
|
|
|
cipher.folderId = null;
|
|
|
|
cipher.organizationId = this.organizationId;
|
|
|
|
cipher.collectionIds = null;
|
|
|
|
|
|
|
|
// make sure password history is limited
|
|
|
|
if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) {
|
|
|
|
cipher.passwordHistory = cipher.passwordHistory.slice(0, 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) {
|
|
|
|
this.result.folderRelationships.push([this.result.ciphers.length, groupingsMap.get(c.folderId)]);
|
|
|
|
} else if (this.organization && c.collectionIds != null) {
|
2021-02-04 16:49:23 +01:00
|
|
|
c.collectionIds.forEach(cId => {
|
2020-12-05 03:05:11 +01:00
|
|
|
if (groupingsMap.has(cId)) {
|
|
|
|
this.result.collectionRelationships.push([this.result.ciphers.length, groupingsMap.get(cId)]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const view = await cipher.decrypt();
|
|
|
|
this.cleanupCipher(view);
|
|
|
|
this.result.ciphers.push(view);
|
|
|
|
}
|
2021-05-13 02:58:59 +02:00
|
|
|
|
|
|
|
this.result.success = true;
|
2020-12-05 03:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private parseDecrypted() {
|
|
|
|
const groupingsMap = new Map<string, number>();
|
|
|
|
if (this.organization && this.results.collections != null) {
|
|
|
|
this.results.collections.forEach((c: CollectionWithId) => {
|
2018-12-17 19:21:06 +01:00
|
|
|
const collection = CollectionWithId.toView(c);
|
|
|
|
if (collection != null) {
|
|
|
|
collection.id = null;
|
|
|
|
collection.organizationId = null;
|
2020-12-05 03:05:11 +01:00
|
|
|
groupingsMap.set(c.id, this.result.collections.length);
|
|
|
|
this.result.collections.push(collection);
|
2018-12-17 19:21:06 +01:00
|
|
|
}
|
|
|
|
});
|
2020-12-05 03:05:11 +01:00
|
|
|
} else if (!this.organization && this.results.folders != null) {
|
|
|
|
this.results.folders.forEach((f: FolderWithId) => {
|
2018-12-17 19:21:06 +01:00
|
|
|
const folder = FolderWithId.toView(f);
|
|
|
|
if (folder != null) {
|
|
|
|
folder.id = null;
|
2020-12-05 03:05:11 +01:00
|
|
|
groupingsMap.set(f.id, this.result.folders.length);
|
|
|
|
this.result.folders.push(folder);
|
2018-12-17 19:21:06 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-12-05 03:05:11 +01:00
|
|
|
this.results.items.forEach((c: CipherWithIds) => {
|
2018-12-17 19:21:06 +01:00
|
|
|
const cipher = CipherWithIds.toView(c);
|
|
|
|
// reset ids incase they were set for some reason
|
|
|
|
cipher.id = null;
|
|
|
|
cipher.folderId = null;
|
|
|
|
cipher.organizationId = null;
|
|
|
|
cipher.collectionIds = null;
|
|
|
|
|
2019-03-25 14:15:38 +01:00
|
|
|
// make sure password history is limited
|
|
|
|
if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) {
|
|
|
|
cipher.passwordHistory = cipher.passwordHistory.slice(0, 5);
|
|
|
|
}
|
|
|
|
|
2018-12-17 19:21:06 +01:00
|
|
|
if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) {
|
2020-12-05 03:05:11 +01:00
|
|
|
this.result.folderRelationships.push([this.result.ciphers.length, groupingsMap.get(c.folderId)]);
|
2018-12-17 19:21:06 +01:00
|
|
|
} else if (this.organization && c.collectionIds != null) {
|
2021-02-04 16:49:23 +01:00
|
|
|
c.collectionIds.forEach(cId => {
|
2018-12-17 19:21:06 +01:00
|
|
|
if (groupingsMap.has(cId)) {
|
2020-12-05 03:05:11 +01:00
|
|
|
this.result.collectionRelationships.push([this.result.ciphers.length, groupingsMap.get(cId)]);
|
2018-12-17 19:21:06 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
this.cleanupCipher(cipher);
|
2020-12-05 03:05:11 +01:00
|
|
|
this.result.ciphers.push(cipher);
|
2018-12-17 19:21:06 +01:00
|
|
|
});
|
2021-05-13 02:58:59 +02:00
|
|
|
|
|
|
|
this.result.success = true;
|
2018-12-17 19:21:06 +01:00
|
|
|
}
|
|
|
|
}
|