From 9db9908cd3aa97933a543800d69945484a81e62a Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 20 Apr 2020 00:05:01 -0400 Subject: add folder as an option for list fields --- src/api.rs | 32 ++++++++++++++++++++++++++++++-- src/bin/rbw/commands.rs | 23 +++++++++++++++++++++++ src/db.rs | 1 + 3 files changed, 54 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/api.rs b/src/api.rs index 4de1285..eeca762 100644 --- a/src/api.rs +++ b/src/api.rs @@ -75,12 +75,16 @@ struct SyncRes { ciphers: Vec, #[serde(rename = "Profile")] profile: SyncResProfile, + #[serde(rename = "Folders")] + folders: Vec, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] struct SyncResCipher { #[serde(rename = "Id")] id: String, + #[serde(rename = "FolderId")] + folder_id: Option, #[serde(rename = "Name")] name: String, #[serde(rename = "Login")] @@ -93,7 +97,10 @@ struct SyncResCipher { impl SyncResCipher { // TODO: handle other kinds of entries other than login - fn to_entry(&self) -> Option { + fn to_entry( + &self, + folders: &[SyncResFolder], + ) -> Option { if let Some(login) = &self.login { let history = if let Some(history) = &self.password_history { history @@ -106,8 +113,20 @@ impl SyncResCipher { } else { vec![] }; + let folder = if let Some(folder_id) = &self.folder_id { + let mut folder_name = None; + for folder in folders { + if &folder.id == folder_id { + folder_name = Some(folder.name.clone()); + } + } + folder_name + } else { + None + }; Some(crate::db::Entry { id: self.id.clone(), + folder, name: self.name.clone(), username: login.username.clone(), password: login.password.clone(), @@ -126,6 +145,14 @@ struct SyncResProfile { key: String, } +#[derive(serde::Deserialize, Debug, Clone)] +struct SyncResFolder { + #[serde(rename = "Id")] + id: String, + #[serde(rename = "Name")] + name: String, +} + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] struct SyncResLogin { #[serde(rename = "Username")] @@ -301,10 +328,11 @@ impl Client { reqwest::StatusCode::OK => { let sync_res: SyncRes = res.json().await.context(crate::error::Reqwest)?; + let folders = sync_res.folders.clone(); let ciphers = sync_res .ciphers .iter() - .filter_map(SyncResCipher::to_entry) + .filter_map(|cipher| cipher.to_entry(&folders)) .collect(); Ok((sync_res.profile.key, ciphers)) } diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index ee30950..6c9d18c 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -4,6 +4,7 @@ use anyhow::Context as _; #[cfg_attr(test, derive(Eq, PartialEq))] struct DecryptedCipher { id: String, + folder: Option, name: String, username: Option, password: Option, @@ -22,6 +23,7 @@ enum ListField { Name, Id, User, + Folder, } impl std::convert::TryFrom<&str> for ListField { @@ -32,6 +34,7 @@ impl std::convert::TryFrom<&str> for ListField { "name" => Self::Name, "id" => Self::Id, "user" => Self::User, + "folder" => Self::Folder, _ => return Err(anyhow::anyhow!("unknown field {}", s)), }) } @@ -127,6 +130,11 @@ pub fn list(fields: &[&str]) -> anyhow::Result<()> { .as_ref() .map(std::string::ToString::to_string) .unwrap_or_else(|| "".to_string()), + ListField::Folder => cipher + .folder + .as_ref() + .map(std::string::ToString::to_string) + .unwrap_or_else(|| "".to_string()), }) .collect(); println!("{}", values.join("\t")); @@ -610,6 +618,18 @@ fn find_entry_raw( } fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result { + let folder = entry + .folder + .as_ref() + .map(|folder| crate::actions::decrypt(folder)) + .transpose(); + let folder = match folder { + Ok(folder) => folder, + Err(e) => { + log::warn!("failed to decrypt folder name: {}", e); + None + } + }; let username = entry .username .as_ref() @@ -658,6 +678,7 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result { .collect::>()?; Ok(DecryptedCipher { id: entry.id.clone(), + folder, name: crate::actions::decrypt(&entry.name)?, username, password, @@ -780,6 +801,7 @@ mod test { ( rbw::db::Entry { id: "irrelevant".to_string(), + folder: None, name: "this is the encrypted name".to_string(), username: username .map(|_| "this is the encrypted username".to_string()), @@ -789,6 +811,7 @@ mod test { }, DecryptedCipher { id: "irrelevant".to_string(), + folder: None, name: name.to_string(), username: username.map(std::string::ToString::to_string), password: None, diff --git a/src/db.rs b/src/db.rs index 9a02598..206e888 100644 --- a/src/db.rs +++ b/src/db.rs @@ -8,6 +8,7 @@ use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; )] pub struct Entry { pub id: String, + pub folder: Option, pub name: String, pub username: Option, pub password: Option, -- cgit v1.2.3-54-g00ecf