Skip to main content

bitwarden_importers/importers/onepassword/convert/
credential.rs

1//! The categories built around a credential, all of which land on a login. Each keeps the same
2//! three slots under ids of its own, and everything else rides along as a custom field.
3
4use bitwarden_exporters::Login;
5
6use super::{
7    claimed::Claimed,
8    login::{first_totp, login_uris, website_addresses},
9    value::non_blank,
10};
11use crate::importers::onepassword::access::wire::{VaultItemDetails, VaultItemOverview};
12
13/// The section field ids a category uses for the username, the password and the address the
14/// credential belongs to. A slot the category does not have is `None`.
15pub(super) struct CredentialFields {
16    username: Option<&'static str>,
17    password: Option<&'static str>,
18    address: Option<&'static str>,
19}
20
21pub(super) const SERVER_FIELDS: CredentialFields = CredentialFields {
22    username: Some("username"),
23    password: Some("password"),
24    address: Some("url"),
25};
26
27pub(super) const DATABASE_FIELDS: CredentialFields = CredentialFields {
28    username: Some("username"),
29    password: Some("password"),
30    address: Some("hostname"),
31};
32
33pub(super) const API_CREDENTIAL_FIELDS: CredentialFields = CredentialFields {
34    username: Some("username"),
35    password: Some("credential"),
36    address: Some("hostname"),
37};
38
39pub(super) const EMAIL_ACCOUNT_FIELDS: CredentialFields = CredentialFields {
40    username: Some("pop_username"),
41    password: Some("pop_password"),
42    address: Some("provider_website"),
43};
44
45/// A router has no account name, and its own password is the base station's; the wireless network
46/// key is a separate field and stays with the rest.
47pub(super) const WIRELESS_ROUTER_FIELDS: CredentialFields = CredentialFields {
48    username: None,
49    password: Some("password"),
50    address: Some("server"),
51};
52
53const PASSWORD_FIELDS: CredentialFields = CredentialFields {
54    username: None,
55    password: None,
56    address: None,
57};
58
59/// Maps a category that carries a credential onto a login. The item's website addresses come
60/// first; the category's own address follows, so a server or database is reachable by its host.
61pub(super) fn credential_login(
62    overview: &VaultItemOverview,
63    details: &VaultItemDetails,
64    ids: &CredentialFields,
65) -> (Login, Claimed) {
66    let mut claimed = Claimed::default();
67    let mut take = |id: Option<&str>| claimed.take_text(details, id?);
68    let username = take(ids.username);
69    let password = take(ids.password);
70    let address = take(ids.address);
71
72    let (totp_position, totp) = first_totp(details).unzip();
73    claimed.fields.extend(totp_position);
74
75    let mut login = Login {
76        username,
77        password,
78        login_uris: login_uris(website_addresses(overview).chain(address.as_deref())),
79        totp,
80        fido2_credentials: None,
81    };
82    login.sanitize_uris();
83
84    (login, claimed)
85}
86
87/// A Password item keeps its secret in the details rather than in a field, and has no username or
88/// address of its own.
89pub(super) fn password(
90    overview: &VaultItemOverview,
91    details: &VaultItemDetails,
92) -> (Login, Claimed) {
93    let (mut login, claimed) = credential_login(overview, details, &PASSWORD_FIELDS);
94    login.password = details
95        .password
96        .as_deref()
97        .and_then(non_blank)
98        .map(str::to_string);
99
100    (login, claimed)
101}