diff --git a/Cargo.lock b/Cargo.lock index ad9c575..fe65e86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.5.0" @@ -245,9 +251,9 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.26.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" dependencies = [ "bitflags 1.3.2", "crossterm_winapi", @@ -357,6 +363,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -479,6 +491,24 @@ dependencies = [ "slab", ] +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -652,6 +682,23 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inquire" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe95f33091b9b7b517a5849bce4dce1b550b430fc20d58059fcaa319ed895d8b" +dependencies = [ + "bitflags 2.4.1", + "crossterm 0.25.0", + "dyn-clone", + "fuzzy-matcher", + "fxhash", + "newline-converter", + "once_cell", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -737,12 +784,12 @@ dependencies = [ "clap", "comfy-table", "directories", + "inquire", "log", "passwords", "pretty_env_logger", "regex", "reqwest", - "scanpw", "serde", "serde_json", "serde_with_macros", @@ -758,15 +805,6 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.17" @@ -813,16 +851,12 @@ dependencies = [ ] [[package]] -name = "nix" -version = "0.26.4" +name = "newline-converter" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "47b6b097ecb1cbfed438542d16e84fd7ad9b0c76c8a65b7f9039212a3d14dc7f" dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset", - "pin-utils", + "unicode-segmentation", ] [[package]] @@ -1175,18 +1209,6 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" -[[package]] -name = "scanpw" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6196197a27d908a0b1cb44606d5a4f358c73d04dd1e9ba0ddc034841fa043a03" -dependencies = [ - "crossterm 0.26.1", - "nix", - "thiserror", - "windows", -] - [[package]] name = "schannel" version = "0.1.23" @@ -1463,6 +1485,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1576,6 +1608,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.11" @@ -1734,15 +1772,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 9dd24c7..e61fb16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ log = "0.4.20" passwords = { version = "3.1.16", features = ["common-password"] } pretty_env_logger = "0.5.0" regex = "1.10.2" -scanpw = "1.0.0" serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" serde_with_macros = "3.4.0" @@ -29,6 +28,7 @@ soft-aes = "0.1.0" thiserror = "1.0.51" url = { version = "2.5.0", features = ["serde"] } reqwest = {version = "0.11.23", optional = true} +inquire = "0.7.4" [features] default = ["update-notify"] diff --git a/src/cli/mod.rs b/src/cli/mod.rs index d76a89d..65298e2 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -18,10 +18,7 @@ use std::path::PathBuf; use clap::Parser; -use crate::{ - vault::{self, Vaults}, - LprsError, LprsResult, RunCommand, -}; +use crate::{utils, vault::Vaults, LprsResult, RunCommand}; pub mod add_command; pub mod clean_command; @@ -65,35 +62,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..c78e087 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::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> {