Fix 1password importer (#222)

* Change cipher type based on csv type header

* Test identity and credit card import

* Do not use node 'fs' module

Karma is being used for automated tests so node modules are not available

* WIP: mac and windows 1password importer split

Need to improve windows field identification to limit secret data
exposure and improve user experience

* Hide fields with likely secret values

Co-authored-by: Matt Gibson <mdgibson@Matts-MBP.lan>
This commit is contained in:
Matt Gibson 2020-12-08 11:29:57 -06:00 committed by GitHub
parent 2d62e10d98
commit 72bf18f369
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 587 additions and 245 deletions

View File

@ -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';

View File

@ -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',
}));
});
});

View File

@ -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]);
});
});

View File

@ -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]);
});
});

View File

@ -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 reservations(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",,,,,,,`;

View File

@ -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)"`;

View File

@ -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","",,,,,"Its 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",,,,,,"",,,"",`;

View File

@ -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","Its 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/"`;

View File

@ -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 reservations(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",,,,,,"Its 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",,,,,,,,,,,,,`;

View File

@ -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","Its 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/"`;

View File

@ -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+' +

View File

@ -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+' +

View File

@ -5,7 +5,7 @@ export abstract class CryptoFunctionService {
pbkdf2: (password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512',
iterations: number) => Promise<ArrayBuffer>;
hkdf: (ikm: ArrayBuffer, salt: string | ArrayBuffer, info: string | ArrayBuffer,
outputByteSize: number, algorithm: 'sha256' | 'sha512') => Promise<ArrayBuffer>
outputByteSize: number, algorithm: 'sha256' | 'sha512') => Promise<ArrayBuffer>;
hkdfExpand: (prk: ArrayBuffer, info: string | ArrayBuffer, outputByteSize: number,
algorithm: 'sha256' | 'sha512') => Promise<ArrayBuffer>;
hash: (value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512' | 'md5') => Promise<ArrayBuffer>;

View File

@ -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 {

View File

@ -68,7 +68,7 @@ export abstract class BaseImporter {
protected parseCsvOptions = {
encoding: 'UTF-8',
skipEmptyLines: false,
}
};
protected organization() {
return this.organizationId != null;

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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<ImportResult> {
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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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<ImportResult> {
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()];
}
}

View File

@ -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) {

View File

@ -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';

View File

@ -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;

View File

@ -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;
}

View File

@ -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':

View File

@ -51,6 +51,10 @@
"check-separator",
"check-type"
],
"max-classes-per-file": false
"max-classes-per-file": false,
"semicolon": [
true,
"always"
]
}
}