feat: Ability to enter password via stdin add&edit (#15)
Some checks failed
Write changelog / write-changelog (push) Failing after 2s
Rust CI / Rust CI (push) Successful in 1m13s

Reviewed-on: #15
Co-authored-by: Awiteb <a@4rs.nl>
Co-committed-by: Awiteb <a@4rs.nl>
This commit is contained in:
Awiteb 2024-04-25 00:27:56 +02:00 committed by awiteb
parent a625f794dd
commit 5f357b89cb
11 changed files with 80 additions and 48 deletions

View file

@ -26,18 +26,36 @@ use crate::{
pub struct Add { pub struct Add {
#[command(flatten)] #[command(flatten)]
vault_info: Vault<Plain>, vault_info: Vault<Plain>,
/// The password, if there is no value for it you will prompt it
#[arg(short, long)]
password: Option<Option<String>>,
} }
impl RunCommand for Add { impl RunCommand for Add {
fn run(&self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(mut self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
if self.vault_info.username.is_none() 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.service.is_none()
&& self.vault_info.note.is_none() && self.vault_info.note.is_none()
{ {
return Err(LprsError::Other("You can't add empty vault".to_owned())); 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() vault_manager.try_export()
} }
} }

View file

@ -28,7 +28,7 @@ use crate::{
pub struct Clean {} pub struct Clean {}
impl RunCommand for Clean { impl RunCommand for Clean {
fn run(&self, vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
fs::write(vault_manager.vaults_file, "[]").map_err(LprsError::Io) fs::write(vault_manager.vaults_file, "[]").map_err(LprsError::Io)
} }
} }

View file

@ -36,8 +36,8 @@ pub struct Edit {
/// The new vault username /// The new vault username
username: Option<String>, username: Option<String>,
#[arg(short, long)] #[arg(short, long)]
/// The new password /// The new password, if there is no value for it you will prompt it
password: Option<String>, password: Option<Option<String>>,
#[arg(short, long)] #[arg(short, long)]
/// The new vault service /// The new vault service
service: Option<String>, service: Option<String>,
@ -47,35 +47,48 @@ pub struct Edit {
} }
impl RunCommand for Edit { impl RunCommand for Edit {
fn run(&self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
let index = self.index.get() as usize; let index = self.index.get() as usize;
if let Some(vault) = vault_manager.vaults.get_mut(index - 1) { let Some(vault) = vault_manager.vaults.get_mut(index - 1) else {
if self.name.is_none() return Err(LprsError::InvalidVaultIndex(format!(
&& 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::<Plain>::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!(
"The index `{}` is greater than the vaults count {}", "The index `{}` is greater than the vaults count {}",
self.index, self.index,
vault_manager.vaults.len() 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::<Plain>::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()
} }
} }

View file

@ -34,7 +34,7 @@ pub struct Export {
} }
impl RunCommand for Export { impl RunCommand for Export {
fn run(&self, vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
if self if self
.path .path
.extension() .extension()

View file

@ -45,7 +45,7 @@ pub struct Gen {
} }
impl RunCommand for Gen { impl RunCommand for Gen {
fn run(&self, _vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, _vault_manager: Vaults<Plain>) -> LprsResult<()> {
if self.uppercase || self.lowercase || self.numbers || self.symbols { if self.uppercase || self.lowercase || self.numbers || self.symbols {
println!( println!(
"{}", "{}",

View file

@ -35,7 +35,7 @@ pub struct Import {
} }
impl RunCommand for Import { impl RunCommand for Import {
fn run(&self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
if self.path.exists() { if self.path.exists() {
if self if self
.path .path
@ -44,10 +44,8 @@ impl RunCommand for Import {
{ {
let imported_passwords_len = match self.format { let imported_passwords_len = match self.format {
Format::Lprs => { Format::Lprs => {
let vaults = Vaults::try_reload( let vaults =
self.path.to_path_buf(), Vaults::try_reload(self.path, vault_manager.master_password.to_vec())?;
vault_manager.master_password.to_vec(),
)?;
let vaults_len = vaults.vaults.len(); let vaults_len = vaults.vaults.len();
vault_manager.vaults.extend(vaults.vaults); vault_manager.vaults.extend(vaults.vaults);

View file

@ -50,7 +50,7 @@ pub struct List {
} }
impl RunCommand for List { impl RunCommand for List {
fn run(&self, vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
if vault_manager.vaults.is_empty() { if vault_manager.vaults.is_empty() {
return Err(LprsError::Other( return Err(LprsError::Other(
"Looks like there is no passwords to list".to_owned(), "Looks like there is no passwords to list".to_owned(),

View file

@ -35,7 +35,7 @@ pub struct Remove {
} }
impl RunCommand for Remove { impl RunCommand for Remove {
fn run(&self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
let index = (self.index.get() - 1) as usize; let index = (self.index.get() - 1) as usize;
if index > vault_manager.vaults.len() { if index > vault_manager.vaults.len() {
if !self.force { if !self.force {

View file

@ -63,7 +63,7 @@ macro_rules! create_commands {
#[automatically_derived] #[automatically_derived]
impl $crate::RunCommand for $enum_name{ 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 { match self {
$( $(
Self::$varint(command) => command.run(vault_manager), Self::$varint(command) => command.run(vault_manager),

View file

@ -21,5 +21,5 @@ use crate::{
/// Trait to run the command /// Trait to run the command
pub trait RunCommand { pub trait RunCommand {
fn run(&self, vault_manager: Vaults<Plain>) -> LprsResult<()>; fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()>;
} }

View file

@ -60,7 +60,7 @@ where
#[arg(short, long)] #[arg(short, long)]
pub username: Option<String>, pub username: Option<String>,
/// The password /// The password
#[arg(short, long)] #[arg(skip)]
pub password: Option<String>, pub password: Option<String>,
/// The service name. e.g the website url /// The service name. e.g the website url
#[arg(short, long)] #[arg(short, long)]
@ -187,11 +187,14 @@ impl Vaults<Plain> {
} }
} }
impl ToString for Format { impl std::fmt::Display for Format {
fn to_string(&self) -> String { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value() write!(
.expect("There is no skiped values") f,
.get_name() "{}",
.to_owned() self.to_possible_value()
.expect("There is no skiped values")
.get_name()
)
} }
} }