diff --git a/spec/common/importers/onepassword1PifImporter.spec.ts b/spec/common/importers/onepassword1PifImporter.spec.ts index 98158fdc6d..b06f32b0a4 100644 --- a/spec/common/importers/onepassword1PifImporter.spec.ts +++ b/spec/common/importers/onepassword1PifImporter.spec.ts @@ -1,5 +1,5 @@ import { FieldType } from '../../../src/enums/fieldType'; -import { OnePassword1PifImporter as Importer } from '../../../src/importers/onepassword1PifImporter'; +import { OnePassword1PifImporter as Importer } from '../../../src/importers/onepasswordImporters/onepassword1PifImporter'; import { Utils } from '../../../src/misc/utils'; diff --git a/spec/common/importers/onepasswordCsvImporter.spec.ts b/spec/common/importers/onepasswordCsvImporter.spec.ts deleted file mode 100644 index 8ca37cef59..0000000000 --- a/spec/common/importers/onepasswordCsvImporter.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { OnePasswordWinCsvImporter as Importer } from '../../../src/importers/onepasswordWinCsvImporter'; - -import { CipherType } from '../../../src/enums'; - -import { data as creditCardData } from './testData/onePasswordCsv/creditCard.csv' -import { data as identityData } from './testData/onePasswordCsv/identity.csv' - -describe('1Password CSV Importer', () => { - it('should parse identity imports', async () => { - const importer = new Importer(); - const result = await importer.parse(identityData); - - expect(result).not.toBeNull(); - expect(result.success).toBe(true); - expect(result.ciphers.length).toBe(1); - const cipher = result.ciphers[0]; - expect(cipher.type).toBe(CipherType.Identity) - - expect(cipher.identity).toEqual(jasmine.objectContaining({ - firstName: 'first name', - middleName: 'mi', - lastName: 'last name', - username: 'userNam3', - company: 'bitwarden', - phone: '8005555555', - email: 'email@bitwarden.com' - })); - - expect(cipher.notes).toContain('address\ncity state zip\nUnited States'); - }); - - it('should parse credit card imports', async () => { - const importer = new Importer(); - const result = await importer.parse(creditCardData); - - expect(result).not.toBeNull(); - expect(result.success).toBe(true); - expect(result.ciphers.length).toBe(1); - const cipher = result.ciphers[0]; - expect(cipher.type).toBe(CipherType.Card); - - expect(cipher.card).toEqual(jasmine.objectContaining({ - number: '4111111111111111', - code: '111', - cardholderName: 'test', - expMonth: '1', - expYear: '2030', - })); - }); -}); diff --git a/spec/common/importers/onepasswordMacCsvImporter.spec.ts b/spec/common/importers/onepasswordMacCsvImporter.spec.ts new file mode 100644 index 0000000000..4cc270f492 --- /dev/null +++ b/spec/common/importers/onepasswordMacCsvImporter.spec.ts @@ -0,0 +1,71 @@ +import { OnePasswordMacCsvImporter as Importer } from '../../../src/importers/onepasswordImporters/onepasswordMacCsvImporter'; + +import { CipherType } from '../../../src/enums'; +import { CipherView } from '../../../src/models/view/cipherView'; + +import { data as creditCardData } from './testData/onePasswordCsv/creditCard.mac.csv'; +import { data as identityData } from './testData/onePasswordCsv/identity.mac.csv'; +import { data as multiTypeData } from './testData/onePasswordCsv/multipleItems.mac.csv'; + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.identity).toEqual(jasmine.objectContaining({ + firstName: 'first name', + middleName: 'mi', + lastName: 'last name', + username: 'userNam3', + company: 'bitwarden', + phone: '8005555555', + email: 'email@bitwarden.com' + })); + + expect(cipher.notes).toContain('address\ncity state zip\nUnited States'); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.card).toEqual(jasmine.objectContaining({ + number: '4111111111111111', + code: '111', + cardholderName: 'test', + expMonth: '1', + expYear: '2030', + })); +} + +describe('1Password mac CSV Importer', () => { + it('should parse identity records', async () => { + const importer = new Importer(); + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + it('should parse credit card records', async () => { + const importer = new Importer(); + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it('should parse csv\'s with multiple record type', async () => { + const importer = new Importer(); + const result = await importer.parse(multiTypeData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(4); + expectIdentity(result.ciphers[1]); + expectCreditCard(result.ciphers[2]); + }); +}); diff --git a/spec/common/importers/onepasswordWinCsvImporter.spec.ts b/spec/common/importers/onepasswordWinCsvImporter.spec.ts new file mode 100644 index 0000000000..a456d0bbd1 --- /dev/null +++ b/spec/common/importers/onepasswordWinCsvImporter.spec.ts @@ -0,0 +1,81 @@ +import { OnePasswordWinCsvImporter as Importer } from '../../../src/importers/onepasswordImporters/onepasswordWinCsvImporter'; + +import { CipherType, FieldType } from '../../../src/enums'; +import { CipherView } from '../../../src/models/view/cipherView'; +import { FieldView } from '../../../src/models/view/fieldView'; + +import { data as creditCardData } from './testData/onePasswordCsv/creditCard.windows.csv'; +import { data as identityData } from './testData/onePasswordCsv/identity.windows.csv'; +import { data as multiTypeData } from './testData/onePasswordCsv/multipleItems.windows.csv'; + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.identity).toEqual(jasmine.objectContaining({ + firstName: 'first name', + middleName: 'mi', + lastName: 'last name', + username: 'userNam3', + company: 'bitwarden', + phone: '8005555555', + email: 'email@bitwarden.com' + })); + + expect(cipher.fields).toEqual(jasmine.arrayContaining([ + Object.assign(new FieldView(), { + type: FieldType.Text, + name: 'address', + value: 'address city state zip us' + }) + ])); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.card).toEqual(jasmine.objectContaining({ + number: '4111111111111111', + code: '111', + cardholderName: 'test', + expMonth: '1', + expYear: '1970', + })); +} + +describe('1Password windows CSV Importer', () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it('should parse identity records', async () => { + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + it('should parse credit card records', async () => { + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it('should parse csv\'s with multiple record types', async () => { + const result = await importer.parse(multiTypeData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(4); + + expectIdentity(result.ciphers[1]); + expectCreditCard(result.ciphers[2]); + }); +}); diff --git a/spec/common/importers/testData/onePasswordCsv/creditCard.csv.ts b/spec/common/importers/testData/onePasswordCsv/creditCard.mac.csv.ts similarity index 98% rename from spec/common/importers/testData/onePasswordCsv/creditCard.csv.ts rename to spec/common/importers/testData/onePasswordCsv/creditCard.mac.csv.ts index 47c05222a5..3062c06752 100644 --- a/spec/common/importers/testData/onePasswordCsv/creditCard.csv.ts +++ b/spec/common/importers/testData/onePasswordCsv/creditCard.mac.csv.ts @@ -1,3 +1,3 @@ export const data = `"account number(accountNo)","address(address)","address(branchAddress)","admin console URL(admin_console_url)","admin console username(admin_console_username)","AirPort ID(airport_id)","alias(alias)","AOL/AIM(aim)","approved wildlife(game)","attached storage password(disk_password)","auth​ method(pop_authentication)","auth​ method(smtp_authentication)","bank name(bankName)","base station name(name)","base station password(password)","birth date(birthdate)","business(busphone)","cardholder name(cardholder)","cash withdrawal limit(cashLimit)","cell(cellphone)","company name(company_name)","company(company)","conditions / restrictions(conditions)","connection options(options)","console password(admin_console_password)","country(country)","Created Date","credit limit(creditLimit)","customer service phone(customer_service_phone)","database(database)","date of birth(birthdate)","default phone(defphone)","department(department)","download page(download_link)","email(email)","expires(expires)","expiry date(expiry_date)","expiry date(expiry)","first name(firstname)","forum signature(forumsig)","full name(fullname)","full name(name)","group(org_name)","height(height)","home(homephone)","IBAN(iban)","ICQ(icq)","initial(initial)","interest rate(interest)","issue number(issuenumber)","issued on(issue_date)","issuing authority(issuing_authority)","issuing bank(bank)","issuing country(issuing_country)","job title(jobtitle)","last name(lastname)","license class(class)","license key(reg_code)","licensed to(reg_name)","maximum quota(quota)","member ID (additional)(additional_no)","member ID(membership_no)","member name(member_name)","member since(member_since)","Modified Date","MSN(msn)","name on account(owner)","name(name)","nationality(nationality)","network name(network_name)","Notes","number(ccnum)","number(number)","occupation(occupation)","order number(order_number)","order total(order_total)","Password","password(password)","password(pop_password)","password(smtp_password)","phone (intl)(phoneIntl)","phone (local)(phone_local)","phone (local)(phoneLocal)","phone (toll free)(phone_tollfree)","phone (toll free)(phoneTollFree)","phone for reserva​tions(reservations_phone)","phone(branchPhone)","PIN(pin)","PIN(telephonePin)","place of birth(birthplace)","port number(pop_port)","port number(smtp_port)","port(port)","provider's website(provider_website)","provider(provider)","publisher(publisher_name)","purchase date(order_date)","registered email(reg_email)","reminder answer(remindera)","reminder question(reminderq)","retail price(retail_price)","routing number(routingNo)","Scope","security(pop_security)","security(smtp_security)","server / IP address(server)","server(hostname)","server(pop_server)","sex(sex)","SID(sid)","skype(skype)","SMTP server(smtp_server)","state(state)","support email(support_email)","support phone(support_contact_phone)","support URL(support_contact_url)","SWIFT(swift)","Tags","telephone(phone)","Title","Type","type(accountType)","type(database_type)","type(pop_type)","type(type)","URL","URL(url)","Username","username(pop_username)","username(smtp_username)","username(username)","valid from(valid_from)","valid from(validFrom)","verification number(cvv)","version(product_version)","website(publisher_website)","website(website)","wireless network password(wireless_password)","wireless security(wireless_security)","Yahoo(yahoo)", ,,,,,,,,,,,,,,,,,"test",,,,,,,,,"1606923869",,,,,,,,,,,"01/2030",,,,,,,,,,,,,,,,,,,,,,,,,,,"1606924056",,,,,,"","4111111111111111",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{( -)}",,"test card","Credit Card",,,,"laser",,,,,,,,,"111",,,,,,,` +)}",,"test card","Credit Card",,,,"laser",,,,,,,,,"111",,,,,,,`; diff --git a/spec/common/importers/testData/onePasswordCsv/creditCard.windows.csv.ts b/spec/common/importers/testData/onePasswordCsv/creditCard.windows.csv.ts new file mode 100644 index 0000000000..f9dd98081b --- /dev/null +++ b/spec/common/importers/testData/onePasswordCsv/creditCard.windows.csv.ts @@ -0,0 +1,2 @@ +export const data = `"UUID","TITLE","SCOPE","AUTOSUBMIT","1: CARDHOLDER NAME","2: NUMBER","3: VERIFICATION NUMBER","4: EXPIRY DATE","SECTION 2: SECTION_PZET7LEKRQXZUINIEGH5ABA2UY","SECTION_PZET7LEKRQXZUINIEGH5ABA2UY 1: LABEL" +"sd26pt226etnsijbl3kqzi5bmm","test card","Default","Default","test","4111111111111111","111","1/3/1970 12:23 AM","section","field (phone)"`; diff --git a/spec/common/importers/testData/onePasswordCsv/identity.csv.ts b/spec/common/importers/testData/onePasswordCsv/identity.mac.csv.ts similarity index 99% rename from spec/common/importers/testData/onePasswordCsv/identity.csv.ts rename to spec/common/importers/testData/onePasswordCsv/identity.mac.csv.ts index 2b74cd8d8a..e02aed520f 100644 --- a/spec/common/importers/testData/onePasswordCsv/identity.csv.ts +++ b/spec/common/importers/testData/onePasswordCsv/identity.mac.csv.ts @@ -3,4 +3,4 @@ export const data = `"account number(accountNo)","address(address)","address(bra city state zip United States",,,,,,"",,,,,,,,"12/2/20","",,,"",,"bitwarden",,,,,"1606923754",,,,"12/2/20","8005555555","department",,"email@bitwarden.com",,,,"first name","",,,,,"",,"","mi",,,,,,,"job title","last name",,,,,,,,,"1607020883","",,,,,"It’s you! 🖐 Select Edit to fill in more details, like your address and contact information.",,,"occupation",,,,,,,,,,,,,,,,,,,,,,,,,"","",,,,,,,,,"",,"",,,,,,,"{( \\"Starter Kit\\" -)}",,"Identity Item","Identity",,,,,,,"userNam3",,,"userNam3",,,,,,"",,,"",` +)}",,"Identity Item","Identity",,,,,,,"userNam3",,,"userNam3",,,,,,"",,,"",`; diff --git a/spec/common/importers/testData/onePasswordCsv/identity.windows.csv.ts b/spec/common/importers/testData/onePasswordCsv/identity.windows.csv.ts new file mode 100644 index 0000000000..3e198e17db --- /dev/null +++ b/spec/common/importers/testData/onePasswordCsv/identity.windows.csv.ts @@ -0,0 +1,2 @@ +export const data = `"UUID","TITLE","SCOPE","AUTOSUBMIT","TAGS","NOTES","SECTION 1: NAME","NAME 1: FIRST NAME","NAME 2: INITIAL","NAME 3: LAST NAME","NAME 4: BIRTH DATE","NAME 5: OCCUPATION","NAME 6: COMPANY","NAME 7: DEPARTMENT","NAME 8: JOB TITLE","SECTION 2: ADDRESS","ADDRESS 1: ADDRESS","ADDRESS 2: DEFAULT PHONE","SECTION 3: INTERNET","INTERNET 1: USERNAME","INTERNET 2: EMAIL","SECTION 4: MFJQKMWEOYDZDFH4YMR7WLJKIY","MFJQKMWEOYDZDFH4YMR7WLJKIY 1: SECTION FIELD","MFJQKMWEOYDZDFH4YMR7WLJKIY 2: SECTION FIELD" +"6v56y5z4tejwg37jsettta7d7m","Identity Item","Default","Default","Starter Kit","It’s you! 🖐 Select Edit to fill in more details, like your address and contact information.","Identification","first name","mi","last name","12/2/2020 4:01 AM","occupation","bitwarden","department","job title","Address","address city state zip us","8005555555","Internet Details","userNam3","email@bitwarden.com","💡 Did you know?","1Password can fill names and addresses into webpages:","https://support.1password.com/credit-card-address-filling/"`; diff --git a/spec/common/importers/testData/onePasswordCsv/multipleItems.mac.csv.ts b/spec/common/importers/testData/onePasswordCsv/multipleItems.mac.csv.ts new file mode 100644 index 0000000000..e994b1afda --- /dev/null +++ b/spec/common/importers/testData/onePasswordCsv/multipleItems.mac.csv.ts @@ -0,0 +1,14 @@ +export const data = `"account number(accountNo)","address(address)","address(branchAddress)","admin console URL(admin_console_url)","admin console username(admin_console_username)","AirPort ID(airport_id)","alias(alias)","AOL/AIM(aim)","approved wildlife(game)","attached storage password(disk_password)","auth​ method(pop_authentication)","auth​ method(smtp_authentication)","bank name(bankName)","base station name(name)","base station password(password)","birth date(birthdate)","business(busphone)","cardholder name(cardholder)","cash withdrawal limit(cashLimit)","cell(cellphone)","company name(company_name)","company(company)","conditions / restrictions(conditions)","connection options(options)","console password(admin_console_password)","country(country)","Created Date","credit limit(creditLimit)","customer service phone(customer_service_phone)","database(database)","date of birth(birthdate)","default phone(defphone)","department(department)","download page(download_link)","email(email)","expires(expires)","expiry date(expiry_date)","expiry date(expiry)","first name(firstname)","forum signature(forumsig)","full name(fullname)","full name(name)","group(org_name)","height(height)","home(homephone)","IBAN(iban)","ICQ(icq)","initial(initial)","interest rate(interest)","issue number(issuenumber)","issued on(issue_date)","issuing authority(issuing_authority)","issuing bank(bank)","issuing country(issuing_country)","job title(jobtitle)","last name(lastname)","license class(class)","license key(reg_code)","licensed to(reg_name)","maximum quota(quota)","member ID (additional)(additional_no)","member ID(membership_no)","member name(member_name)","member since(member_since)","Modified Date","MSN(msn)","name on account(owner)","name(name)","nationality(nationality)","network name(network_name)","Notes","number(ccnum)","number(number)","occupation(occupation)","order number(order_number)","order total(order_total)","Password","password(password)","password(pop_password)","password(smtp_password)","phone (intl)(phoneIntl)","phone (local)(phone_local)","phone (local)(phoneLocal)","phone (toll free)(phone_tollfree)","phone (toll free)(phoneTollFree)","phone for reserva​tions(reservations_phone)","phone(branchPhone)","PIN(pin)","PIN(telephonePin)","place of birth(birthplace)","port number(pop_port)","port number(smtp_port)","port(port)","provider's website(provider_website)","provider(provider)","publisher(publisher_name)","purchase date(order_date)","registered email(reg_email)","reminder answer(remindera)","reminder question(reminderq)","retail price(retail_price)","routing number(routingNo)","Scope","security(pop_security)","security(smtp_security)","server / IP address(server)","server(hostname)","server(pop_server)","sex(sex)","SID(sid)","skype(skype)","SMTP server(smtp_server)","state(state)","support email(support_email)","support phone(support_contact_phone)","support URL(support_contact_url)","SWIFT(swift)","Tags","telephone(phone)","Title","Type","type(accountType)","type(database_type)","type(pop_type)","type(type)","URL","URL(url)","Username","username(pop_username)","username(smtp_username)","username(username)","valid from(valid_from)","valid from(validFrom)","verification number(cvv)","version(product_version)","website(publisher_website)","website(website)","wireless network password(wireless_password)","wireless security(wireless_security)","Yahoo(yahoo)", +,,,,,,,,,,,,,,,,,,,,,,,,,,"1606923754",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"1606923754",,,,,,"Follow these steps to get started.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{( + \\"Starter Kit\\" +)}",,"🎉 Welcome to 1Password!","Secure Note",,,,,,,,,,,,,,,,,,,, +,"address +city state zip +United States",,,,,,,,,,,,,,"12/2/20",,,,,,"bitwarden",,,,,"1606923754",,,,"12/2/20","8005555555","department",,"email@bitwarden.com",,,,"first name",,,,,,,,,"mi",,,,,,,"job title","last name",,,,,,,,,"1607390191",,,,,,"It’s you! 🖐 Select Edit to fill in more details, like your address and contact information.",,,"occupation",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{( + \\"Starter Kit\\" +)}",,"Identity Item","Identity",,,,,,,"userNam3",,,"userNam3",,,,,,,,,, +,,,,,,,,,,,,,,,,,"test",,,,,,,,,"1606923869",,,,,,,,,,,"01/2030",,,,,,,,,,,,,,,,,,,,,,,,,,,"1607355631",,,,,,"","4111111111111111",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{( +)}",,"test card","Credit Card",,,,"laser",,,,,,,,,"111",,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,"1606923754",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"1607020972",,,,,,"You can use this login to sign in to your account on 1password.com.",,,,,,"the account's password",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{( + \\"Starter Kit\\" +)}",,"1Password Account","Login",,,,,"https://my.1password.com",,"email@bitwarden.com",,,,,,,,,,,,,`; diff --git a/spec/common/importers/testData/onePasswordCsv/multipleItems.windows.csv.ts b/spec/common/importers/testData/onePasswordCsv/multipleItems.windows.csv.ts new file mode 100644 index 0000000000..918b7a52e9 --- /dev/null +++ b/spec/common/importers/testData/onePasswordCsv/multipleItems.windows.csv.ts @@ -0,0 +1,5 @@ +export const data = `"UUID","TITLE","USERNAME","PASSWORD","URL","URLS","EMAIL","MASTER-PASSWORD","ACCOUNT-KEY","SCOPE","AUTOSUBMIT","TAGS","NOTES","SECTION 1: WXHDKEQREE3TH6QRFCPFPSD3AE","WXHDKEQREE3TH6QRFCPFPSD3AE 1: SECRET KEY","SECTION 1: NAME","NAME 1: FIRST NAME","NAME 2: INITIAL","NAME 3: LAST NAME","NAME 4: BIRTH DATE","NAME 5: OCCUPATION","NAME 6: COMPANY","NAME 7: DEPARTMENT","NAME 8: JOB TITLE","SECTION 2: ADDRESS","ADDRESS 1: ADDRESS","ADDRESS 2: DEFAULT PHONE","SECTION 3: INTERNET","INTERNET 1: USERNAME","INTERNET 2: EMAIL","SECTION 4: MFJQKMWEOYDZDFH4YMR7WLJKIY","MFJQKMWEOYDZDFH4YMR7WLJKIY 1: SECTION FIELD","MFJQKMWEOYDZDFH4YMR7WLJKIY 2: SECTION FIELD","1: CARDHOLDER NAME","2: NUMBER","3: VERIFICATION NUMBER","4: EXPIRY DATE","SECTION 2: SECTION_PZET7LEKRQXZUINIEGH5ABA2UY","SECTION_PZET7LEKRQXZUINIEGH5ABA2UY 1: LABEL","SECTION 1: 4PQVXPR4BMOPGC3DBMTP5U4OFY","4PQVXPR4BMOPGC3DBMTP5U4OFY 1: SECTION FIELD","4PQVXPR4BMOPGC3DBMTP5U4OFY 2: SECTION FIELD","SECTION 2: M2NTUZZBFOFTPAYXVXE6EMZ5JU","M2NTUZZBFOFTPAYXVXE6EMZ5JU 1: SECTION FIELD","M2NTUZZBFOFTPAYXVXE6EMZ5JU 2: SECTION FIELD","SECTION 3: WC3KPAWH6ZAEQB2ARJB6WYZ3DQ","WC3KPAWH6ZAEQB2ARJB6WYZ3DQ 1: SECTION FIELD","WC3KPAWH6ZAEQB2ARJB6WYZ3DQ 2: SECTION FIELD","WC3KPAWH6ZAEQB2ARJB6WYZ3DQ 3: SECTION FIELD","SECTION 4: TOHUYJEJEMGMI6GEQAZ2LJODFE","TOHUYJEJEMGMI6GEQAZ2LJODFE 1: SECTION FIELD","TOHUYJEJEMGMI6GEQAZ2LJODFE 2: SECTION FIELD","SECTION 5: O26UWJJTXRAANG3ONYYOUUJHDM","O26UWJJTXRAANG3ONYYOUUJHDM 1: SECTION FIELD","O26UWJJTXRAANG3ONYYOUUJHDM 2: WATCH VIDEOS","O26UWJJTXRAANG3ONYYOUUJHDM 3: GET SUPPORT","O26UWJJTXRAANG3ONYYOUUJHDM 4: READ THE BLOG","O26UWJJTXRAANG3ONYYOUUJHDM 5: CONTACT US" +"xjq32axcswefpcxu2mtxxqnufa","1Password Account","email@bitwarden.com","the account's password","https://my.1password.com","https://my.1password.com","email@bitwarden.com","the account's password","A3-76TR2N-NJG3TZ-9NXFX-WT8GF-6YQC9-R2659","Default","Default","Starter Kit","You can use this login to sign in to your account on 1password.com.","🔑 Secret Key","the account's secret key","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","" +"6v56y5z4tejwg37jsettta7d7m","Identity Item","","","","","","","","Default","Default","Starter Kit","It’s you! 🖐 Select Edit to fill in more details, like your address and contact information.","","","Identification","first name","mi","last name","12/2/2020 4:01 AM","occupation","bitwarden","department","job title","Address","address city state zip us","8005555555","Internet Details","userNam3","email@bitwarden.com","💡 Did you know?","1Password can fill names and addresses into webpages:","https://support.1password.com/credit-card-address-filling/","","","","","","","","","","","","","","","","","","","","","","","","","" +"sd26pt226etnsijbl3kqzi5bmm","test card","","","","","","","","Default","Default","","","","","","","","","","","","","","","","","","","","","","","test","4111111111111111","111","1/3/1970 12:23 AM","section","field (phone)","","","","","","","","","","","","","","","","","","","" +"oml2sgit3yk7737kxdis65o4xq","🎉 Welcome to 1Password!","","","","","","","","Default","Default","Starter Kit","Follow these steps to get started.","","","","","","","","","","","","","","","","","","","","","","","","","","","1️⃣ Get the apps","https://1password.com/downloads","Install 1Password everywhere you need your passwords.","2️⃣ Get 1Password in your browser","https://1password.com/downloads/#browsers","Install 1Password in your browser to save and fill passwords.","3️⃣ Save your first password","1. Sign in to your favorite website.","2. 1Password will ask to save your username and password.","3. Click Save Login.","4️⃣ Fill passwords and more","https://support.1password.com/explore/extension/","Save and fill passwords, credit cards, and addresses.","📚 Learn 1Password","Check out our videos and articles:","https://youtube.com/1PasswordVideos","https://support.1password.com/","https://blog.1password.com/","https://support.1password.com/contact-us/"`; diff --git a/spec/node/services/nodeCryptoFunction.service.spec.ts b/spec/node/services/nodeCryptoFunction.service.spec.ts index 392c0a61bc..a7fbc8022f 100644 --- a/spec/node/services/nodeCryptoFunction.service.spec.ts +++ b/spec/node/services/nodeCryptoFunction.service.spec.ts @@ -66,7 +66,7 @@ describe('NodeCrypto Function Service', () => { const prk16Byte = 'criAmKtfzxanbgea5/kelQ=='; const prk32Byte = 'F5h4KdYQnIVH4rKH0P9CZb1GrR4n16/sJrS0PsQEn0Y='; const prk64Byte = 'ssBK0mRG17VHdtsgt8yo4v25CRNpauH+0r2fwY/E9rLyaFBAOMbIeTry+' + - 'gUJ28p8y+hFh3EI9pcrEWaNvFYonQ==' + 'gUJ28p8y+hFh3EI9pcrEWaNvFYonQ=='; testHkdfExpand('sha256', prk32Byte, 32, 'BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD8='); testHkdfExpand('sha256', prk32Byte, 64, 'BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD9BV+' + diff --git a/spec/web/services/webCryptoFunction.service.spec.ts b/spec/web/services/webCryptoFunction.service.spec.ts index e71cb3a493..ee81c14cdc 100644 --- a/spec/web/services/webCryptoFunction.service.spec.ts +++ b/spec/web/services/webCryptoFunction.service.spec.ts @@ -70,7 +70,7 @@ describe('WebCrypto Function Service', () => { const prk16Byte = 'criAmKtfzxanbgea5/kelQ=='; const prk32Byte = 'F5h4KdYQnIVH4rKH0P9CZb1GrR4n16/sJrS0PsQEn0Y='; const prk64Byte = 'ssBK0mRG17VHdtsgt8yo4v25CRNpauH+0r2fwY/E9rLyaFBAOMbIeTry+' + - 'gUJ28p8y+hFh3EI9pcrEWaNvFYonQ==' + 'gUJ28p8y+hFh3EI9pcrEWaNvFYonQ=='; testHkdfExpand('sha256', prk32Byte, 32, 'BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD8='); testHkdfExpand('sha256', prk32Byte, 64, 'BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD9BV+' + diff --git a/src/abstractions/cryptoFunction.service.ts b/src/abstractions/cryptoFunction.service.ts index ddb38d1fe7..7573050ce0 100644 --- a/src/abstractions/cryptoFunction.service.ts +++ b/src/abstractions/cryptoFunction.service.ts @@ -5,7 +5,7 @@ export abstract class CryptoFunctionService { pbkdf2: (password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512', iterations: number) => Promise; hkdf: (ikm: ArrayBuffer, salt: string | ArrayBuffer, info: string | ArrayBuffer, - outputByteSize: number, algorithm: 'sha256' | 'sha512') => Promise + outputByteSize: number, algorithm: 'sha256' | 'sha512') => Promise; hkdfExpand: (prk: ArrayBuffer, info: string | ArrayBuffer, outputByteSize: number, algorithm: 'sha256' | 'sha512') => Promise; hash: (value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512' | 'md5') => Promise; diff --git a/src/angular/components/two-factor.component.ts b/src/angular/components/two-factor.component.ts index 0221bceede..3459b96934 100644 --- a/src/angular/components/two-factor.component.ts +++ b/src/angular/components/two-factor.component.ts @@ -246,7 +246,7 @@ export class TwoFactorComponent implements OnInit, OnDestroy { } get authing(): boolean { - return this.authService.authingWithPassword() || this.authService.authingWithSso() || this.authService.authingWithApiKey() + return this.authService.authingWithPassword() || this.authService.authingWithSso() || this.authService.authingWithApiKey(); } get needsLock(): boolean { diff --git a/src/importers/baseImporter.ts b/src/importers/baseImporter.ts index ab02785bd6..61c42bfe9b 100644 --- a/src/importers/baseImporter.ts +++ b/src/importers/baseImporter.ts @@ -68,7 +68,7 @@ export abstract class BaseImporter { protected parseCsvOptions = { encoding: 'UTF-8', skipEmptyLines: false, - } + }; protected organization() { return this.organizationId != null; diff --git a/src/importers/onepasswordImporters/cipherImportContext.ts b/src/importers/onepasswordImporters/cipherImportContext.ts new file mode 100644 index 0000000000..e3a6ff8d6c --- /dev/null +++ b/src/importers/onepasswordImporters/cipherImportContext.ts @@ -0,0 +1,8 @@ +import { CipherView } from '../../models/view'; + +export class CipherImportContext { + lowerProperty: string; + constructor(public importRecord: any, public property: string, public cipher: CipherView) { + this.lowerProperty = property.toLowerCase(); + } +} diff --git a/src/importers/onepassword1PifImporter.ts b/src/importers/onepasswordImporters/onepassword1PifImporter.ts similarity index 94% rename from src/importers/onepassword1PifImporter.ts rename to src/importers/onepasswordImporters/onepassword1PifImporter.ts index 821b69d45e..66f5e77d9b 100644 --- a/src/importers/onepassword1PifImporter.ts +++ b/src/importers/onepasswordImporters/onepassword1PifImporter.ts @@ -1,17 +1,17 @@ -import { BaseImporter } from './baseImporter'; -import { Importer } from './importer'; +import { BaseImporter } from '../baseImporter'; +import { Importer } from '../importer'; -import { ImportResult } from '../models/domain/importResult'; +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 { PasswordHistoryView } from '../models/view/passwordHistoryView'; -import { SecureNoteView } from '../models/view/secureNoteView'; +import { CardView } from '../../models/view/cardView'; +import { CipherView } from '../../models/view/cipherView'; +import { IdentityView } from '../../models/view/identityView'; +import { PasswordHistoryView } from '../../models/view/passwordHistoryView'; +import { SecureNoteView } from '../../models/view/secureNoteView'; -import { CipherType } from '../enums/cipherType'; -import { FieldType } from '../enums/fieldType'; -import { SecureNoteType } from '../enums/secureNoteType'; +import { CipherType } from '../../enums/cipherType'; +import { FieldType } from '../../enums/fieldType'; +import { SecureNoteType } from '../../enums/secureNoteType'; export class OnePassword1PifImporter extends BaseImporter implements Importer { result = new ImportResult(); diff --git a/src/importers/onepasswordImporters/onepasswordCsvImporter.ts b/src/importers/onepasswordImporters/onepasswordCsvImporter.ts new file mode 100644 index 0000000000..87ee36e54b --- /dev/null +++ b/src/importers/onepasswordImporters/onepasswordCsvImporter.ts @@ -0,0 +1,288 @@ +import { ImportResult } from '../../models/domain/importResult'; +import { BaseImporter } from '../baseImporter'; +import { Importer } from '../importer'; + +import { CipherType } from '../../enums/cipherType'; +import { FieldType } from '../../enums/fieldType'; +import { CipherView } from '../../models/view'; +import { CipherImportContext } from './cipherImportContext'; + +export const IgnoredProperties = ['ainfo', 'autosubmit', 'notesplain', 'ps', 'scope', 'tags', 'title', 'uuid', 'notes']; + +export abstract class OnePasswordCsvImporter extends BaseImporter implements Importer { + protected loginPropertyParsers = [this.setLoginUsername, this.setLoginPassword, this.setLoginUris]; + protected creditCardPropertyParsers = [this.setCreditCardNumber, this.setCreditCardVerification, this.setCreditCardCardholderName, this.setCreditCardExpiry]; + protected identityPropertyParsers = [this.setIdentityFirstName, this.setIdentityInitial, this.setIdentityLastName, this.setIdentityUserName, this.setIdentityEmail, this.setIdentityPhone, this.setIdentityCompany]; + + abstract setCipherType(value: any, cipher: CipherView): void; + + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true, { + quoteChar: '"', + escapeChar: '\\', + }); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (this.isNullOrWhitespace(this.getProp(value, 'title'))) { + return; + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(this.getProp(value, 'title'), '--'); + + this.setNotes(value, cipher); + + this.setCipherType(value, cipher); + + let altUsername: string = null; + for (const property in value) { + if (!value.hasOwnProperty(property) || this.isNullOrWhitespace(value[property])) { + continue; + } + + const context = new CipherImportContext(value, property, cipher); + if (cipher.type === CipherType.Login && this.setKnownLoginValue(context)) { + continue; + } else if (cipher.type === CipherType.Card && this.setKnownCreditCardValue(context)) { + continue; + } else if (cipher.type === CipherType.Identity && this.setKnownIdentityValue(context)) { + continue; + } + + altUsername = this.setUnknownValue(context, altUsername); + } + + if (cipher.type === CipherType.Login && !this.isNullOrWhitespace(altUsername) && + this.isNullOrWhitespace(cipher.login.username) && altUsername.indexOf('://') === -1) { + cipher.login.username = altUsername; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + protected getProp(obj: any, name: string): any { + const lowerObj = Object.entries(obj).reduce((agg: any, entry: [string, any]) => { + agg[entry[0].toLowerCase()] = entry[1]; + return agg; + }, {}); + return lowerObj[name.toLowerCase()]; + } + + protected getPropByRegexp(obj: any, regexp: RegExp): any { + const matchingKeys = Object.keys(obj).reduce((agg: string[], key: string) => { + if (key.match(regexp)) { + agg.push(key); + } + return agg; + }, []); + if (matchingKeys.length === 0) { + return null; + } else { + return obj[matchingKeys[0]]; + } + } + + protected getPropIncluding(obj: any, name: string): any { + const includesMap = Object.keys(obj).reduce((agg: string[], entry: string) => { + if (entry.toLowerCase().includes(name.toLowerCase())) { + agg.push(entry); + } + return agg; + }, []); + if (includesMap.length === 0) { + return null; + } else { + return obj[includesMap[0]]; + } + } + + protected setNotes(importRecord: any, cipher: CipherView) { + cipher.notes = this.getValueOrDefault(this.getProp(importRecord, 'notesPlain'), '') + '\n' + + this.getValueOrDefault(this.getProp(importRecord, 'notes'), '') + '\n'; + cipher.notes.trim(); + + } + + protected setKnownLoginValue(context: CipherImportContext): boolean { + return this.loginPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setKnownCreditCardValue(context: CipherImportContext): boolean { + return this.creditCardPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setKnownIdentityValue(context: CipherImportContext): boolean { + return this.identityPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setUnknownValue(context: CipherImportContext, altUsername: string): string { + if (IgnoredProperties.indexOf(context.lowerProperty) === -1 && !context.lowerProperty.startsWith('section:') && + !context.lowerProperty.startsWith('section ')) { + if (altUsername == null && context.lowerProperty === 'email') { + return context.importRecord[context.property]; + } + else if (context.lowerProperty === 'created date' || context.lowerProperty === 'modified date') { + const readableDate = new Date(parseInt(context.importRecord[context.property], 10) * 1000).toUTCString(); + this.processKvp(context.cipher, '1Password ' + context.property, readableDate); + return null; + } + if (context.lowerProperty.includes('password') || context.lowerProperty.includes('key') || context.lowerProperty.includes('secret')) { + this.processKvp(context.cipher, context.property, context.importRecord[context.property], FieldType.Hidden); + } else { + this.processKvp(context.cipher, context.property, context.importRecord[context.property]); + } + } + return null; + } + + protected setIdentityFirstName(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.identity.firstName) && context.lowerProperty.includes('first name')) { + context.cipher.identity.firstName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityInitial(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.identity.middleName) && context.lowerProperty.includes('initial')) { + context.cipher.identity.middleName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityLastName(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.identity.lastName) && context.lowerProperty.includes('last name')) { + context.cipher.identity.lastName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityUserName(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.identity.username) && context.lowerProperty.includes('username')) { + context.cipher.identity.username = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityCompany(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.identity.company) && context.lowerProperty.includes('company')) { + context.cipher.identity.company = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityPhone(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.identity.phone) && context.lowerProperty.includes('default phone')) { + context.cipher.identity.phone = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityEmail(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.identity.email) && context.lowerProperty.includes('email')) { + context.cipher.identity.email = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardNumber(context: CipherImportContext): boolean { + if (this.isNullOrWhitespace(context.cipher.card.number) && context.lowerProperty.includes('number')) { + context.cipher.card.number = context.importRecord[context.property]; + context.cipher.card.brand = this.getCardBrand(context.cipher.card.number); + return true; + } + return false; + } + + protected setCreditCardVerification(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.card.code) && context.lowerProperty.includes('verification number')) { + context.cipher.card.code = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardCardholderName(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.card.cardholderName) && context.lowerProperty.includes('cardholder name')) { + context.cipher.card.cardholderName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardExpiry(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.card.expiration) && context.lowerProperty.includes('expiry date') && + context.importRecord[context.property].length === 7) { + context.cipher.card.expMonth = (context.importRecord[context.property] as string).substr(0, 2); + if (context.cipher.card.expMonth[0] === '0') { + context.cipher.card.expMonth = context.cipher.card.expMonth.substr(1, 1); + } + context.cipher.card.expYear = (context.importRecord[context.property] as string).substr(3, 4); + return true; + } + return false; + } + + protected setLoginPassword(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.login.password) && context.lowerProperty === 'password') { + context.cipher.login.password = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setLoginUsername(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.login.username) && context.lowerProperty === 'username') { + context.cipher.login.username = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setLoginUris(context: CipherImportContext) { + if ((context.cipher.login.uris == null || context.cipher.login.uris.length === 0) && context.lowerProperty === 'urls') { + const urls = context.importRecord[context.property].split(this.newLineRegex); + context.cipher.login.uris = this.makeUriArray(urls); + return true; + } else if ((context.lowerProperty === 'url')) { + if (context.cipher.login.uris == null) { + context.cipher.login.uris = []; + } + context.cipher.login.uris.concat(this.makeUriArray(context.importRecord[context.property])); + return true; + } + return false; + } +} diff --git a/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts b/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts new file mode 100644 index 0000000000..443647c82d --- /dev/null +++ b/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts @@ -0,0 +1,28 @@ +import { Importer } from '../importer'; +import { IgnoredProperties, OnePasswordCsvImporter } from './onepasswordCsvImporter'; + +import { CipherType } from '../../enums/cipherType'; +import { CardView, CipherView, IdentityView } from '../../models/view'; + +export class OnePasswordMacCsvImporter extends OnePasswordCsvImporter implements Importer { + setCipherType(value: any, cipher: CipherView) { + const onePassType = this.getValueOrDefault(this.getProp(value, 'type'), 'Login'); + switch (onePassType) { + case 'Credit Card': + cipher.type = CipherType.Card; + cipher.card = new CardView(); + IgnoredProperties.push('type'); + break; + case 'Identity': + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + IgnoredProperties.push('type'); + break; + case 'Login': + case 'Secure Note': + IgnoredProperties.push('type'); + default: + break; + } + } +} diff --git a/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts b/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts new file mode 100644 index 0000000000..9b4fae89c6 --- /dev/null +++ b/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts @@ -0,0 +1,53 @@ +import { Importer } from '../importer'; +import { CipherImportContext } from './cipherImportContext'; +import { OnePasswordCsvImporter } from './onepasswordCsvImporter'; + +import { CipherType } from '../../enums/cipherType'; +import { CardView, CipherView, IdentityView, LoginView } from '../../models/view'; + +export class OnePasswordWinCsvImporter extends OnePasswordCsvImporter implements Importer { + constructor() { + super(); + this.identityPropertyParsers.push(this.setIdentityAddress); + } + + setCipherType(value: any, cipher: CipherView) { + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + + if (!this.isNullOrWhitespace(this.getPropByRegexp(value, /\d+: number/i)) && + !this.isNullOrWhitespace(this.getPropByRegexp(value, /\d+: expiry date/i))) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } + + if (!this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: first name/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: initial/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: last name/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /internet \d+: email/i))) { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + } + } + + setIdentityAddress(context: CipherImportContext) { + if (context.lowerProperty.match(/address \d+: address/i)) { + this.processKvp(context.cipher, 'address', context.importRecord[context.property]); + return true; + } + return false; + } + + setCreditCardExpiry(context: CipherImportContext) { + if (this.isNullOrWhitespace(context.cipher.card.expiration) && context.lowerProperty.includes('expiry date')) { + const expSplit = (context.importRecord[context.property] as string).split('/'); + context.cipher.card.expMonth = expSplit[0]; + if (context.cipher.card.expMonth[0] === '0' && context.cipher.card.expMonth.length === 2) { + context.cipher.card.expMonth = context.cipher.card.expMonth.substr(1, 1); + } + context.cipher.card.expYear = expSplit[2].length > 4 ? expSplit[2].substr(0, 4) : expSplit[2]; + return true; + } + return false; + } +} diff --git a/src/importers/onepasswordWinCsvImporter.ts b/src/importers/onepasswordWinCsvImporter.ts deleted file mode 100644 index 6e0192e697..0000000000 --- a/src/importers/onepasswordWinCsvImporter.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { BaseImporter } from './baseImporter'; -import { Importer } from './importer'; - -import { ImportResult } from '../models/domain/importResult'; - -import { CipherType } from '../enums/cipherType'; -import { CardView, IdentityView } from '../models/view'; - -const IgnoredProperties = ['ainfo', 'autosubmit', 'notesplain', 'ps', 'scope', 'tags', 'title', 'uuid', 'notes']; - -export class OnePasswordWinCsvImporter extends BaseImporter implements Importer { - parse(data: string): Promise { - const result = new ImportResult(); - const results = this.parseCsv(data, true, { - quoteChar: '"', - escapeChar: '\\', - }); - if (results == null) { - result.success = false; - return Promise.resolve(result); - } - - results.forEach((value) => { - if (this.isNullOrWhitespace(this.getProp(value, 'title'))) { - return; - } - - const cipher = this.initLoginCipher(); - cipher.name = this.getValueOrDefault(this.getProp(value, 'title'), '--'); - - cipher.notes = this.getValueOrDefault(this.getProp(value, 'notesPlain'), '') + '\n' + - this.getValueOrDefault(this.getProp(value, 'notes'), '') + '\n'; - cipher.notes.trim(); - - const onePassType = this.getValueOrDefault(this.getProp(value, 'type'), 'Login') - switch (onePassType) { - case 'Credit Card': - cipher.type = CipherType.Card; - cipher.card = new CardView(); - IgnoredProperties.push('type'); - break; - case 'Identity': - cipher.type = CipherType.Identity; - cipher.identity = new IdentityView(); - IgnoredProperties.push('type'); - break; - case 'Login': - case 'Secure Note': - IgnoredProperties.push('type'); - default: - break; - } - - if (!this.isNullOrWhitespace(this.getProp(value, 'number')) && - !this.isNullOrWhitespace(this.getProp(value, 'expiry date'))) { - cipher.type = CipherType.Card; - cipher.card = new CardView(); - } - - let altUsername: string = null; - for (const property in value) { - if (!value.hasOwnProperty(property) || this.isNullOrWhitespace(value[property])) { - continue; - } - - const lowerProp = property.toLowerCase(); - if (cipher.type === CipherType.Login) { - if (this.isNullOrWhitespace(cipher.login.password) && lowerProp === 'password') { - cipher.login.password = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.login.username) && lowerProp === 'username') { - cipher.login.username = value[property]; - continue; - } else if ((cipher.login.uris == null || cipher.login.uri.length === 0) && lowerProp === 'urls') { - const urls = value[property].split(this.newLineRegex); - cipher.login.uris = this.makeUriArray(urls); - continue; - } else if ((lowerProp === 'url')) { - if (cipher.login.uris == null) { - cipher.login.uris = []; - } - cipher.login.uris.concat(this.makeUriArray(value[property])); - continue; - } - } else if (cipher.type === CipherType.Card) { - if (this.isNullOrWhitespace(cipher.card.number) && lowerProp.includes('number')) { - cipher.card.number = value[property]; - cipher.card.brand = this.getCardBrand(this.getProp(value, 'number')); - continue; - } else if (this.isNullOrWhitespace(cipher.card.code) && lowerProp.includes('verification number')) { - cipher.card.code = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.card.cardholderName) && lowerProp.includes('cardholder name')) { - cipher.card.cardholderName = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.card.expiration) && lowerProp.includes('expiry date') && - value[property].length === 7) { - cipher.card.expMonth = (value[property] as string).substr(0, 2); - if (cipher.card.expMonth[0] === '0') { - cipher.card.expMonth = cipher.card.expMonth.substr(1, 1); - } - cipher.card.expYear = (value[property] as string).substr(3, 4); - continue; - } else if (lowerProp === 'type' || lowerProp === 'type(type)') { - // Skip since brand was determined from number above - continue; - } - } else if (cipher.type === CipherType.Identity) { - if (this.isNullOrWhitespace(cipher.identity.firstName) && lowerProp.includes('first name')) { - cipher.identity.firstName = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.identity.middleName) && lowerProp.includes('initial')) { - cipher.identity.middleName = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.identity.lastName) && lowerProp.includes('last name')) { - cipher.identity.lastName = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.identity.username) && lowerProp.includes('username')) { - cipher.identity.username = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.identity.company) && lowerProp.includes('company')) { - cipher.identity.company = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.identity.phone) && lowerProp.includes('default phone')) { - cipher.identity.phone = value[property]; - continue; - } else if (this.isNullOrWhitespace(cipher.identity.email) && lowerProp.includes('email')) { - cipher.identity.email = value[property]; - continue; - } - } - - if (IgnoredProperties.indexOf(lowerProp) === -1 && !lowerProp.startsWith('section:') && - !lowerProp.startsWith('section ')) { - if (altUsername == null && lowerProp === 'email') { - altUsername = value[property]; - } - else if (lowerProp === 'created date' || lowerProp === 'modified date') { - const readableDate = new Date(parseInt(value[property], 10) * 1000).toUTCString(); - this.processKvp(cipher, '1Password ' + property, readableDate); - continue; - } - this.processKvp(cipher, property, value[property]); - } - } - - if (cipher.type === CipherType.Login && !this.isNullOrWhitespace(altUsername) && - this.isNullOrWhitespace(cipher.login.username) && altUsername.indexOf('://') === -1) { - cipher.login.username = altUsername; - } - - this.convertToNoteIfNeeded(cipher); - this.cleanupCipher(cipher); - result.ciphers.push(cipher); - }); - - result.success = true; - return Promise.resolve(result); - } - - private getProp(obj: any, name: string): any { - const lowerObj = Object.entries(obj).reduce((agg: any, entry: [string, any]) => { - agg[entry[0].toLowerCase()] = entry[1]; - return agg; - }, {}); - return lowerObj[name.toLowerCase()]; - } -} diff --git a/src/misc/utils.ts b/src/misc/utils.ts index cacda81e3e..619270212f 100644 --- a/src/misc/utils.ts +++ b/src/misc/utils.ts @@ -94,7 +94,7 @@ export class Utils { } static fromBufferToUrlB64(buffer: ArrayBuffer): string { - return Utils.fromB64toUrlB64(Utils.fromBufferToB64(buffer)) + return Utils.fromB64toUrlB64(Utils.fromBufferToB64(buffer)); } static fromB64toUrlB64(b64Str: string) { diff --git a/src/models/request/sendRequest.ts b/src/models/request/sendRequest.ts index a2c64a2ca0..035fe8e03f 100644 --- a/src/models/request/sendRequest.ts +++ b/src/models/request/sendRequest.ts @@ -1,6 +1,6 @@ import { SendType } from '../../enums/sendType'; -import { SendFileApi } from '../api/sendFileApi' +import { SendFileApi } from '../api/sendFileApi'; import { SendTextApi } from '../api/sendTextApi'; import { Send } from '../domain/send'; diff --git a/src/models/request/tokenRequest.ts b/src/models/request/tokenRequest.ts index a3db628670..1180b08120 100644 --- a/src/models/request/tokenRequest.ts +++ b/src/models/request/tokenRequest.ts @@ -25,8 +25,8 @@ export class TokenRequest { this.codeVerifier = codes[1]; this.redirectUri = codes[2]; } else if (clientIdClientSecret != null && clientIdClientSecret.length > 1) { - this.clientId = clientIdClientSecret[0] - this.clientSecret = clientIdClientSecret[1] + this.clientId = clientIdClientSecret[0]; + this.clientSecret = clientIdClientSecret[1]; } this.token = token; this.provider = provider; diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index d8ec84277d..de52de67e1 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -283,7 +283,7 @@ export class AuthService implements AuthServiceAbstraction { codeCodeVerifier = null; } if (clientId != null && clientSecret != null) { - clientIdClientSecret = [clientId, clientSecret] + clientIdClientSecret = [clientId, clientSecret]; } else { clientIdClientSecret = null; } diff --git a/src/services/import.service.ts b/src/services/import.service.ts index 3b2d4ed444..d41cee06bd 100644 --- a/src/services/import.service.ts +++ b/src/services/import.service.ts @@ -52,8 +52,9 @@ import { LogMeOnceCsvImporter } from '../importers/logMeOnceCsvImporter'; import { MeldiumCsvImporter } from '../importers/meldiumCsvImporter'; import { MSecureCsvImporter } from '../importers/msecureCsvImporter'; import { MykiCsvImporter } from '../importers/mykiCsvImporter'; -import { OnePassword1PifImporter } from '../importers/onepassword1PifImporter'; -import { OnePasswordWinCsvImporter } from '../importers/onepasswordWinCsvImporter'; +import { OnePassword1PifImporter } from '../importers/onepasswordImporters/onepassword1PifImporter'; +import { OnePasswordMacCsvImporter } from '../importers/onepasswordImporters/onepasswordMacCsvImporter'; +import { OnePasswordWinCsvImporter } from '../importers/onepasswordImporters/onepasswordWinCsvImporter'; import { PadlockCsvImporter } from '../importers/padlockCsvImporter'; import { PassKeepCsvImporter } from '../importers/passkeepCsvImporter'; import { PassmanJsonImporter } from '../importers/passmanJsonImporter'; @@ -90,6 +91,7 @@ export class ImportService implements ImportServiceAbstraction { regularImportOptions: ImportOption[] = [ { id: 'keepassxcsv', name: 'KeePassX (csv)' }, { id: '1passwordwincsv', name: '1Password 6 and 7 Windows (csv)' }, + { id: '1passwordmaccsv', name: '1Password 6 and 7 Mac (csv)' }, { id: 'roboformcsv', name: 'RoboForm (csv)' }, { id: 'keepercsv', name: 'Keeper (csv)' }, { id: 'enpasscsv', name: 'Enpass (csv)' }, @@ -215,6 +217,8 @@ export class ImportService implements ImportServiceAbstraction { return new OnePassword1PifImporter(); case '1passwordwincsv': return new OnePasswordWinCsvImporter(); + case '1passwordmaccsv': + return new OnePasswordMacCsvImporter(); case 'keepercsv': return new KeeperCsvImporter(); case 'passworddragonxml': diff --git a/tslint.json b/tslint.json index 41090a98e1..3fe8b5b5ba 100644 --- a/tslint.json +++ b/tslint.json @@ -51,6 +51,10 @@ "check-separator", "check-type" ], - "max-classes-per-file": false + "max-classes-per-file": false, + "semicolon": [ + true, + "always" + ] } }