diff --git a/crates/bitwarden/src/crypto/enc_string.rs b/crates/bitwarden/src/crypto/enc_string.rs index 308a013ec..5291ee853 100644 --- a/crates/bitwarden/src/crypto/enc_string.rs +++ b/crates/bitwarden/src/crypto/enc_string.rs @@ -124,6 +124,11 @@ impl FromStr for EncString { } impl EncString { + /// Synthetic sugar for mapping `Option` to `Result>` + pub(crate) fn try_from(s: Option) -> Result, Error> { + Ok(s.map(|s| s.parse()).transpose()?) + } + #[cfg(feature = "mobile")] pub(crate) fn from_buffer(buf: &[u8]) -> Result { if buf.is_empty() { diff --git a/crates/bitwarden/src/platform/sync.rs b/crates/bitwarden/src/platform/sync.rs index 2487d2a6d..6d5c73c4c 100644 --- a/crates/bitwarden/src/platform/sync.rs +++ b/crates/bitwarden/src/platform/sync.rs @@ -103,7 +103,10 @@ impl SyncResponse { .into_iter() .map(|c| c.into()) .collect(), - ciphers: ciphers.into_iter().map(|c| c.into()).collect(), + ciphers: ciphers + .into_iter() + .map(|c| c.try_into()) + .collect::>>()?, domains: response.domains.map(|d| (*d).into()), policies: response .policies diff --git a/crates/bitwarden/src/vault/cipher/cipher.rs b/crates/bitwarden/src/vault/cipher/cipher.rs index e63af8aa6..d869024b9 100644 --- a/crates/bitwarden/src/vault/cipher/cipher.rs +++ b/crates/bitwarden/src/vault/cipher/cipher.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use bitwarden_api_api::models::CipherDetailsResponseModel; use chrono::{DateTime, Utc}; use log::debug; @@ -16,7 +14,7 @@ use super::{ use crate::{ client::encryption_settings::EncryptionSettings, crypto::{Decryptable, EncString, Encryptable}, - error::Result, + error::{Error, Result}, vault::password_history, }; @@ -290,17 +288,19 @@ impl Decryptable for Cipher { } } -impl From for Cipher { - fn from(cipher: CipherDetailsResponseModel) -> Self { - Cipher { +impl TryFrom for Cipher { + type Error = Error; + + fn try_from(cipher: CipherDetailsResponseModel) -> Result { + Ok(Self { id: cipher.id, organization_id: cipher.organization_id, folder_id: cipher.folder_id, collection_ids: cipher.collection_ids.unwrap_or_default(), - name: EncString::from_str(&cipher.name.unwrap()).unwrap(), - notes: cipher.notes.map(|s| EncString::from_str(&s).unwrap()), - r#type: cipher.r#type.unwrap().into(), - login: cipher.login.map(|l| (*l).into()), + name: EncString::try_from(cipher.name)?.ok_or(Error::MissingFields)?, + notes: EncString::try_from(cipher.notes)?, + r#type: cipher.r#type.ok_or(Error::MissingFields)?.into(), + login: cipher.login.map(|l| (*l).try_into()).transpose()?, identity: cipher.identity.map(|i| (*i).into()), card: cipher.card.map(|c| (*c).into()), secure_note: cipher.secure_note.map(|s| (*s).into()), @@ -327,10 +327,10 @@ impl From for Cipher { password_history: cipher .password_history .map(|p| p.into_iter().map(|p| p.into()).collect()), - creation_date: cipher.creation_date.unwrap().parse().unwrap(), + creation_date: cipher.creation_date.ok_or(Error::MissingFields)?.parse()?, deleted_date: cipher.deleted_date.map(|d| d.parse().unwrap()), - revision_date: cipher.revision_date.unwrap().parse().unwrap(), - } + revision_date: cipher.revision_date.ok_or(Error::MissingFields)?.parse()?, + }) } } diff --git a/crates/bitwarden/src/vault/cipher/login.rs b/crates/bitwarden/src/vault/cipher/login.rs index 55d797583..a11c81df6 100644 --- a/crates/bitwarden/src/vault/cipher/login.rs +++ b/crates/bitwarden/src/vault/cipher/login.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use bitwarden_api_api::models::{CipherLoginModel, CipherLoginUriModel}; use chrono::{DateTime, Utc}; use schemars::JsonSchema; @@ -10,7 +8,7 @@ use uuid::Uuid; use crate::{ client::encryption_settings::EncryptionSettings, crypto::{Decryptable, EncString, Encryptable}, - error::Result, + error::{Error, Result}, }; #[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug, JsonSchema)] @@ -112,27 +110,35 @@ impl Decryptable for Login { } } -impl From for Login { - fn from(login: CipherLoginModel) -> Self { - Self { - username: login.username.map(|s| EncString::from_str(&s).unwrap()), - password: login.password.map(|s| EncString::from_str(&s).unwrap()), - password_revision_date: login.password_revision_date.map(|d| d.parse().unwrap()), +impl TryFrom for Login { + type Error = Error; + + fn try_from(login: CipherLoginModel) -> Result { + Ok(Self { + username: EncString::try_from(login.username)?, + password: EncString::try_from(login.password)?, + password_revision_date: login + .password_revision_date + .map(|d| d.parse()) + .transpose()?, uris: login .uris - .map(|v| v.into_iter().map(|u| u.into()).collect()), - totp: login.totp.map(|s| EncString::from_str(&s).unwrap()), + .map(|v| v.into_iter().map(|u| u.try_into()).collect()) + .transpose()?, + totp: EncString::try_from(login.totp)?, autofill_on_page_load: login.autofill_on_page_load, - } + }) } } -impl From for LoginUri { - fn from(uri: CipherLoginUriModel) -> Self { - Self { - uri: uri.uri.map(|s| EncString::from_str(&s).unwrap()), +impl TryFrom for LoginUri { + type Error = Error; + + fn try_from(uri: CipherLoginUriModel) -> Result { + Ok(Self { + uri: EncString::try_from(uri.uri)?, r#match: uri.r#match.map(|m| m.into()), - } + }) } }