From c5d01a97ae4fda0871c8fd49777be325376ba5df Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 25 Jul 2020 03:08:53 -0400 Subject: add totp, uris, and custom fields to --full output --- src/api.rs | 31 +++++++++++++++++ src/bin/rbw/commands.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++--- src/db.rs | 10 ++++++ 3 files changed, 127 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/api.rs b/src/api.rs index 6ee77ca..aca87b0 100644 --- a/src/api.rs +++ b/src/api.rs @@ -139,6 +139,8 @@ struct SyncResCipher { notes: Option, #[serde(rename = "PasswordHistory")] password_history: Option>, + #[serde(rename = "Fields")] + fields: Option>, } impl SyncResCipher { @@ -172,6 +174,7 @@ impl SyncResCipher { crate::db::EntryData::Login { username: login.username.clone(), password: login.password.clone(), + totp: login.totp.clone(), uris: login.uris.as_ref().map_or_else( std::vec::Vec::new, |uris| { @@ -215,6 +218,17 @@ impl SyncResCipher { } else { return None; }; + let fields = if let Some(fields) = &self.fields { + fields + .iter() + .map(|field| crate::db::Field { + name: field.name.clone(), + value: field.value.clone(), + }) + .collect() + } else { + vec![] + }; Some(crate::db::Entry { id: self.id.clone(), org_id: self.organization_id.clone(), @@ -222,6 +236,7 @@ impl SyncResCipher { folder_id: folder_id.map(std::string::ToString::to_string), name: self.name.clone(), data, + fields, notes: self.notes.clone(), history, }) @@ -260,6 +275,8 @@ struct CipherLogin { username: Option, #[serde(rename = "Password")] password: Option, + #[serde(rename = "Totp")] + totp: Option, #[serde(rename = "Uris")] uris: Option>, } @@ -337,6 +354,16 @@ struct SyncResPasswordHistory { password: String, } +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +struct SyncResField { + #[serde(rename = "Type")] + ty: u32, + #[serde(rename = "Name")] + name: Option, + #[serde(rename = "Value")] + value: Option, +} + #[derive(serde::Serialize, Debug)] struct CiphersPostReq { #[serde(rename = "type")] @@ -546,6 +573,7 @@ impl Client { crate::db::EntryData::Login { username, password, + totp, uris, } => { let uris = if uris.is_empty() { @@ -562,6 +590,7 @@ impl Client { req.login = Some(CipherLogin { username: username.clone(), password: password.clone(), + totp: totp.clone(), uris, }) } @@ -676,6 +705,7 @@ impl Client { crate::db::EntryData::Login { username, password, + totp, uris, } => { let uris = if uris.is_empty() { @@ -692,6 +722,7 @@ impl Client { req.login = Some(CipherLogin { username: username.clone(), password: password.clone(), + totp: totp.clone(), uris, }); } diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index 9ee80fc..3180cc9 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -17,6 +17,7 @@ struct DecryptedCipher { folder: Option, name: String, data: DecryptedData, + fields: Vec, notes: Option, history: Vec, } @@ -78,10 +79,30 @@ impl DecryptedCipher { fn display_long(&self, desc: &str) { match &self.data { - DecryptedData::Login { username, .. } => { + DecryptedData::Login { + username, + totp, + uris, + .. + } => { let mut displayed = self.display_short(desc); displayed |= self.display_field("Username", username.as_deref()); + displayed |= + self.display_field("TOTP Secret", totp.as_deref()); + + if let Some(uris) = uris { + for uri in uris { + displayed |= self.display_field("URI", Some(uri)); + } + } + + for field in &self.fields { + displayed |= self.display_field( + field.name.as_deref().unwrap_or_else(|| "(null)"), + Some(field.value.as_deref().unwrap_or_else(|| "")), + ); + } if let Some(notes) = &self.notes { if displayed { @@ -292,6 +313,8 @@ enum DecryptedData { Login { username: Option, password: Option, + totp: Option, + uris: Option>, }, Card { cardholder_name: Option, @@ -323,6 +346,13 @@ enum DecryptedData { SecureNote, } +#[derive(Debug, Clone)] +#[cfg_attr(test, derive(Eq, PartialEq))] +struct DecryptedField { + name: Option, + value: Option, +} + #[derive(Debug, Clone)] #[cfg_attr(test, derive(Eq, PartialEq))] struct DecryptedHistoryEntry { @@ -593,6 +623,7 @@ pub fn add( username, password, uris, + totp: None, }, notes.as_deref(), folder_id.as_deref(), @@ -682,6 +713,7 @@ pub fn generate( username, password: Some(password), uris, + totp: None, }, None, folder_id.as_deref(), @@ -743,13 +775,14 @@ pub fn edit( }) .transpose()?; let mut history = entry.history.clone(); - let (entry_username, entry_password, entry_uris) = + let (entry_username, entry_password, entry_uris, entry_totp) = match &entry.data { rbw::db::EntryData::Login { username, password, uris, - } => (username, password, uris), + totp, + } => (username, password, uris, totp), _ => unreachable!(), }; let new_history_entry = rbw::db::HistoryEntry { @@ -764,6 +797,7 @@ pub fn edit( username: entry_username.clone(), password, uris: entry_uris.to_vec(), + totp: entry_totp.clone(), }; (data, notes, history) } @@ -1069,6 +1103,34 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result { None } }; + let fields = entry + .fields + .iter() + .map(|field| { + Ok(DecryptedField { + name: field + .name + .as_ref() + .map(|name| { + crate::actions::decrypt( + &name, + entry.org_id.as_deref(), + ) + }) + .transpose()?, + value: field + .value + .as_ref() + .map(|value| { + crate::actions::decrypt( + &value, + entry.org_id.as_deref(), + ) + }) + .transpose()?, + }) + }) + .collect::>()?; let notes = entry .notes .as_ref() @@ -1097,7 +1159,10 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result { let data = match &entry.data { rbw::db::EntryData::Login { - username, password, .. + username, + password, + totp, + uris, } => DecryptedData::Login { username: decrypt_field( "username", @@ -1109,6 +1174,17 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result { password.as_deref(), entry.org_id.as_deref(), ), + totp: decrypt_field( + "totp", + totp.as_deref(), + entry.org_id.as_deref(), + ), + uris: uris + .iter() + .map(|s| { + decrypt_field("uri", Some(s), entry.org_id.as_deref()) + }) + .collect(), }, rbw::db::EntryData::Card { cardholder_name, @@ -1262,6 +1338,7 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result { folder, name: crate::actions::decrypt(&entry.name, entry.org_id.as_deref())?, data, + fields, notes, history, }) @@ -1455,7 +1532,9 @@ mod test { }), password: None, uris: vec![], + totp: None, }, + fields: vec![], notes: None, history: vec![], }, @@ -1466,7 +1545,10 @@ mod test { data: DecryptedData::Login { username: username.map(std::string::ToString::to_string), password: None, + totp: None, + uris: None, }, + fields: vec![], notes: None, history: vec![], }, diff --git a/src/db.rs b/src/db.rs index c695577..d519f00 100644 --- a/src/db.rs +++ b/src/db.rs @@ -13,6 +13,7 @@ pub struct Entry { pub folder_id: Option, pub name: String, pub data: EntryData, + pub fields: Vec, pub notes: Option, pub history: Vec, } @@ -24,6 +25,7 @@ pub enum EntryData { Login { username: Option, password: Option, + totp: Option, uris: Vec, }, Card { @@ -56,6 +58,14 @@ pub enum EntryData { SecureNote, } +#[derive( + serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq, +)] +pub struct Field { + pub name: Option, + pub value: Option, +} + #[derive( serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq, )] -- cgit v1.2.3-54-g00ecf