diff --git a/src/cli/mod.rs b/src/cli/mod.rs index d76a89d..0746b32 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -17,10 +17,12 @@ use std::path::PathBuf; use clap::Parser; +use inquire::validator::Validation; use crate::{ + utils, vault::{self, Vaults}, - LprsError, LprsResult, RunCommand, + LprsResult, RunCommand, }; pub mod add_command; @@ -65,35 +67,21 @@ impl Cli { crate::utils::vaults_file()? }; log::debug!("Getting the vaults file: {}", vaults_file.to_string_lossy()); + let vault_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) { + // Returns empty vault manager for those commands don't need it Vaults { vaults_file, ..Default::default() } } else { - let master_password = scanpw::scanpw!("Master Password: "); - - if vault::is_new_vaults_file(&vaults_file)? { - let analyzed = passwords::analyzer::analyze(&master_password); - if analyzed.length() < 15 { - return Err(LprsError::WeakPassword( - "The master password length must be beggier then 15".to_owned(), - )); - } else if passwords::scorer::score(&analyzed) < 80.0 { - return Err(LprsError::WeakPassword( - "Your master password is not stronge enough".to_owned(), - )); - } - } - - let master_password = sha256::digest(master_password); + let master_password = utils::master_password_prompt(&vaults_file)?; Vaults::try_reload( vaults_file, master_password.into_bytes().into_iter().take(32).collect(), )? }; - self.command.run(vault_manager)?; - Ok(()) + self.command.run(vault_manager) } } diff --git a/src/errors.rs b/src/errors.rs index 52ac749..c6a2985 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -39,6 +39,8 @@ pub enum Error { #[error("{0}")] Other(String), + #[error("CLI error: {0}")] + Inquire(#[from] inquire::InquireError), #[error("Invalid Regex: {0}")] InvalidRegex(#[from] regex::Error), #[error("UTF8 Error: {0}")] diff --git a/src/utils.rs b/src/utils.rs index 4e21c24..1670e4a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,9 +14,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::{fs, path::PathBuf}; +use std::{ + fs, + path::{Path, PathBuf}, +}; -use crate::{LprsError, LprsResult}; +use inquire::{ui::RenderConfig, validator::Validation}; + +use crate::{vault, LprsError, LprsResult}; /// Returns the local project dir joined with the given file name pub fn local_project_file(filename: &str) -> LprsResult { @@ -40,6 +45,46 @@ pub fn vaults_file() -> LprsResult { Ok(vaults_file) } +/// Validate the password +/// +/// ## To pass +/// - The length must be higher than 14 (>=15) +/// - Its score must be greater than 80.0 +pub fn password_validator(password: &str) -> Result { + let analyzed = passwords::analyzer::analyze(password); + if analyzed.length() < 15 { + return Ok(Validation::Invalid( + "The master password length must be beggier then 15".into(), + )); + } else if passwords::scorer::score(&analyzed) < 80.0 { + return Ok(Validation::Invalid( + "Your master password is not stronge enough".into(), + )); + } + Ok(Validation::Valid) +} + +/// Ask the user for the master password, then returns it +pub fn master_password_prompt(vaults_file: &Path) -> LprsResult { + let is_new_vaults_file = vault::is_new_vaults_file(vaults_file)?; + + inquire::Password { + message: "Master Password:", + enable_confirmation: is_new_vaults_file, + validators: if is_new_vaults_file { + vec![Box::new(password_validator)] + } else { + vec![] + }, + ..inquire::Password::new("") + } + .with_formatter(&|p| "*".repeat(p.chars().count())) + .with_display_mode(inquire::PasswordDisplayMode::Masked) + .prompt() + .map(sha256::digest) + .map_err(Into::into) +} + /// Retuns the current lprs version from `crates.io` #[cfg(feature = "update-notify")] pub fn lprs_version() -> LprsResult> {