Skip to main content

bitwarden_importers/importers/onepassword/convert/
identity.rs

1//! The Identity category.
2
3use bitwarden_exporters::Identity;
4
5use super::{claimed::Claimed, value::render_value};
6use crate::importers::onepassword::access::wire::VaultItemDetails;
7
8/// The parts of 1Password's address object that have an identity slot.
9const ADDRESS_PARTS: [&str; 5] = ["street", "city", "state", "zip", "country"];
10
11/// Reads a personal identity. 1Password's template is far wider than Bitwarden's: the birth date,
12/// job title, messenger handles and the other phone numbers have no slot and stay custom fields.
13pub(super) fn identity(details: &VaultItemDetails) -> (Identity, Claimed) {
14    let mut claimed = Claimed::default();
15    // 1Password keeps the whole address in one field, so it is claimed only when every part it
16    // fills in has a slot. Otherwise the leftover pass keeps all of it.
17    let address = claimed.take(details, "address", |field| {
18        let parts = field.value.as_ref()?.as_object()?;
19        parts
20            .iter()
21            .all(|(part, value)| {
22                ADDRESS_PARTS.contains(&part.as_str()) || render_value(None, value).is_none()
23            })
24            .then_some(parts)
25    });
26    let address_part = |part: &str| render_value(None, address?.get(part)?);
27
28    let identity = Identity {
29        title: None,
30        first_name: claimed.take_text(details, "firstname"),
31        middle_name: claimed.take_text(details, "initial"),
32        last_name: claimed.take_text(details, "lastname"),
33        address1: address_part("street"),
34        address2: None,
35        address3: None,
36        city: address_part("city"),
37        state: address_part("state"),
38        postal_code: address_part("zip"),
39        country: address_part("country"),
40        company: claimed.take_text(details, "company"),
41        email: claimed.take_text(details, "email"),
42        phone: claimed.take_text(details, "defphone"),
43        username: claimed.take_text(details, "username"),
44        // 1Password keeps these in categories of their own, never on an identity.
45        ssn: None,
46        passport_number: None,
47        license_number: None,
48    };
49
50    (identity, claimed)
51}