mirror of
https://github.com/bitwarden/browser
synced 2025-12-22 03:03:43 +00:00
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:
@@ -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>;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -68,7 +68,7 @@ export abstract class BaseImporter {
|
||||
protected parseCsvOptions = {
|
||||
encoding: 'UTF-8',
|
||||
skipEmptyLines: false,
|
||||
}
|
||||
};
|
||||
|
||||
protected organization() {
|
||||
return this.organizationId != null;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
288
src/importers/onepasswordImporters/onepasswordCsvImporter.ts
Normal file
288
src/importers/onepasswordImporters/onepasswordCsvImporter.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()];
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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':
|
||||
|
||||
Reference in New Issue
Block a user