diff --git a/common/src/enums/importOptions.ts b/common/src/enums/importOptions.ts index 5b690196d9..01773b60ac 100644 --- a/common/src/enums/importOptions.ts +++ b/common/src/enums/importOptions.ts @@ -12,11 +12,12 @@ export const featuredImportOptions = [ { id: "keepass2xml", name: "KeePass 2 (xml)" }, { id: "lastpasscsv", name: "LastPass (csv)" }, { id: "safaricsv", name: "Safari and macOS (csv)" }, - { id: "1password1pif", name: "1Password (1pif)" }, + { id: "1password1pux", name: "1Password (1pux)" }, ] as const; export const regularImportOptions = [ { id: "keepassxcsv", name: "KeePassX (csv)" }, + { id: "1password1pif", name: "1Password (1pif)" }, { id: "1passwordwincsv", name: "1Password 6 and 7 Windows (csv)" }, { id: "1passwordmaccsv", name: "1Password 6 and 7 Mac (csv)" }, { id: "roboformcsv", name: "RoboForm (csv)" }, diff --git a/common/src/importers/baseImporter.ts b/common/src/importers/baseImporter.ts index f24d1f193a..122b34724d 100644 --- a/common/src/importers/baseImporter.ts +++ b/common/src/importers/baseImporter.ts @@ -446,4 +446,21 @@ export abstract class BaseImporter { cipher.secureNote.type = SecureNoteType.Generic; } } + + protected processFullName(cipher: CipherView, fullName: string) { + if (this.isNullOrWhitespace(fullName)) { + return; + } + + const nameParts = fullName.split(" "); + if (nameParts.length > 0) { + cipher.identity.firstName = this.getValueOrDefault(nameParts[0]); + } + if (nameParts.length === 2) { + cipher.identity.lastName = this.getValueOrDefault(nameParts[1]); + } else if (nameParts.length >= 3) { + cipher.identity.middleName = this.getValueOrDefault(nameParts[1]); + cipher.identity.lastName = nameParts.slice(2, nameParts.length).join(" "); + } + } } diff --git a/common/src/importers/nordpassCsvImporter.ts b/common/src/importers/nordpassCsvImporter.ts index cca0ef03dc..225e7ec2ae 100644 --- a/common/src/importers/nordpassCsvImporter.ts +++ b/common/src/importers/nordpassCsvImporter.ts @@ -72,7 +72,7 @@ export class NordPassCsvImporter extends BaseImporter implements Importer { case CipherType.Identity: cipher.type = CipherType.Identity; - this.processName(cipher, this.getValueOrDefault(record.full_name)); + this.processFullName(cipher, this.getValueOrDefault(record.full_name)); cipher.identity.address1 = this.getValueOrDefault(record.address1); cipher.identity.address2 = this.getValueOrDefault(record.address2); cipher.identity.city = this.getValueOrDefault(record.city); @@ -124,21 +124,4 @@ export class NordPassCsvImporter extends BaseImporter implements Importer { return undefined; } - - private processName(cipher: CipherView, fullName: string) { - if (this.isNullOrWhitespace(fullName)) { - return; - } - - const nameParts = fullName.split(" "); - if (nameParts.length > 0) { - cipher.identity.firstName = this.getValueOrDefault(nameParts[0]); - } - if (nameParts.length === 2) { - cipher.identity.lastName = this.getValueOrDefault(nameParts[1]); - } else if (nameParts.length >= 3) { - cipher.identity.middleName = this.getValueOrDefault(nameParts[1]); - cipher.identity.lastName = nameParts.slice(2, nameParts.length).join(" "); - } - } } diff --git a/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts b/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts new file mode 100644 index 0000000000..ad30100f8a --- /dev/null +++ b/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts @@ -0,0 +1,617 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { LoginView } from "../../models/view/loginView"; +import { PasswordHistoryView } from "../../models/view/passwordHistoryView"; +import { SecureNoteView } from "../../models/view/secureNoteView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { + CategoryEnum, + Details, + ExportData, + FieldsEntity, + Item, + ItemCollection, + LoginFieldTypeEnum, + Overview, + PasswordHistoryEntity, + SectionsEntity, + UrlsEntity, + Value, + VaultsEntity, +} from "./types/onepassword1PuxImporterTypes"; + +export class OnePassword1PuxImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + parse(data: string): Promise { + const exportData: ExportData = JSON.parse(data); + + const account = exportData.accounts[0]; + // TODO Add handling of multiple vaults + // const personalVaults = account.vaults[0].filter((v) => v.attrs.type === VaultAttributeTypeEnum.Personal); + account.vaults.forEach((vault: VaultsEntity) => { + vault.items.forEach((itemCollection: ItemCollection) => { + const item: Item = itemCollection.item; + if (item.trashed === true) { + return; + } + + const cipher = this.initLoginCipher(); + + const category = item.categoryUuid as CategoryEnum; + switch (category) { + case CategoryEnum.Login: + case CategoryEnum.Database: + case CategoryEnum.Password: + case CategoryEnum.WirelessRouter: + case CategoryEnum.Server: + case CategoryEnum.API_Credential: + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + break; + case CategoryEnum.CreditCard: + case CategoryEnum.BankAccount: + cipher.type = CipherType.Card; + cipher.card = new CardView(); + break; + case CategoryEnum.SecureNote: + case CategoryEnum.SoftwareLicense: + case CategoryEnum.EmailAccount: + case CategoryEnum.MedicalRecord: + // case CategoryEnum.Document: + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + break; + case CategoryEnum.Identity: + case CategoryEnum.DriversLicense: + case CategoryEnum.OutdoorLicense: + case CategoryEnum.Membership: + case CategoryEnum.Passport: + case CategoryEnum.RewardsProgram: + case CategoryEnum.SocialSecurityNumber: + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + break; + default: + break; + } + + cipher.favorite = item.favIndex === 1 ? true : false; + + this.processOverview(item.overview, cipher); + + this.processLoginFields(item, cipher); + + this.processDetails(category, item.details, cipher); + + this.parsePasswordHistory(item.details.passwordHistory, cipher); + + this.processSections(category, item.details.sections, cipher); + + if (!this.isNullOrWhitespace(item.details.notesPlain)) { + cipher.notes = item.details.notesPlain.split(this.newLineRegex).join("\n") + "\n"; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + }); + + if (this.organization) { + this.moveFoldersToCollections(this.result); + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + private processOverview(overview: Overview, cipher: CipherView) { + if (overview == null) { + return; + } + + cipher.name = this.getValueOrDefault(overview.title); + + if (overview.urls != null) { + const urls: string[] = []; + overview.urls.forEach((url: UrlsEntity) => { + if (!this.isNullOrWhitespace(url.url)) { + urls.push(url.url); + } + }); + cipher.login.uris = this.makeUriArray(urls); + } + + if (overview.tags != null && overview.tags.length > 0) { + const folderName = this.capitalize(overview.tags[0]); + this.processFolder(this.result, folderName); + } + } + + private capitalize(inputString: string): string { + return inputString.trim().replace(/\w\S*/g, (w) => w.replace(/^\w/, (c) => c.toUpperCase())); + } + + private processLoginFields(item: Item, cipher: CipherView) { + if (item.details == null) { + return; + } + + if (item.details.loginFields == null || item.details.loginFields.length === 0) { + return; + } + + item.details.loginFields.forEach((loginField) => { + if (loginField.designation === "username" && loginField.value !== "") { + cipher.type = CipherType.Login; + cipher.login.username = loginField.value; + return; + } + + if (loginField.designation === "password" && loginField.value !== "") { + cipher.type = CipherType.Login; + cipher.login.password = loginField.value; + return; + } + + let fieldValue = loginField.value; + let fieldType: FieldType = FieldType.Text; + switch (loginField.fieldType) { + case LoginFieldTypeEnum.Password: + fieldType = FieldType.Hidden; + break; + case LoginFieldTypeEnum.CheckBox: + fieldValue = loginField.value !== "" ? "true" : "false"; + fieldType = FieldType.Boolean; + break; + default: + break; + } + this.processKvp(cipher, loginField.name, fieldValue, fieldType); + }); + } + + private processDetails(category: CategoryEnum, details: Details, cipher: CipherView) { + if (category !== CategoryEnum.Password) { + return; + } + + if (details == null) { + return; + } + cipher.login.password = details.password; + } + + private processSections(category: CategoryEnum, sections: SectionsEntity[], cipher: CipherView) { + if (sections == null || sections.length === 0) { + return; + } + + sections.forEach((section: SectionsEntity) => { + if (section.fields == null) { + return; + } + + this.parseSectionFields(category, section.fields, cipher); + }); + } + + private parseSectionFields(category: CategoryEnum, fields: FieldsEntity[], cipher: CipherView) { + fields.forEach((field: FieldsEntity) => { + const valueKey = Object.keys(field.value)[0]; + const anyField = field as any; + + if ( + anyField.value == null || + anyField.value[valueKey] == null || + anyField.value[valueKey] === "" + ) { + return; + } + + const fieldName = this.getFieldName(field.id, field.title); + const fieldValue = this.extractValue(field.value, valueKey); + + if (cipher.type === CipherType.Login) { + if (this.fillLogin(field, fieldValue, cipher)) { + return; + } + + switch (category) { + case CategoryEnum.Login: + case CategoryEnum.Database: + case CategoryEnum.EmailAccount: + case CategoryEnum.WirelessRouter: + break; + + case CategoryEnum.Server: + if (this.isNullOrWhitespace(cipher.login.uri) && field.id === "url") { + cipher.login.uris = this.makeUriArray(fieldValue); + return; + } + break; + + case CategoryEnum.API_Credential: + if (this.fillApiCredentials(field, fieldValue, cipher)) { + return; + } + break; + default: + break; + } + } else if (cipher.type === CipherType.Card) { + if (this.fillCreditCard(field, fieldValue, cipher)) { + return; + } + + if (category === CategoryEnum.BankAccount) { + if (this.fillBankAccount(field, fieldValue, cipher)) { + return; + } + } + } else if (cipher.type === CipherType.Identity) { + if (this.fillIdentity(field, fieldValue, cipher)) { + return; + } + if (valueKey === "address") { + // fieldValue is an object casted into a string, so access the plain value instead + const { street, city, country, zip, state } = field.value.address; + cipher.identity.address1 = this.getValueOrDefault(street); + cipher.identity.city = this.getValueOrDefault(city); + if (!this.isNullOrWhitespace(country)) { + cipher.identity.country = country.toUpperCase(); + } + cipher.identity.postalCode = this.getValueOrDefault(zip); + cipher.identity.state = this.getValueOrDefault(state); + return; + } + + switch (category) { + case CategoryEnum.Identity: + break; + case CategoryEnum.DriversLicense: + if (this.fillDriversLicense(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.OutdoorLicense: + if (this.fillOutdoorLicense(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.Membership: + if (this.fillMembership(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.Passport: + if (this.fillPassport(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.RewardsProgram: + if (this.fillRewardsProgram(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.SocialSecurityNumber: + if (this.fillSSN(field, fieldValue, cipher)) { + return; + } + break; + default: + break; + } + } + + // Do not include a password field if it's already in the history + if ( + field.title === "password" && + cipher.passwordHistory != null && + cipher.passwordHistory.some((h) => h.password === fieldValue) + ) { + return; + } + + // TODO ?? If one of the fields is marked as guarded, then activate Password-Reprompt for the entire item + if (field.guarded && cipher.reprompt === CipherRepromptType.None) { + cipher.reprompt = CipherRepromptType.Password; + } + + const fieldType = valueKey === "concealed" ? FieldType.Hidden : FieldType.Text; + this.processKvp(cipher, fieldName, fieldValue, fieldType); + }); + } + + private getFieldName(id: string, title: string): string { + if (this.isNullOrWhitespace(title)) { + return id; + } + + // Naive approach of checking if the fields id is usable + if (id.length > 25 && RegExp(/[0-9]{2}[A-Z]{2}/, "i").test(id)) { + return title; + } + return id; + } + + private extractValue(value: Value, valueKey: string): string { + if (valueKey === "date") { + return new Date(value.date * 1000).toUTCString(); + } + + if (valueKey === "monthYear") { + return value.monthYear.toString(); + } + + return (value as any)[valueKey]; + } + + private fillLogin(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + const fieldName = this.getFieldName(field.id, field.title); + + if (this.isNullOrWhitespace(cipher.login.username) && fieldName === "username") { + cipher.login.username = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "password") { + cipher.login.password = fieldValue; + return true; + } + + if ( + this.isNullOrWhitespace(cipher.login.totp) && + field.id != null && + field.id.startsWith("TOTP_") + ) { + cipher.login.totp = fieldValue; + return true; + } + + return false; + } + + private fillApiCredentials(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + const fieldName = this.getFieldName(field.id, field.title); + + if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "credential") { + cipher.login.password = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.login.uri) && fieldName === "hostname") { + cipher.login.uris = this.makeUriArray(fieldValue); + return true; + } + + return false; + } + + private fillCreditCard(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.card.number) && field.id === "ccnum") { + cipher.card.number = fieldValue; + cipher.card.brand = this.getCardBrand(fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.card.code) && field.id === "cvv") { + cipher.card.code = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "cardholder") { + cipher.card.cardholderName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.card.expiration) && field.id === "expiry") { + const monthYear: string = fieldValue.toString().trim(); + cipher.card.expMonth = monthYear.substring(4, 6); + if (cipher.card.expMonth[0] === "0") { + cipher.card.expMonth = cipher.card.expMonth.substring(1, 2); + } + cipher.card.expYear = monthYear.substring(0, 4); + return true; + } + + if (field.id === "type") { + // Skip since brand was determined from number above + return true; + } + + return false; + } + + private fillBankAccount(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "owner") { + cipher.card.cardholderName = fieldValue; + return true; + } + + return false; + } + + private fillIdentity(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "firstname") { + cipher.identity.firstName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.lastName) && field.id === "lastname") { + cipher.identity.lastName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.middleName) && field.id === "initial") { + cipher.identity.middleName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "defphone") { + cipher.identity.phone = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company") { + cipher.identity.company = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.email) && field.id === "email") { + cipher.identity.email = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.username) && field.id === "username") { + cipher.identity.username = fieldValue; + return true; + } + return false; + } + + private fillDriversLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.address1) && field.id === "address") { + cipher.identity.address1 = fieldValue; + return true; + } + + // TODO ISO code + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") { + cipher.identity.state = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.licenseNumber) && field.id === "number") { + cipher.identity.licenseNumber = fieldValue; + return true; + } + + return false; + } + + private fillOutdoorLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") { + this.processFullName(cipher, fieldValue); + return true; + } + + // TODO ISO code + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") { + cipher.identity.state = fieldValue; + return true; + } + + return false; + } + + private fillMembership(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "org_name") { + cipher.identity.company = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "phone") { + cipher.identity.phone = fieldValue; + return true; + } + + return false; + } + + private fillPassport(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") { + this.processFullName(cipher, fieldValue); + return true; + } + + // TODO Iso + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "issuing_country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.passportNumber) && field.id === "number") { + cipher.identity.passportNumber = fieldValue; + return true; + } + + return false; + } + + private fillRewardsProgram(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company_name") { + cipher.identity.company = fieldValue; + return true; + } + + return false; + } + + private fillSSN(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.ssn) && field.id === "number") { + cipher.identity.ssn = fieldValue; + return true; + } + + return false; + } + + private parsePasswordHistory(historyItems: PasswordHistoryEntity[], cipher: CipherView) { + if (historyItems == null || historyItems.length === 0) { + return; + } + + const maxSize = historyItems.length > 5 ? 5 : historyItems.length; + cipher.passwordHistory = historyItems + .filter((h: any) => !this.isNullOrWhitespace(h.value) && h.time != null) + .sort((a, b) => b.time - a.time) + .slice(0, maxSize) + .map((h: any) => { + const ph = new PasswordHistoryView(); + ph.password = h.value; + ph.lastUsedDate = new Date(("" + h.time).length >= 13 ? h.time : h.time * 1000); + return ph; + }); + } +} diff --git a/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts b/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts new file mode 100644 index 0000000000..eaefcd86e6 --- /dev/null +++ b/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts @@ -0,0 +1,157 @@ +export interface ExportData { + accounts?: AccountsEntity[] | null; +} +export interface AccountsEntity { + attrs: AccountAttributes; + vaults?: VaultsEntity[] | null; +} +export interface AccountAttributes { + accountName: string; + name: string; + avatar: string; + email: string; + uuid: string; + domain: string; +} +export interface VaultsEntity { + attrs: VaultAttributes; + items?: ItemCollection[] | null; +} +export interface VaultAttributes { + uuid: string; + desc: string; + avatar: string; + name: string; + type: string; +} +export interface ItemCollection { + item: Item; +} + +export enum CategoryEnum { + Login = "001", + CreditCard = "002", + SecureNote = "003", + Identity = "004", + Password = "005", + Document = "006", + SoftwareLicense = "100", + BankAccount = "101", + Database = "102", + DriversLicense = "103", + OutdoorLicense = "104", + Membership = "105", + Passport = "106", + RewardsProgram = "107", + SocialSecurityNumber = "108", + WirelessRouter = "109", + Server = "110", + EmailAccount = "111", + API_Credential = "112", + MedicalRecord = "113", +} + +export interface Item { + uuid: string; + favIndex: number; + createdAt: number; + updatedAt: number; + trashed: boolean; + categoryUuid: string; + details: Details; + overview: Overview; +} +export interface Details { + loginFields?: (LoginFieldsEntity | null)[] | null; + notesPlain?: string | null; + sections?: (SectionsEntity | null)[] | null; + passwordHistory?: (PasswordHistoryEntity | null)[] | null; + documentAttributes?: DocumentAttributes | null; + password?: string | null; +} + +export enum LoginFieldTypeEnum { + TextOrHtml = "T", + EmailAddress = "E", + URL = "U", + Number = "N", + Password = "P", + TextArea = "A", + PhoneNumber = "T", + CheckBox = "C", +} +export interface LoginFieldsEntity { + value: string; + id: string; + name: string; + fieldType: LoginFieldTypeEnum | string; + designation?: string | null; +} +export interface SectionsEntity { + title: string; + name?: string | null; + fields?: FieldsEntity[] | null; +} +export interface FieldsEntity { + title: string; + id: string; + value: Value; + indexAtSource: number; + guarded: boolean; + multiline: boolean; + dontGenerate: boolean; + placeholder?: string; + inputTraits: InputTraits; + clipboardFilter?: string | null; +} +export interface Value { + totp?: string | null; + date?: number | null; + string?: string | null; + concealed?: string | null; + email?: string | null; + phone?: string | null; + menu?: string | null; + gender?: string | null; + monthYear?: number | null; + url?: string | null; + address?: Address | null; + creditCardType?: string | null; + creditCardNumber?: string | null; + reference?: string | null; +} +export interface Address { + street: string; + city: string; + country: string; + zip: string; + state: string; +} +export interface InputTraits { + keyboard: string; + correction: string; + capitalization: string; +} +export interface PasswordHistoryEntity { + value: string; + time: number; +} +export interface DocumentAttributes { + fileName: string; + documentId: string; + decryptedSize: number; +} +export interface Overview { + subtitle: string; + title: string; + url: string; + urls?: UrlsEntity[] | null; + ps?: number | null; + pbe?: number | null; + pgrng?: boolean | null; + tags?: string[] | null; +} +export interface UrlsEntity { + label: string; + url: string; +} diff --git a/common/src/services/import.service.ts b/common/src/services/import.service.ts index 3913074d0d..1a59b20acc 100644 --- a/common/src/services/import.service.ts +++ b/common/src/services/import.service.ts @@ -46,6 +46,7 @@ import { MSecureCsvImporter } from "../importers/msecureCsvImporter"; import { MykiCsvImporter } from "../importers/mykiCsvImporter"; import { NordPassCsvImporter } from "../importers/nordpassCsvImporter"; import { OnePassword1PifImporter } from "../importers/onepasswordImporters/onepassword1PifImporter"; +import { OnePassword1PuxImporter } from "../importers/onepasswordImporters/onepassword1PuxImporter"; import { OnePasswordMacCsvImporter } from "../importers/onepasswordImporters/onepasswordMacCsvImporter"; import { OnePasswordWinCsvImporter } from "../importers/onepasswordImporters/onepasswordWinCsvImporter"; import { PadlockCsvImporter } from "../importers/padlockCsvImporter"; @@ -199,6 +200,8 @@ export class ImportService implements ImportServiceAbstraction { return new MeldiumCsvImporter(); case "1password1pif": return new OnePassword1PifImporter(); + case "1password1pux": + return new OnePassword1PuxImporter(); case "1passwordwincsv": return new OnePasswordWinCsvImporter(); case "1passwordmaccsv": diff --git a/spec/common/importers/onepassword1PuxImporter.spec.ts b/spec/common/importers/onepassword1PuxImporter.spec.ts new file mode 100644 index 0000000000..84ab8f2559 --- /dev/null +++ b/spec/common/importers/onepassword1PuxImporter.spec.ts @@ -0,0 +1,629 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { OnePassword1PuxImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepassword1PuxImporter"; +import { Utils } from "jslib-common/misc/utils"; +import { FieldView } from "jslib-common/models/view/fieldView"; + +import { APICredentialsData } from "./testData/onePassword1Pux/APICredentials"; +import { BankAccountData } from "./testData/onePassword1Pux/BankAccount"; +import { CreditCardData } from "./testData/onePassword1Pux/CreditCard"; +import { DatabaseData } from "./testData/onePassword1Pux/Database"; +import { DriversLicenseData } from "./testData/onePassword1Pux/DriversLicense"; +import { EmailAccountData } from "./testData/onePassword1Pux/EmailAccount"; +import { IdentityData } from "./testData/onePassword1Pux/IdentityData"; +import { LoginData } from "./testData/onePassword1Pux/LoginData"; +import { MedicalRecordData } from "./testData/onePassword1Pux/MedicalRecord"; +import { MembershipData } from "./testData/onePassword1Pux/Membership"; +import { OnePuxExampleFile } from "./testData/onePassword1Pux/Onepux_example"; +import { OutdoorLicenseData } from "./testData/onePassword1Pux/OutdoorLicense"; +import { PassportData } from "./testData/onePassword1Pux/Passport"; +import { PasswordData } from "./testData/onePassword1Pux/Password"; +import { RewardsProgramData } from "./testData/onePassword1Pux/RewardsProgram"; +import { SSNData } from "./testData/onePassword1Pux/SSN"; +import { SanitizedExport } from "./testData/onePassword1Pux/SanitizedExport"; +import { SecureNoteData } from "./testData/onePassword1Pux/SecureNote"; +import { ServerData } from "./testData/onePassword1Pux/Server"; +import { SoftwareLicenseData } from "./testData/onePassword1Pux/SoftwareLicense"; +import { WirelessRouterData } from "./testData/onePassword1Pux/WirelessRouter"; + +function validateCustomField(fields: FieldView[], fieldName: string, expectedValue: any) { + expect(fields).toBeDefined(); + const customField = fields.find((f) => f.name === fieldName); + expect(customField).withContext(`CustomField: ('${fieldName}') was not found`).toBeDefined(); + + expect(customField.value) + .withContext(`Customfield: ('${fieldName}'), should be equal to: '${expectedValue}'`) + .toEqual(expectedValue); +} + +describe("1Password 1Pux Importer", async () => { + const OnePuxExampleFileJson = JSON.stringify(OnePuxExampleFile); + const LoginDataJson = JSON.stringify(LoginData); + const CreditCardDataJson = JSON.stringify(CreditCardData); + const IdentityDataJson = JSON.stringify(IdentityData); + const SecureNoteDataJson = JSON.stringify(SecureNoteData); + const SanitizedExportJson = JSON.stringify(SanitizedExport); + + it("should parse login data", async () => { + const importer = new Importer(); + const result = await importer.parse(LoginDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("eToro"); + + expect(cipher.login.username).toEqual("username123123123@gmail.com"); + expect(cipher.login.password).toEqual("password!"); + expect(cipher.login.uris.length).toEqual(1); + expect(cipher.login.uri).toEqual("https://www.fakesite.com"); + expect(cipher.login.totp).toEqual("otpseed777"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(3); + validateCustomField(cipher.fields, "terms", "false"); + validateCustomField(cipher.fields, "policies", "true"); + validateCustomField(cipher.fields, "cyqyggt2otns6tbbqtsl6w2ceu", "username123123"); + }); + + it("should parse notes", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.notes).toEqual("This is a note. *bold*! _italic_!"); + }); + + it("should set favourite if favIndex equals 1", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.favorite).toBeTrue(); + }); + + it("should handle custom boolean fields", async () => { + const importer = new Importer(); + const result = await importer.parse(LoginDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + expect(cipher.fields[0].name).toEqual("terms"); + expect(cipher.fields[0].value).toEqual("false"); + expect(cipher.fields[0].type).toBe(FieldType.Boolean); + + expect(cipher.fields[1].name).toEqual("policies"); + expect(cipher.fields[1].value).toEqual("true"); + expect(cipher.fields[1].type).toBe(FieldType.Boolean); + }); + + it('should create concealed field as "hidden" type', async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + const fields = cipher.fields; + expect(fields.length).toEqual(1); + + const field = fields.shift(); + expect(field.name).toEqual("PIN"); + expect(field.value).toEqual("12345"); + expect(field.type).toEqual(FieldType.Hidden); + }); + + it("should create password history", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + const cipher = result.ciphers.shift(); + + expect(cipher.passwordHistory.length).toEqual(1); + const ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("12345password"); + expect(ph.lastUsedDate.toISOString()).toEqual("2016-03-18T17:32:35.000Z"); + }); + + it("should create credit card records", async () => { + const importer = new Importer(); + const result = await importer.parse(CreditCardDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Parent's Credit Card"); + expect(cipher.notes).toEqual("My parents' credit card."); + + const card = cipher.card; + expect(card.cardholderName).toEqual("Fred Engels"); + expect(card.number).toEqual("6011111111111117"); + expect(card.code).toEqual("1312"); + expect(card.brand).toEqual("Discover"); + expect(card.expMonth).toEqual("12"); + expect(card.expYear).toEqual("2099"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(12); + validateCustomField(cipher.fields, "txbzvwzpck7ejhfres3733rbpm", "card"); + validateCustomField(cipher.fields, "cashLimit", "$500"); + validateCustomField(cipher.fields, "creditLimit", "$1312"); + validateCustomField(cipher.fields, "validFrom", "200101"); + validateCustomField(cipher.fields, "bank", "Some bank"); + validateCustomField(cipher.fields, "phoneLocal", "123456"); + validateCustomField(cipher.fields, "phoneTollFree", "0800123456"); + validateCustomField(cipher.fields, "phoneIntl", "+49123456"); + validateCustomField(cipher.fields, "website", "somebank.com"); + validateCustomField(cipher.fields, "pin", "1234"); + validateCustomField(cipher.fields, "interest", "1%"); + validateCustomField(cipher.fields, "issuenumber", "123456"); + }); + + it("should create identity records", async () => { + const importer = new Importer(); + const result = await importer.parse(IdentityDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("George Engels"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("George"); + expect(identity.middleName).toEqual("S"); + expect(identity.lastName).toEqual("Engels"); + expect(identity.company).toEqual("Acme Inc."); + expect(identity.address1).toEqual("1312 Main St."); + expect(identity.country).toEqual("US"); + expect(identity.state).toEqual("California"); + expect(identity.city).toEqual("Atlantis"); + expect(identity.postalCode).toEqual("90210"); + expect(identity.phone).toEqual("4565555555"); + expect(identity.email).toEqual("gengels@nullvalue.test"); + expect(identity.username).toEqual("gengels"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(17); + validateCustomField(cipher.fields, "sex", "male"); + validateCustomField(cipher.fields, "birthdate", "Thu, 01 Jan 1981 12:01:00 GMT"); + validateCustomField(cipher.fields, "occupation", "Steel Worker"); + validateCustomField(cipher.fields, "department", "QA"); + validateCustomField(cipher.fields, "jobtitle", "Quality Assurance Manager"); + validateCustomField(cipher.fields, "homephone", "4575555555"); + validateCustomField(cipher.fields, "cellphone", "4585555555"); + validateCustomField(cipher.fields, "busphone", "4595555555"); + validateCustomField(cipher.fields, "reminderq", "Who's a super cool guy?"); + validateCustomField(cipher.fields, "remindera", "Me, buddy."); + validateCustomField(cipher.fields, "website", "cv.gengels.nullvalue.test"); + validateCustomField(cipher.fields, "icq", "12345678"); + validateCustomField(cipher.fields, "skype", "skypeisbad1619"); + validateCustomField(cipher.fields, "aim", "aollol@lololol.aol.com"); + validateCustomField(cipher.fields, "yahoo", "sk8rboi13@yah00.com"); + validateCustomField(cipher.fields, "msn", "msnothankyou@msn&m&m.com"); + validateCustomField(cipher.fields, "forumsig", "super cool guy"); + }); + + it("should parse category 005 - Password (Legacy)", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(PasswordData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("SuperSecret Password"); + expect(cipher.notes).toEqual("SuperSecret Password Notes"); + + expect(cipher.login.password).toEqual("GBq[AGb]4*Si3tjwuab^"); + expect(cipher.login.uri).toEqual("https://n0t.y0ur.n0rm4l.w3bs1t3"); + }); + + it("should parse category 100 - SoftwareLicense", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(SoftwareLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Limux Product Key"); + expect(cipher.notes).toEqual("My Software License"); + + expect(cipher.fields.length).toEqual(13); + validateCustomField(cipher.fields, "product_version", "5.10.1000"); + validateCustomField(cipher.fields, "reg_code", "265453-13457355-847327"); + validateCustomField(cipher.fields, "reg_name", "Kay Riddler"); + validateCustomField(cipher.fields, "reg_email", "kriddler@nullvalue.test"); + validateCustomField(cipher.fields, "company", "Riddles and Jigsaw Puzzles GmbH"); + validateCustomField( + cipher.fields, + "download_link", + "https://limuxcompany.nullvalue.test/5.10.1000/isos" + ); + validateCustomField(cipher.fields, "publisher_name", "Limux Software and Hardware"); + validateCustomField(cipher.fields, "publisher_website", "https://limuxcompany.nullvalue.test/"); + validateCustomField(cipher.fields, "retail_price", "$999"); + validateCustomField(cipher.fields, "support_email", "support@nullvalue.test"); + validateCustomField(cipher.fields, "order_date", "Thu, 01 Apr 2021 12:01:00 GMT"); + validateCustomField(cipher.fields, "order_number", "594839"); + validateCustomField(cipher.fields, "order_total", "$1086.59"); + }); + + it("should parse category 101 - BankAccount", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(BankAccountData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Card); + expect(cipher.name).toEqual("Bank Account"); + expect(cipher.notes).toEqual("My Bank Account"); + + expect(cipher.card.cardholderName).toEqual("Cool Guy"); + + expect(cipher.fields.length).toEqual(9); + validateCustomField(cipher.fields, "bankName", "Super Credit Union"); + validateCustomField(cipher.fields, "accountType", "checking"); + validateCustomField(cipher.fields, "routingNo", "111000999"); + validateCustomField(cipher.fields, "accountNo", "192837465918273645"); + validateCustomField(cipher.fields, "swift", "123456"); + validateCustomField(cipher.fields, "iban", "DE12 123456"); + validateCustomField(cipher.fields, "telephonePin", "5555"); + validateCustomField(cipher.fields, "branchPhone", "9399399933"); + validateCustomField(cipher.fields, "branchAddress", "1 Fifth Avenue"); + }); + + it("should parse category 102 - Database", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(DatabaseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Database"); + expect(cipher.notes).toEqual("My Database"); + + const login = cipher.login; + expect(login.username).toEqual("cooldbuser"); + expect(login.password).toEqual("^+kTjhLaN7wVPAhGU)*J"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "database_type", "postgresql"); + validateCustomField(cipher.fields, "hostname", "my.secret.db.server"); + validateCustomField(cipher.fields, "port", "1337"); + validateCustomField(cipher.fields, "database", "user_database"); + validateCustomField(cipher.fields, "sid", "ASDIUFU-283234"); + validateCustomField(cipher.fields, "alias", "cdbu"); + validateCustomField(cipher.fields, "options", "ssh"); + }); + + it("should parse category 103 - Drivers license", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(DriversLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Michael Scarn"); + expect(cipher.subTitle).toEqual("Michael Scarn"); + expect(cipher.notes).toEqual("My Driver's License"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Michael"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Scarn"); + expect(identity.address1).toEqual("2120 Mifflin Rd."); + expect(identity.state).toEqual("Pennsylvania"); + expect(identity.country).toEqual("United States"); + expect(identity.licenseNumber).toEqual("12345678901"); + + expect(cipher.fields.length).toEqual(6); + validateCustomField(cipher.fields, "birthdate", "Sun, 01 Jan 1978 12:01:00 GMT"); + validateCustomField(cipher.fields, "sex", "male"); + validateCustomField(cipher.fields, "height", "5'11\""); + validateCustomField(cipher.fields, "class", "C"); + validateCustomField(cipher.fields, "conditions", "B"); + validateCustomField(cipher.fields, "expiry_date", "203012"); + }); + + it("should parse category 104 - Outdoor License", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(OutdoorLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Harvest License"); + expect(cipher.subTitle).toEqual("Cash Bandit"); + expect(cipher.notes).toEqual("My Outdoor License"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Cash"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Bandit"); + expect(identity.state).toEqual("Washington"); + expect(identity.country).toEqual("United States of America"); + + expect(cipher.fields.length).toEqual(4); + validateCustomField(cipher.fields, "valid_from", "Thu, 01 Apr 2021 12:01:00 GMT"); + validateCustomField(cipher.fields, "expires", "Fri, 01 Apr 2044 12:01:00 GMT"); + validateCustomField(cipher.fields, "game", "Bananas,blueberries,corn"); + validateCustomField(cipher.fields, "quota", "100/each"); + }); + + it("should parse category 105 - Membership", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(MembershipData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Library Card"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("George"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Engels"); + expect(identity.company).toEqual("National Public Library"); + expect(identity.phone).toEqual("9995555555"); + + expect(cipher.fields.length).toEqual(5); + validateCustomField(cipher.fields, "website", "https://npl.nullvalue.gov.test"); + validateCustomField(cipher.fields, "member_since", "199901"); + validateCustomField(cipher.fields, "expiry_date", "203412"); + validateCustomField(cipher.fields, "membership_no", "64783862"); + validateCustomField(cipher.fields, "pin", "19191"); + }); + + it("should parse category 106 - Passport", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(PassportData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Mr. Globewide"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("David"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Global"); + expect(identity.passportNumber).toEqual("76436847"); + + expect(cipher.fields.length).toEqual(8); + validateCustomField(cipher.fields, "type", "US Passport"); + validateCustomField(cipher.fields, "sex", "female"); + validateCustomField(cipher.fields, "nationality", "International"); + validateCustomField(cipher.fields, "issuing_authority", "Department of State"); + validateCustomField(cipher.fields, "birthdate", "Fri, 01 Apr 1983 12:01:00 GMT"); + validateCustomField(cipher.fields, "birthplace", "A cave somewhere in Maine"); + validateCustomField(cipher.fields, "issue_date", "Wed, 01 Jan 2020 12:01:00 GMT"); + validateCustomField(cipher.fields, "expiry_date", "Sat, 01 Jan 2050 12:01:00 GMT"); + }); + + it("should parse category 107 - RewardsProgram", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(RewardsProgramData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Retail Reward Thing"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Chef"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Coldroom"); + expect(identity.company).toEqual("Super Cool Store Co."); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "membership_no", "member-29813569"); + validateCustomField(cipher.fields, "pin", "99913"); + validateCustomField(cipher.fields, "additional_no", "additional member id"); + validateCustomField(cipher.fields, "member_since", "202101"); + validateCustomField(cipher.fields, "customer_service_phone", "123456"); + validateCustomField(cipher.fields, "reservations_phone", "123456"); + validateCustomField(cipher.fields, "website", "supercoolstore.com"); + }); + + it("should parse category 108 - SSN", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(SSNData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("SSN"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Jack"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Judd"); + expect(identity.ssn).toEqual("131-216-1900"); + }); + + it("should parse category 109 - WirelessRouter", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(WirelessRouterData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Wireless Router"); + expect(cipher.notes).toEqual("My Wifi Router Config"); + + expect(cipher.login.password).toEqual("BqatGTVQ9TCN72tLbjrsHqkb"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "name", "pixel 2Xl"); + validateCustomField(cipher.fields, "server", "127.0.0.1"); + validateCustomField(cipher.fields, "airport_id", "some airportId"); + validateCustomField(cipher.fields, "network_name", "some network name"); + validateCustomField(cipher.fields, "wireless_security", "WPA"); + validateCustomField(cipher.fields, "wireless_password", "wifipassword"); + validateCustomField(cipher.fields, "disk_password", "diskpassword"); + }); + + it("should parse category 110 - Server", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(ServerData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Super Cool Server"); + expect(cipher.notes).toEqual("My Server"); + + expect(cipher.login.username).toEqual("frankly-notsure"); + expect(cipher.login.password).toEqual("*&YHJI87yjy78u"); + expect(cipher.login.uri).toEqual("https://coolserver.nullvalue.test"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField( + cipher.fields, + "admin_console_url", + "https://coolserver.nullvalue.test/admin" + ); + validateCustomField(cipher.fields, "admin_console_username", "frankly-idontknowwhatimdoing"); + validateCustomField(cipher.fields, "admin_console_password", "^%RY&^YUiju8iUYHJI(U"); + validateCustomField(cipher.fields, "name", "Private Hosting Provider Inc."); + validateCustomField(cipher.fields, "website", "https://phpi.nullvalue.test"); + validateCustomField( + cipher.fields, + "support_contact_url", + "https://phpi.nullvalue.test/support" + ); + validateCustomField(cipher.fields, "support_contact_phone", "8882569382"); + }); + + it("should parse category 111 - EmailAccount", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(EmailAccountData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Email Config"); + expect(cipher.notes).toEqual("My Email Config"); + + expect(cipher.fields.length).toEqual(17); + validateCustomField(cipher.fields, "pop_type", "either"); + validateCustomField(cipher.fields, "pop_username", "someuser@nullvalue.test"); + validateCustomField(cipher.fields, "pop_server", "mailserver.nullvalue.test"); + validateCustomField(cipher.fields, "pop_port", "587"); + validateCustomField(cipher.fields, "pop_password", "u1jsf { + const importer = new Importer(); + const jsonString = JSON.stringify(APICredentialsData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("API Credential"); + expect(cipher.notes).toEqual("My API Credential"); + + expect(cipher.login.username).toEqual("apiuser@nullvalue.test"); + expect(cipher.login.password).toEqual("apiapiapiapiapiapiappy"); + expect(cipher.login.uri).toEqual("http://not.your.everyday.hostname"); + + expect(cipher.fields.length).toEqual(4); + validateCustomField(cipher.fields, "type", "jwt"); + validateCustomField(cipher.fields, "filename", "filename.jwt"); + validateCustomField(cipher.fields, "validFrom", "Mon, 04 Apr 2011 12:01:00 GMT"); + validateCustomField(cipher.fields, "expires", "Tue, 01 Apr 2031 12:01:00 GMT"); + }); + + it("should create secure notes", async () => { + const importer = new Importer(); + const result = await importer.parse(SecureNoteDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("Secure Note #1"); + expect(cipher.notes).toEqual( + "This is my secure note. \n\nLorem ipsum expecto patronum. \nThe quick brown fox jumped over the lazy dog." + ); + expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic); + }); + + it("should parse category 113 - Medical Record", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(MedicalRecordData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Some Health Record"); + expect(cipher.notes).toEqual("Some notes about my medical history"); + expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic); + + expect(cipher.fields.length).toEqual(8); + validateCustomField(cipher.fields, "date", "Sat, 01 Jan 2022 12:01:00 GMT"); + validateCustomField(cipher.fields, "location", "some hospital/clinic"); + validateCustomField(cipher.fields, "healthcareprofessional", "Some Doctor"); + validateCustomField(cipher.fields, "patient", "Me"); + validateCustomField(cipher.fields, "reason", "unwell"); + validateCustomField(cipher.fields, "medication", "Insuline"); + validateCustomField(cipher.fields, "dosage", "1"); + validateCustomField(cipher.fields, "notes", "multiple times a day"); + }); + + it("should create folders", async () => { + const importer = new Importer(); + const result = await importer.parse(SanitizedExportJson); + expect(result != null).toBe(true); + + const folders = result.folders; + expect(folders.length).toBe(5); + expect(folders[0].name).toBe("Movies"); + expect(folders[1].name).toBe("Finance"); + expect(folders[2].name).toBe("Travel"); + expect(folders[3].name).toBe("Education"); + expect(folders[4].name).toBe("Starter Kit"); + + // Check that ciphers have a folder assigned to them + expect(result.ciphers.filter((c) => c.folderId === folders[0].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[1].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[2].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[3].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[4].id).length).toBeGreaterThan(0); + }); + + it("should create collections if part of an organization", async () => { + const importer = new Importer(); + importer.organizationId = Utils.newGuid(); + const result = await importer.parse(SanitizedExportJson); + expect(result != null).toBe(true); + + const collections = result.collections; + expect(collections.length).toBe(5); + expect(collections[0].name).toBe("Movies"); + expect(collections[1].name).toBe("Finance"); + expect(collections[2].name).toBe("Travel"); + expect(collections[3].name).toBe("Education"); + expect(collections[4].name).toBe("Starter Kit"); + }); +}); diff --git a/spec/common/importers/testData/onePassword1Pux/APICredentials.ts b/spec/common/importers/testData/onePassword1Pux/APICredentials.ts new file mode 100644 index 0000000000..39ca32b5aa --- /dev/null +++ b/spec/common/importers/testData/onePassword1Pux/APICredentials.ts @@ -0,0 +1,171 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const APICredentialsData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + item: { + uuid: "6nqnjdqyk5mwvqbdgbdr47oabe", + favIndex: 0, + createdAt: 1619465969, + updatedAt: 1619466052, + trashed: false, + categoryUuid: "112", + details: { + loginFields: [], + notesPlain: "My API Credential", + sections: [ + { + title: "", + fields: [ + { + title: "username", + id: "username", + value: { + string: "apiuser@nullvalue.test", + }, + indexAtSource: 0, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "credential", + id: "credential", + value: { + concealed: "apiapiapiapiapiapiappy", + }, + indexAtSource: 1, + guarded: true, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "default", + }, + }, + { + title: "type", + id: "type", + value: { + menu: "jwt", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "filename", + id: "filename", + value: { + string: "filename.jwt", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "valid from", + id: "validFrom", + value: { + date: 1301918460, + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expires", + id: "expires", + value: { + date: 1932811260, + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "hostname", + id: "hostname", + value: { + string: "not.your.everyday.hostname", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "", + title: "API Credential", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/spec/common/importers/testData/onePassword1Pux/BankAccount.ts b/spec/common/importers/testData/onePassword1Pux/BankAccount.ts new file mode 100644 index 0000000000..0f62bd2d9f --- /dev/null +++ b/spec/common/importers/testData/onePassword1Pux/BankAccount.ts @@ -0,0 +1,226 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const BankAccountData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + item: { + uuid: "u2l4sjbencvsowwjuj3dfpt73q", + favIndex: 0, + createdAt: 1619466056, + updatedAt: 1619466187, + trashed: false, + categoryUuid: "101", + details: { + loginFields: [], + notesPlain: "My Bank Account", + sections: [ + { + title: "", + fields: [ + { + title: "bank name", + id: "bankName", + value: { + string: "Super Credit Union", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "name on account", + id: "owner", + value: { + string: "Cool Guy", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "type", + id: "accountType", + value: { + menu: "checking", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "routing number", + id: "routingNo", + value: { + string: "111000999", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "account number", + id: "accountNo", + value: { + string: "192837465918273645", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "SWIFT", + id: "swift", + value: { + string: "123456", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "IBAN", + id: "iban", + value: { + string: "DE12 123456", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "PIN", + id: "telephonePin", + value: { + concealed: "5555", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Branch Information", + name: "branchInfo", + fields: [ + { + title: "phone", + id: "branchPhone", + value: { + phone: "9399399933", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "address", + id: "branchAddress", + value: { + string: "1 Fifth Avenue", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "sentences", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "Super Credit Union", + tags: ["Finance"], + title: "Bank Account", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/spec/common/importers/testData/onePassword1Pux/CreditCard.ts b/spec/common/importers/testData/onePassword1Pux/CreditCard.ts new file mode 100644 index 0000000000..2c76944697 --- /dev/null +++ b/spec/common/importers/testData/onePassword1Pux/CreditCard.ts @@ -0,0 +1,345 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const CreditCardData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + item: { + uuid: "vpxi2esuujz7nrbojp34rd5aja", + favIndex: 0, + createdAt: 1619465282, + updatedAt: 1619465447, + trashed: false, + categoryUuid: "002", + details: { + loginFields: [], + notesPlain: "My parents' credit card. ", + sections: [ + { + title: "", + fields: [ + { + title: "cardholder name", + id: "cardholder", + value: { + string: "Fred Engels", + }, + indexAtSource: 0, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "type", + id: "type", + value: { + creditCardType: "discover", + }, + indexAtSource: 1, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "number", + id: "ccnum", + value: { + creditCardNumber: "6011111111111117", + }, + indexAtSource: 2, + guarded: true, + clipboardFilter: "0123456789", + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "verification number", + id: "cvv", + value: { + concealed: "1312", + }, + indexAtSource: 3, + guarded: true, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expiry date", + id: "expiry", + value: { + monthYear: 209912, + }, + indexAtSource: 4, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "valid from", + id: "validFrom", + value: { + monthYear: 200101, + }, + indexAtSource: 5, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "", + id: "txbzvwzpck7ejhfres3733rbpm", + value: { + string: "card", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Contact Information", + name: "contactInfo", + fields: [ + { + title: "issuing bank", + id: "bank", + value: { + string: "Some bank", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "phone (local)", + id: "phoneLocal", + value: { + phone: "123456", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "phone (toll free)", + id: "phoneTollFree", + value: { + phone: "0800123456", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "phone (intl)", + id: "phoneIntl", + value: { + phone: "+49123456", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "website", + id: "website", + value: { + url: "somebank.com", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Additional Details", + name: "details", + fields: [ + { + title: "PIN", + id: "pin", + value: { + concealed: "1234", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "credit limit", + id: "creditLimit", + value: { + string: "$1312", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "cash withdrawal limit", + id: "cashLimit", + value: { + string: "$500", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "interest rate", + id: "interest", + value: { + string: "1%", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "issue number", + id: "issuenumber", + value: { + string: "123456", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "1234 **** 6789", + tags: ["Finance"], + title: "Parent's Credit Card", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/spec/common/importers/testData/onePassword1Pux/Database.ts b/spec/common/importers/testData/onePassword1Pux/Database.ts new file mode 100644 index 0000000000..3aaad2da4f --- /dev/null +++ b/spec/common/importers/testData/onePassword1Pux/Database.ts @@ -0,0 +1,203 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const DatabaseData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + item: { + uuid: "ospvepl3ex2y6hjwwqwyvtf2sy", + favIndex: 0, + createdAt: 1619466193, + updatedAt: 1619466276, + trashed: false, + categoryUuid: "102", + details: { + loginFields: [], + notesPlain: "My Database", + sections: [ + { + title: "", + fields: [ + { + title: "type", + id: "database_type", + value: { + menu: "postgresql", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "server", + id: "hostname", + value: { + string: "my.secret.db.server", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + { + title: "port", + id: "port", + value: { + string: "1337", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "database", + id: "database", + value: { + string: "user_database", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "username", + id: "username", + value: { + string: "cooldbuser", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "password", + id: "password", + value: { + concealed: "^+kTjhLaN7wVPAhGU)*J", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "SID", + id: "sid", + value: { + string: "ASDIUFU-283234", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "alias", + id: "alias", + value: { + string: "cdbu", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "connection options", + id: "options", + value: { + string: "ssh", + }, + indexAtSource: 8, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "my.secret.db.server", + title: "Database", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/spec/common/importers/testData/onePassword1Pux/DriversLicense.ts b/spec/common/importers/testData/onePassword1Pux/DriversLicense.ts new file mode 100644 index 0000000000..4a55b20c92 --- /dev/null +++ b/spec/common/importers/testData/onePassword1Pux/DriversLicense.ts @@ -0,0 +1,235 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const DriversLicenseData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + item: { + uuid: "nntuge2g7s2wrlokyfhea354ay", + favIndex: 0, + createdAt: 1619466279, + updatedAt: 1619466425, + trashed: false, + categoryUuid: "103", + details: { + loginFields: [], + notesPlain: "My Driver's License", + sections: [ + { + title: "", + fields: [ + { + title: "full name", + id: "fullname", + value: { + string: "Michael Scarn", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "address", + id: "address", + value: { + string: "2120 Mifflin Rd.", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "sentences", + }, + }, + { + title: "date of birth", + id: "birthdate", + value: { + date: 252504060, + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "sex", + id: "sex", + value: { + gender: "male", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "height", + id: "height", + value: { + string: "5'11\"", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "number", + id: "number", + value: { + string: "12345678901", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "license class", + id: "class", + value: { + string: "C", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "conditions / restrictions", + id: "conditions", + value: { + string: "B", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "state", + id: "state", + value: { + string: "Pennsylvania", + }, + indexAtSource: 8, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "country", + id: "country", + value: { + string: "United States", + }, + indexAtSource: 9, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expiry date", + id: "expiry_date", + value: { + monthYear: 203012, + }, + indexAtSource: 10, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "12345678901", + title: "Michael Scarn", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/spec/common/importers/testData/onePassword1Pux/EmailAccount.ts b/spec/common/importers/testData/onePassword1Pux/EmailAccount.ts new file mode 100644 index 0000000000..2db083fd92 --- /dev/null +++ b/spec/common/importers/testData/onePassword1Pux/EmailAccount.ts @@ -0,0 +1,343 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const EmailAccountData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + item: { + uuid: "p3hohdgwpt4u2ra2fc3tvzomsm", + favIndex: 0, + createdAt: 1619466428, + updatedAt: 1619466585, + trashed: false, + categoryUuid: "111", + details: { + loginFields: [], + notesPlain: "My Email Config", + sections: [ + { + title: "", + fields: [ + { + title: "type", + id: "pop_type", + value: { + menu: "either", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "username", + id: "pop_username", + value: { + string: "someuser@nullvalue.test", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "server", + id: "pop_server", + value: { + string: "mailserver.nullvalue.test", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + { + title: "port number", + id: "pop_port", + value: { + string: "587", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "password", + id: "pop_password", + value: { + concealed: "u1jsf