Enhance SafeInCloud import (#307)
* don't import deleted cards * keep favourite status while importing from saveInCloud * import all passwords from saveInCloud * add test data
This commit is contained in:
parent
7cc23dab72
commit
a36f1c25d8
|
@ -0,0 +1,62 @@
|
|||
export const data = `
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<database> <!-- LABELS -->
|
||||
<label name="Business" id="1"></label>
|
||||
<label name="Samples" id="3"></label>
|
||||
<label name="Web Accounts" id="4" type="web_accounts"></label> <!-- TEMPLATES -->
|
||||
<card title="Web Account" id="102" symbol="web_site" color="gray" template="true" autofill="on">
|
||||
<field name="Login" type="login" autofill="username"></field>
|
||||
<field name="Password" type="password" autofill="current-password"></field>
|
||||
<field name="Website" type="website" autofill="url"></field>
|
||||
<field name="One-time password" type="one_time_password" autofill="one-time-code"></field>
|
||||
</card>
|
||||
<card title="Note (Sample)" id="206" symbol="note" color="yellow" type="note" autofill="off">
|
||||
<notes>This is a sample note.</notes>
|
||||
<label_id>3</label_id>
|
||||
</card>
|
||||
<ghost id="2" time_stamp="1615910263225"></ghost>
|
||||
<card title="Visa Card (Sample)" id="205" symbol="visa" color="0xff3f5ca8" autofill="on" time_stamp="1615910280931">
|
||||
<field name="Number" type="number" autofill="cc-number">5555123456789000</field>
|
||||
<field name="Owner" type="text" autofill="cc-name">John Smith</field>
|
||||
<field name="Expires" type="expiry" autofill="cc-exp">01/23</field>
|
||||
<field name="CVV" type="pin" autofill="cc-csc">555</field>
|
||||
<field name="PIN" type="pin" autofill="off">1111</field>
|
||||
<field name="Blocking" type="phone" autofill="off">555-0153</field>
|
||||
<label_id>1</label_id>
|
||||
</card>
|
||||
<card title="Facebook (Sample)" id="301" symbol="f" color="0xff3f5ca8" autofill="on" deleted="true" time_stamp="1615910298700">
|
||||
<field name="Login" type="login" autofill="username">john555@gmail.com</field>
|
||||
<field name="Password" type="password" autofill="current-password">early91*Fail*</field>
|
||||
<field name="Website" type="website" autofill="url">https://www.facebook.com</field>
|
||||
<label_id>3</label_id>
|
||||
</card>
|
||||
<card title="Google (Sample)" id="300" symbol="g" color="blue" autofill="on" prev_stamp="1615910345167" time_stamp="1615910386722">
|
||||
<field name="Email" type="login" autofill="username">john555@gmail.com</field>
|
||||
<field name="Password" type="password" score="4" hash="5c76cb4d2fd87820be94530315581e14" autofill="current-password">plain79{Area{</field>
|
||||
<field name="Website" type="website" autofill="url">https://www.google.com</field>
|
||||
<label_id>3</label_id>
|
||||
<field type="one_time_password" name="One-time password" history="{"1615910350891":""}" autofill="one-time-code">thisisanotp</field>
|
||||
<field type="secret" name="2FA-Reset" history="{"1615910350891":""}" autofill="off">thisshouldbehidden</field>
|
||||
</card>
|
||||
<card title="Passport (Sample)" id="203" symbol="passport" color="purple" autofill="off" time_stamp="1615910424608">
|
||||
<field name="Number" type="number" autofill="off">555111111</field>
|
||||
<field name="Name" type="text" autofill="off">John Smith</field>
|
||||
<field name="Birthday" type="date" autofill="off">05/05/1980</field>
|
||||
<field name="Issued" type="date" autofill="off">01/01/2018</field>
|
||||
<field name="Expires" type="expiry" autofill="off" score="1830380399000" hash="52f13d61109f06e642f86caf5e140474">01/01/2028</field>
|
||||
<label_id>3</label_id>
|
||||
<notes>This is a note attached to a card</notes>
|
||||
</card>
|
||||
<card title="Twitter (Sample)" id="302" symbol="t" color="blue" autofill="off" time_stamp="1615910462627">
|
||||
<field name="Login" type="login" autofill="username">john555@gmail.com</field>
|
||||
<field name="Website" type="website" autofill="url">https://twitter.com</field>
|
||||
<label_id>3</label_id>
|
||||
<field type="password" name="Secret login data" autofill="username" history="{"1615910438286":""}" score="0" hash="f9f910baf9c2cfb4f640e2d231f4a39b">shouldbepassword</field>
|
||||
</card>
|
||||
<card title="Laptop (Sample)" id="303" symbol="laptop" color="gray" autofill="off" star="false" prev_stamp="1615910472161" time_stamp="1615910473734">
|
||||
<field name="Login" type="login" autofill="username">john555</field>
|
||||
<field name="Password" type="password" autofill="current-password">Save63\apple\</field>
|
||||
<label_id>3</label_id>
|
||||
</card>
|
||||
</database>
|
||||
`
|
|
@ -9,6 +9,9 @@ import { SecureNoteView } from '../models/view/secureNoteView';
|
|||
import { CipherType } from '../enums/cipherType';
|
||||
import { SecureNoteType } from '../enums/secureNoteType';
|
||||
|
||||
import { FieldType } from '../enums';
|
||||
import { CipherView, FieldView } from '../models/view';
|
||||
|
||||
export class SafeInCloudXmlImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
|
@ -39,7 +42,7 @@ export class SafeInCloudXmlImporter extends BaseImporter implements Importer {
|
|||
});
|
||||
|
||||
Array.from(doc.querySelectorAll('database > card')).forEach(cardEl => {
|
||||
if (cardEl.getAttribute('template') === 'true') {
|
||||
if (cardEl.getAttribute('template') === 'true' || cardEl.getAttribute('deleted') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -54,6 +57,10 @@ export class SafeInCloudXmlImporter extends BaseImporter implements Importer {
|
|||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(cardEl.getAttribute('title'), '--');
|
||||
|
||||
if (cardEl.getAttribute('star') === 'true') {
|
||||
cipher.favorite = true;
|
||||
}
|
||||
|
||||
const cardType = cardEl.getAttribute('type');
|
||||
if (cardType === 'note') {
|
||||
cipher.type = CipherType.SecureNote;
|
||||
|
@ -69,15 +76,17 @@ export class SafeInCloudXmlImporter extends BaseImporter implements Importer {
|
|||
const fieldType = this.getValueOrDefault(fieldEl.getAttribute('type'), '').toLowerCase();
|
||||
if (fieldType === 'login') {
|
||||
cipher.login.username = text;
|
||||
} else if (fieldType === 'password') {
|
||||
cipher.login.password = text;
|
||||
} else if (fieldType === 'password' || fieldType === 'secret') {
|
||||
// safeInCloud allows for more than one password. we just insert them here and find the one used as password later
|
||||
this.processKvp(cipher, name, text, FieldType.Hidden);
|
||||
} else if (fieldType === 'one_time_password') {
|
||||
cipher.login.totp = text;
|
||||
} else if (fieldType === 'notes') {
|
||||
cipher.notes += (text + '\n');
|
||||
} else if (fieldType === 'weblogin' || fieldType === 'website') {
|
||||
cipher.login.uris = this.makeUriArray(text);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.processKvp(cipher, name, text);
|
||||
}
|
||||
});
|
||||
|
@ -87,6 +96,7 @@ export class SafeInCloudXmlImporter extends BaseImporter implements Importer {
|
|||
cipher.notes += (notesEl.textContent + '\n');
|
||||
});
|
||||
|
||||
this.setPassword(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
@ -98,4 +108,28 @@ export class SafeInCloudXmlImporter extends BaseImporter implements Importer {
|
|||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
// Choose a password from all passwords. Take one that has password in its name, or the first one if there is no such entry
|
||||
// if its name is password, we can safely remove it form the fields. otherwise, it would maybe be best to keep it as a hidden field
|
||||
setPassword(cipher: CipherView) {
|
||||
const candidates = cipher.fields.filter(field => field.type === FieldType.Hidden);
|
||||
if (!candidates.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let choice: FieldView;
|
||||
for (const field of candidates) {
|
||||
if (this.passwordFieldNames.includes(field.name.toLowerCase())) {
|
||||
choice = field;
|
||||
cipher.fields = cipher.fields.filter(f => f !== choice);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!choice) {
|
||||
choice = candidates[0];
|
||||
}
|
||||
|
||||
cipher.login.password = choice.value;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue