From 30a388f7946bf12148d2be956e69034013dcf78e Mon Sep 17 00:00:00 2001 From: holly sparkles Date: Fri, 4 Aug 2023 12:41:09 +0200 Subject: [PATCH] refactor!: optimize loops BREAKING CHANGE: the signature for `register_key` has changed - `item_id` replaced with `item` for debug purposes - searching for items now uses `iter()` with `find()` and `filter()` --- src/bwutil.rs | 2 +- src/main.rs | 51 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/bwutil.rs b/src/bwutil.rs index 0254ae9..468cddd 100644 --- a/src/bwutil.rs +++ b/src/bwutil.rs @@ -26,7 +26,7 @@ pub struct BitwardenItem { #[serde(rename_all = "camelCase")] pub struct BitwardenFieldItem { pub name: String, - pub value: String + pub value: Option } /// Represents a file attachment in Bitwarden. diff --git a/src/main.rs b/src/main.rs index 27a8356..dc93488 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use gumdrop::Options; use anyhow::{Result, Context}; use colored::*; +use crate::bwutil::BitwardenItem; mod util; mod bwutil; @@ -59,8 +60,13 @@ fn main() -> Result<()> { debug_println!("Found {} folder(s) named `{}`", folders.len().to_string().cyan(), args.folder.cyan()); // Retrieve items from each folder since there may be multiple folders with the same name. + // Pre-filter the items by checking for the presence of custom fields AND at least one attachment. for folder in folders { - let folder_items = bwutil::exec_list_folder_items(&session_token, &folder.id)?; + let folder_items: Vec = bwutil::exec_list_folder_items(&session_token, &folder.id)?; + let folder_items: Vec<&BitwardenItem> = folder_items + .iter() + .filter(|&item| item.fields.is_some() && item.attachments.is_some()) + .collect(); debug_println!("Found {} item(s) in folder `{}` id({}) with at least one custom field and attachment", folder_items.len().to_string().cyan(), folder.name.cyan(), folder.id.cyan()); @@ -72,22 +78,24 @@ fn main() -> Result<()> { // The field name is not case sensitive in order to be more user-friendly. // - attachment (required) an attachment with the name specified in `private`. if let Some(fields) = &item.fields { - let mut key_filename = String::new(); - let mut key_passphrase = String::new(); - for field in fields { - if field.name.to_lowercase().eq(&args.key.to_lowercase()) { - key_filename.push_str(&field.value); - } - if field.name.to_lowercase().eq(&args.passphrase.to_lowercase()) { - key_passphrase.push_str(&field.value); - } - } - - if let Some(attachments) = &item.attachments { - for attachment in attachments { - if attachment.file_name.eq(&key_filename) { - let _key = register_key(&item.id, &attachment.id, &key_passphrase, &session_token)?; - } + let key_filename: String = fields + .iter() + .find(|field| field.name.to_lowercase().eq(&args.key.to_lowercase()) && field.value.is_some()) + .map(|field| field.value.as_ref().unwrap().clone()) + .unwrap_or_default(); + + if !&key_filename.is_empty() { + let key_passphrase: String = fields + .iter() + .find(|field| field.name.to_lowercase().eq(&args.passphrase.to_lowercase()) && field.value.is_some()) + .map(|field| field.value.as_ref().unwrap().clone()) + .unwrap_or_default(); + + if let Some(attachments) = &item.attachments { + attachments + .iter() + .filter(|attachment| attachment.file_name.eq(&key_filename)) + .for_each(|attachment| register_key(&item, &attachment.id, &key_passphrase, &session_token).unwrap()); } } } @@ -138,20 +146,23 @@ fn check_session_token(args: &util::Cli) -> Result { Ok(session_token) } -fn register_key(item_id: &str, attachment_id: &str, passphrase: &str, session_token: &str) -> Result<()> { +/// Registers a key with `ssh-agent`. +/// First retrieves the attachment from `bitwarden-cli` and then passes it to `ssh-agent`. +fn register_key(item: &BitwardenItem, attachment_id: &str, passphrase: &str, session_token: &str) -> Result<()> { + debug_println!("Item `{}` id({}) meets all requirements. Adding to `{}`", item.name.cyan(), item.id.to_string().cyan(), "ssh-agent".cyan()); let bw_command = Command::new("bw") .arg("get") .arg("attachment") .arg(attachment_id) .arg("--itemid") - .arg(item_id) + .arg(&item.id) .arg("--raw") .arg("--session") .arg(session_token) .stdout(Stdio::piped()) .spawn() .with_context(|| - format!("Error running command:\n `bw get attachment {} --itemid {} --raw --session **REDACTED**`", attachment_id, item_id) + format!("Error running command:\n `bw get attachment {} --itemid {} --raw --session **REDACTED**`", attachment_id, item.id) )?; let app_path = env::current_exe()?; -- libgit2 1.7.2