From 5f357b89cb6a49be1c5461fa4b6cd5aaec8e541f Mon Sep 17 00:00:00 2001 From: Awiteb Date: Thu, 25 Apr 2024 00:27:56 +0200 Subject: [PATCH] feat: Ability to enter password via stdin `add`&`edit` (#15) Reviewed-on: https://git.4rs.nl/awiteb/lprs/pulls/15 Co-authored-by: Awiteb Co-committed-by: Awiteb --- src/cli/add_command.rs | 24 +++++++++++++-- src/cli/clean_command.rs | 2 +- src/cli/edit_command.rs | 65 +++++++++++++++++++++++---------------- src/cli/export_command.rs | 2 +- src/cli/gen_command.rs | 2 +- src/cli/import_command.rs | 8 ++--- src/cli/list_command.rs | 2 +- src/cli/remove_command.rs | 2 +- src/macros.rs | 2 +- src/traits.rs | 2 +- src/vault/mod.rs | 17 +++++----- 11 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/cli/add_command.rs b/src/cli/add_command.rs index 25009c6..ce3c8df 100644 --- a/src/cli/add_command.rs +++ b/src/cli/add_command.rs @@ -26,18 +26,36 @@ use crate::{ pub struct Add { #[command(flatten)] vault_info: Vault, + /// The password, if there is no value for it you will prompt it + #[arg(short, long)] + password: Option>, } impl RunCommand for Add { - fn run(&self, mut vault_manager: Vaults) -> LprsResult<()> { + fn run(mut self, mut vault_manager: Vaults) -> LprsResult<()> { if self.vault_info.username.is_none() - && self.vault_info.password.is_none() + && self.password.is_none() && self.vault_info.service.is_none() && self.vault_info.note.is_none() { return Err(LprsError::Other("You can't add empty vault".to_owned())); } - vault_manager.add_vault(self.vault_info.clone()); + + match self.password { + Some(Some(password)) => self.vault_info.password = Some(password), + Some(None) => { + self.vault_info.password = Some( + inquire::Password::new("Vault password:") + .without_confirmation() + .with_formatter(&|p| "*".repeat(p.chars().count())) + .with_display_mode(inquire::PasswordDisplayMode::Masked) + .prompt()?, + ); + } + None => {} + }; + + vault_manager.add_vault(self.vault_info); vault_manager.try_export() } } diff --git a/src/cli/clean_command.rs b/src/cli/clean_command.rs index 2efeed3..11ae7ec 100644 --- a/src/cli/clean_command.rs +++ b/src/cli/clean_command.rs @@ -28,7 +28,7 @@ use crate::{ pub struct Clean {} impl RunCommand for Clean { - fn run(&self, vault_manager: Vaults) -> LprsResult<()> { + fn run(self, vault_manager: Vaults) -> LprsResult<()> { fs::write(vault_manager.vaults_file, "[]").map_err(LprsError::Io) } } diff --git a/src/cli/edit_command.rs b/src/cli/edit_command.rs index a7d393d..7c594f9 100644 --- a/src/cli/edit_command.rs +++ b/src/cli/edit_command.rs @@ -36,8 +36,8 @@ pub struct Edit { /// The new vault username username: Option, #[arg(short, long)] - /// The new password - password: Option, + /// The new password, if there is no value for it you will prompt it + password: Option>, #[arg(short, long)] /// The new vault service service: Option, @@ -47,35 +47,48 @@ pub struct Edit { } impl RunCommand for Edit { - fn run(&self, mut vault_manager: Vaults) -> LprsResult<()> { + fn run(self, mut vault_manager: Vaults) -> LprsResult<()> { let index = self.index.get() as usize; - if let Some(vault) = vault_manager.vaults.get_mut(index - 1) { - if self.name.is_none() - && self.username.is_none() - && self.password.is_none() - && self.service.is_none() - && self.note.is_none() - { - Err(LprsError::Other( - "You must edit one option at least".to_owned(), - )) - } else { - *vault = Vault::::new( - self.name.as_ref().unwrap_or(&vault.name), - self.username.as_ref().or(vault.username.as_ref()), - self.password.as_ref().or(vault.password.as_ref()), - self.service.as_ref().or(vault.service.as_ref()), - self.note.as_ref().or(vault.note.as_ref()), - ); - vault_manager.try_export() - } - } else { - Err(LprsError::InvalidVaultIndex(format!( + let Some(vault) = vault_manager.vaults.get_mut(index - 1) else { + return Err(LprsError::InvalidVaultIndex(format!( "The index `{}` is greater than the vaults count {}", self.index, vault_manager.vaults.len() - ))) + ))); + }; + + if self.name.is_none() + && self.username.is_none() + && self.password.is_none() + && self.service.is_none() + && self.note.is_none() + { + return Err(LprsError::Other( + "You must edit one option at least".to_owned(), + )); } + + // Get the password from stdin or from its value if provided + let password = match self.password { + Some(Some(password)) => Some(password), + Some(None) => Some( + inquire::Password::new("New vault password:") + .without_confirmation() + .with_formatter(&|p| "*".repeat(p.chars().count())) + .with_display_mode(inquire::PasswordDisplayMode::Masked) + .prompt()?, + ), + None => None, + }; + + *vault = Vault::::new( + self.name.as_ref().unwrap_or(&vault.name), + self.username.as_ref().or(vault.username.as_ref()), + password.as_ref().or(vault.password.as_ref()), + self.service.as_ref().or(vault.service.as_ref()), + self.note.as_ref().or(vault.note.as_ref()), + ); + vault_manager.try_export() } } diff --git a/src/cli/export_command.rs b/src/cli/export_command.rs index 1375ebb..34b7abf 100644 --- a/src/cli/export_command.rs +++ b/src/cli/export_command.rs @@ -34,7 +34,7 @@ pub struct Export { } impl RunCommand for Export { - fn run(&self, vault_manager: Vaults) -> LprsResult<()> { + fn run(self, vault_manager: Vaults) -> LprsResult<()> { if self .path .extension() diff --git a/src/cli/gen_command.rs b/src/cli/gen_command.rs index d5dacac..f35f92f 100644 --- a/src/cli/gen_command.rs +++ b/src/cli/gen_command.rs @@ -45,7 +45,7 @@ pub struct Gen { } impl RunCommand for Gen { - fn run(&self, _vault_manager: Vaults) -> LprsResult<()> { + fn run(self, _vault_manager: Vaults) -> LprsResult<()> { if self.uppercase || self.lowercase || self.numbers || self.symbols { println!( "{}", diff --git a/src/cli/import_command.rs b/src/cli/import_command.rs index cbc080a..f9de214 100644 --- a/src/cli/import_command.rs +++ b/src/cli/import_command.rs @@ -35,7 +35,7 @@ pub struct Import { } impl RunCommand for Import { - fn run(&self, mut vault_manager: Vaults) -> LprsResult<()> { + fn run(self, mut vault_manager: Vaults) -> LprsResult<()> { if self.path.exists() { if self .path @@ -44,10 +44,8 @@ impl RunCommand for Import { { let imported_passwords_len = match self.format { Format::Lprs => { - let vaults = Vaults::try_reload( - self.path.to_path_buf(), - vault_manager.master_password.to_vec(), - )?; + let vaults = + Vaults::try_reload(self.path, vault_manager.master_password.to_vec())?; let vaults_len = vaults.vaults.len(); vault_manager.vaults.extend(vaults.vaults); diff --git a/src/cli/list_command.rs b/src/cli/list_command.rs index c26483c..16cfc63 100644 --- a/src/cli/list_command.rs +++ b/src/cli/list_command.rs @@ -50,7 +50,7 @@ pub struct List { } impl RunCommand for List { - fn run(&self, vault_manager: Vaults) -> LprsResult<()> { + fn run(self, vault_manager: Vaults) -> LprsResult<()> { if vault_manager.vaults.is_empty() { return Err(LprsError::Other( "Looks like there is no passwords to list".to_owned(), diff --git a/src/cli/remove_command.rs b/src/cli/remove_command.rs index 29ac6b6..36e8ebb 100644 --- a/src/cli/remove_command.rs +++ b/src/cli/remove_command.rs @@ -35,7 +35,7 @@ pub struct Remove { } impl RunCommand for Remove { - fn run(&self, mut vault_manager: Vaults) -> LprsResult<()> { + fn run(self, mut vault_manager: Vaults) -> LprsResult<()> { let index = (self.index.get() - 1) as usize; if index > vault_manager.vaults.len() { if !self.force { diff --git a/src/macros.rs b/src/macros.rs index 454b8ed..a753c97 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -63,7 +63,7 @@ macro_rules! create_commands { #[automatically_derived] impl $crate::RunCommand for $enum_name{ - fn run(&self, vault_manager: $crate::vault::Vaults<$crate::vault::vault_state::Plain>) -> $crate::LprsResult<()> { + fn run(self, vault_manager: $crate::vault::Vaults<$crate::vault::vault_state::Plain>) -> $crate::LprsResult<()> { match self { $( Self::$varint(command) => command.run(vault_manager), diff --git a/src/traits.rs b/src/traits.rs index dd8e9c4..cb0882c 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -21,5 +21,5 @@ use crate::{ /// Trait to run the command pub trait RunCommand { - fn run(&self, vault_manager: Vaults) -> LprsResult<()>; + fn run(self, vault_manager: Vaults) -> LprsResult<()>; } diff --git a/src/vault/mod.rs b/src/vault/mod.rs index 5f47c53..81c05d3 100644 --- a/src/vault/mod.rs +++ b/src/vault/mod.rs @@ -60,7 +60,7 @@ where #[arg(short, long)] pub username: Option, /// The password - #[arg(short, long)] + #[arg(skip)] pub password: Option, /// The service name. e.g the website url #[arg(short, long)] @@ -187,11 +187,14 @@ impl Vaults { } } -impl ToString for Format { - fn to_string(&self) -> String { - self.to_possible_value() - .expect("There is no skiped values") - .get_name() - .to_owned() +impl std::fmt::Display for Format { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.to_possible_value() + .expect("There is no skiped values") + .get_name() + ) } }