diff --git a/Cargo.toml b/Cargo.toml index f996055..5624025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" aes-gcm = "0.10.3" clap = { version = "4.5.16", features = ["derive"] } hex = "0.4.3" -once_cell = "1.19.0" rpassword = "7.3.1" tempfile = "3.12.0" diff --git a/README.md b/README.md index b1e1efb..9a12570 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ -# mps +# MPS + +A small tool for storing passwords locally with git sync + +## Installation + +1. Create empty repository for storing your passwords on gitlab or somewhere you prefer +2. Clone to your home folder ~/.mps/storage +3. Set variable $MPS_HOME="~/.mps/" +4. Add to your $PATH: where you installed your mps +5. Run ./mps init + -A small tool for storing passwords locally with git sync \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6debaa6..0c2e001 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,20 +78,41 @@ impl MPS { MPS { storage: None } } - fn init() -> io::Result<()> { - if Storage::is_inited() { - return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Reinitialization attempted")); - } + + fn prompt_new_password() -> io::Result { print!("Enter passphrase for storage: "); io::stdout().flush()?; let ps = rpassword::read_password()?; + if ps.len() < 8 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "Passphrase must be longet that 8 letters")); + } + if ps.len() > 32 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "Passphrase must be shorted that 32 letters")); + } print!("Reenter passphrase: "); io::stdout().flush()?; let ps2 = rpassword::read_password()?; if ps != ps2 { return Err(io::Error::new(io::ErrorKind::InvalidInput, "Passphrases must be equal")); } - Storage::init(ps)?; + Ok(ps) + } + + fn init() -> io::Result<()> { + if Storage::is_inited()? { + return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Reinitialization attempted")); + } + let mut passphrase = String::from(""); + loop { + match MPS::prompt_new_password() { + Ok(ps) => { + passphrase = ps; + break; + }, + Err(_) => continue + } + } + Storage::init(passphrase)?; Ok(()) } @@ -200,10 +221,10 @@ fn run_command() -> io::Result<()> { mps.edit(id)? } None => { - if !Storage::is_inited() { + if !Storage::is_inited()? { match get_prompt("Do you want to init your storage?")? { PROMPT::YES => MPS::init()?, - PROMPT::NO => Storage::print_init_hint(), + PROMPT::NO => Storage::print_init_hint()?, } } else { let mut mps = MPS::new(); diff --git a/src/storage.rs b/src/storage.rs index 2a0101d..34f6e28 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -2,11 +2,10 @@ use crate::encoder; use encoder::Encoder; -use once_cell::sync::Lazy; - use std::collections::HashSet; use std::hash::{Hash, Hasher}; +use std::env; use std::fs; use std::path::Path; use std::io::{self, Write, BufRead}; @@ -14,10 +13,10 @@ use std::fmt; use std::cmp::{PartialEq, Ordering}; -static STORAGE_FOLDER: Lazy = Lazy::new(|| "storage".to_string() ); -static STORAGE_PATH: Lazy = Lazy::new(|| { - format!("{}/db.mps", &*STORAGE_FOLDER) -}); +static ENV_MPS_HOME: &str = "MPS_HOME"; + +static PATH_STORAGE: &str = "storage"; // should be under git +static PATH_DB: &str = "db.mps"; #[derive(Clone)] @@ -82,15 +81,30 @@ impl Storage { encoder: Encoder::from(&passphrase) } } + + fn get_storage_path() -> io::Result { + let mps_home = env::var(ENV_MPS_HOME).map_err(|err| { + io::Error::new(io::ErrorKind::NotFound, format!("{} error: {}", ENV_MPS_HOME, err)) + })?; + let result = format!("{}/{}", mps_home, PATH_STORAGE); + Ok(result) + } + + fn get_storage_db_path() -> io::Result { + let st = Storage::get_storage_path()?; + let result = format!("{}/{}", st, PATH_DB); + Ok(result) + } + pub fn from_db(passphrase: String) -> io::Result { - if !Storage::is_inited() { + if !Storage::is_inited()? { return Err(io::Error::new( io::ErrorKind::Other, "Storage is not initialized" )); } let encoder = Encoder::from(&passphrase); - let file = fs::File::open(&*STORAGE_PATH)?; + let file = fs::File::open(Storage::get_storage_db_path()?)?; let reader = io::BufReader::new(file); let mut items = HashSet::::new(); let mut id: Option = None; @@ -129,31 +143,36 @@ impl Storage { } pub fn init(passphrase: String) -> io::Result<()> { - fs::create_dir(&*STORAGE_FOLDER)?; - println!("Storage folder created"); - fs::File::create(&*STORAGE_PATH)?; - let st = Storage::new(passphrase); - st.dump()?; - println!("Storage db created."); - println!("Initialization complete."); - println!(""); - println!("Now it's required to add folder `{}` under git manually.", &*STORAGE_FOLDER); - println!("Don't worry it's going to be encrypted."); + // TODO: revrite all logic + // Check if the folder exists and is a directory + //if path.exists() && path.is_dir() { + // Err(io::Error::new(io::ErrorKind::NotFound, "Folder does not exist")) + //} + //fs::File::create(&*STORAGE_PATH)?; + //let st = Storage::new(passphrase); + //st.dump()?; + //println!("Storage db created."); + //println!("Initialization complete."); + //println!(""); + //println!("Now it's required to add folder `{}` under git manually.", &*STORAGE_FOLDER); + //println!("Don't worry it's going to be encrypted."); Ok(()) } - pub fn print_init_hint() { + pub fn print_init_hint() -> io::Result<()> { println!("mps can work only when storage inited."); println!("Hint: you can restore your storage if you have it already:"); - println!(" git clone {}", &*STORAGE_FOLDER); - println!("to init manually your storage and config") + println!(" git clone {}", Storage::get_storage_path()?); + println!("to init manually your storage and config"); + Ok(()) } - pub fn is_inited() -> bool { + pub fn is_inited() -> io::Result { // TODO: check db // TODO check git - let path = Path::new(&*STORAGE_FOLDER); - return path.exists(); + //let path = Path::new(&*STORAGE_FOLDER); + //return path.exists(); + Ok(true) } @@ -189,7 +208,7 @@ impl Storage { let mut file = fs::OpenOptions::new() .write(true) .append(false) - .open(&*STORAGE_PATH)?; + .open(Storage::get_storage_db_path()?)?; writeln!(file, "{}", self.encoder.get_encoded_test_passphrase()?)?; for item in self.items.iter() {