From 85ac2a04200763baf0ce5858f24b7efa24c7b854 Mon Sep 17 00:00:00 2001 From: Oleksandr Kozachuk Date: Tue, 13 Dec 2022 22:39:41 +0100 Subject: [PATCH] Added enc and pass commands, change names of commands back to full. --- Cargo.toml | 1 + src/lk.rs | 2 ++ src/parser.rs | 28 +++++++++------ src/repl.rs | 97 +++++++++++++++++++++++++++++++++++++++++--------- src/structs.rs | 20 +++++++++++ 5 files changed, 122 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c14880..cd02c0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ anyerror = "0.1.7" home = "0.5.4" sha1 = "0.10.5" base64 = "0.20.0" +rpassword = "7.2.0" diff --git a/src/lk.rs b/src/lk.rs index 0f43e6c..73f266d 100644 --- a/src/lk.rs +++ b/src/lk.rs @@ -6,6 +6,7 @@ use std::collections::HashMap; pub struct LK { pub db: HashMap, pub ls: HashMap, + pub secrets: HashMap, } impl LK { @@ -13,6 +14,7 @@ impl LK { Self { db: HashMap::new(), ls: HashMap::new(), + secrets: HashMap::new(), } } diff --git a/src/parser.rs b/src/parser.rs index f5df3e3..4181544 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -16,6 +16,8 @@ peg::parser! { / ls_cmd() / mv_cmd() / rm_cmd() + / enc_cmd() + / pass_cmd() / comment_cmd() ) { c } @@ -32,9 +34,13 @@ peg::parser! { { Password::new(Some(pr), pn, pl, pm, 99, pd, pc) } rule sname() -> Password = &(word() _ num()? mode() _ date()) pn:word() _ pl:num()? pm:mode() _ pd:date() pc:comment()? { Password::new(None, pn, pl, pm, 99, pd, pc) } - rule ssname() -> Password = &(word()) pn:word() + rule nname() -> Password = &(word() _ num()? mode()) pn:word() _ pl:num()? pm:mode() + { Password::new(None, pn, pl, pm, 99, Local::now().naive_local().date(), None) } + rule qname() -> Password = &(word()) pn:word() { Password::new(None, pn, None, Mode::NoSpaceCamel, 99, Local::now().naive_local().date(), None) } - pub rule name() -> Password = name:(jname() / pname() / mname() / sname() / ssname())? {? match name { Some(n) => Ok(n), None => Err("failed to parse password description") } } + pub rule name() -> Password = name:(jname() / pname() / mname() / sname() / nname() / qname())? {? + match name { Some(n) => Ok(n), None => Err("failed to parse password description") } + } rule date() -> NaiveDate = y:$("-"? ['0'..='9']*<1,4>) "-" m:$(['0'..='9']*<1,2>) "-" d:$(['0'..='9']*<1,2>) {? let year: i32 = match y.parse() { Ok(n) => n, Err(_) => return Err("year") }; @@ -51,7 +57,7 @@ peg::parser! { _ => Err("unknown mode"), } } - rule rmode() -> Mode = m:$("R" / "r" / "U" / "u" / "N" / "n" / "C" / "H" / "h" / "B" / "b" / "D" / "d") {? + rule rmode() -> Mode = m:$("R" / "r" / "U" / "u" / "N" / "n" / "C" / "c" / "H" / "h" / "B" / "b" / "D" / "d") {? match m.to_uppercase().as_str() { "R" => Ok(Mode::Regular), "N" => Ok(Mode::NoSpace), @@ -64,14 +70,16 @@ peg::parser! { } } rule mode() -> Mode = m:(umode() / rmode()) { m } - rule help_cmd() -> Command<'input> = "h" { Command::Help } - rule quit_cmd() -> Command<'input> = "q" { Command::Quit } - rule ls_cmd() -> Command<'input> = "l" { Command::Ls } - rule add_cmd() -> Command<'input> = "a" _ name:name() { Command::Add(Rc::new(RefCell::new(name))) } + rule help_cmd() -> Command<'input> = "help" { Command::Help } + rule quit_cmd() -> Command<'input> = "quit" { Command::Quit } + rule ls_cmd() -> Command<'input> = "ls" { Command::Ls } + rule add_cmd() -> Command<'input> = "add" _ name:name() { Command::Add(Rc::new(RefCell::new(name))) } rule error_cmd() -> Command<'input> = "error" _ e:$(([' '..='~'])+) { Command::Error(LKErr::Error(e)) } - rule mv_cmd() -> Command<'input> = "m" _ name:word() _ folder:word() { Command::Mv(name, folder) } - rule rm_cmd() -> Command<'input> = "r" _ name:word() { Command::Rm(name) } - rule comment_cmd() -> Command<'input> = "c" _ name:word() c:comment()? { Command::Comment(name, c) } + rule mv_cmd() -> Command<'input> = "mv" _ name:word() _ folder:word() { Command::Mv(name, folder) } + rule pass_cmd() -> Command<'input> = "pass" _ name:word() { Command::Pass(name) } + rule enc_cmd() -> Command<'input> = "enc" _ name:word() { Command::Enc(name) } + rule rm_cmd() -> Command<'input> = "rm" _ name:word() { Command::Rm(name) } + rule comment_cmd() -> Command<'input> = "comment" _ name:word() c:comment()? { Command::Comment(name, c) } } } diff --git a/src/repl.rs b/src/repl.rs index 23b7fed..9a5c74b 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,4 +1,5 @@ use home::home_dir; +use rpassword::prompt_password; use rustyline::Editor; use std::{cell::RefCell, rc::Rc}; @@ -84,7 +85,60 @@ impl<'a> LKEval<'a> { } } - fn cmd_ls(&mut self, out: &mut Vec) { + fn read_master(&self, pwd: PasswordRef) -> String { + let parent = match &pwd.borrow().parent { + Some(p) => p.borrow().name.clone(), + None => Rc::new("/".to_string()), + }; + let secret = match self.state.borrow().secrets.get(&parent) { + Some(p) => Some(p.clone()), + None => None, + }; + match (pwd.borrow().parent.clone(), secret) { + (_, Some(s)) => s.to_string(), + (None, None) => { + let password = prompt_password("Master: ").unwrap(); + self.state.borrow_mut().secrets.insert(Rc::new("/".to_string()), password.clone()); + password + } + (Some(pn), None) => { + let password = prompt_password(format!("Password for {}: ", pn.borrow().name)).unwrap(); + if password.len() > 0 { + self.state.borrow_mut().secrets.insert(pn.borrow().name.clone(), password.clone()); + password + } else { + let master = self.read_master(pn.clone()); + let password = pn.borrow().encode(master.as_str()); + self.state.borrow_mut().secrets.insert(pn.borrow().name.clone(), password.clone()); + password + } + } + } + } + + fn cmd_enc(&self, out: &mut Vec, name: &String) { + let root_folder = Rc::new("/".to_string()); + if name == "/" && self.state.borrow().secrets.contains_key(&root_folder) { + out.push(self.state.borrow().secrets.get(&root_folder).unwrap().to_string()); + return; + } + let pwd = match self.get_password(name) { + Some(p) => p.clone(), + None => { + out.push(format!("error: name {} not found", name)); + return; + } + }; + let name = pwd.borrow().name.clone(); + if self.state.borrow().secrets.contains_key(&name) { + out.push(self.state.borrow().secrets.get(&name).unwrap().to_string()); + return; + } + let sec = self.read_master(pwd.clone()); + out.push(pwd.borrow().encode(sec.as_str())); + } + + fn cmd_ls(&self, out: &mut Vec) { let mut tmp: Vec = vec![]; for (_, name) in &self.state.borrow().db { tmp.push(name.clone()); @@ -100,7 +154,7 @@ impl<'a> LKEval<'a> { } } - pub fn eval(&mut self) -> LKPrint { + pub fn eval(&self) -> LKPrint { let mut out: Vec = vec![]; let mut quit: bool = false; @@ -134,27 +188,38 @@ impl<'a> LKEval<'a> { } None => out.push("error: password not found".to_string()), }, + Command::Enc(name) => self.cmd_enc(&mut out, name), + Command::Pass(name) => match self.get_password(name) { + Some(p) => { + self.state.borrow_mut().secrets.insert(p.borrow().name.clone(), prompt_password(format!("Password for {}: ", p.borrow().name)).unwrap()); + } + None => { + if name == "/" { + self.state.borrow_mut().secrets.insert(Rc::new("/".to_string()), prompt_password("Master: ").unwrap()); + } else { + out.push(format!("error: password with name {} not found", name)); + } + } + }, Command::Help => { out.push("HELP".to_string()); } - Command::Mv(name, folder) => { - for (_, tmp) in &self.state.borrow().db { - if *tmp.borrow().name == *name { - if folder == "/" { - tmp.borrow_mut().parent = None - } else { - for (_, fld) in &self.state.borrow().db { - if *fld.borrow().name == *folder { - tmp.borrow_mut().parent = Some(fld.clone()); - fix_password_recursion(tmp.clone()); - break; - } + Command::Mv(name, folder) => match self.get_password(name) { + Some(pwd) => { + if folder == "/" { + pwd.borrow_mut().parent = None + } else { + match self.get_password(folder) { + Some(fld) => { + pwd.borrow_mut().parent = Some(fld.clone()); + fix_password_recursion(pwd.clone()); } + None => out.push(format!("error: folder {} not found", folder)), } - break; } } - } + None => out.push(format!("error: password with name {} not found", name)), + }, Command::Error(err) => match err { LKErr::ParseError(e) => out.push(e.to_string()), LKErr::ReadError(e) => out.push(e.to_string()), diff --git a/src/structs.rs b/src/structs.rs index da6f0bd..265f334 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -17,6 +17,8 @@ pub enum Command<'a> { Ls, Mv(Name, Name), Rm(Name), + Enc(Name), + Pass(Name), Comment(Name, Comment), Error(LKErr<'a>), Help, @@ -108,3 +110,21 @@ impl fmt::Display for Radix { Ok(()) } } + +/* +impl fmt::Display for Radix { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let sign = if self.x < 0 { '-' } else { ' ' }; + let mut x: u32 = self.x.abs() as u32; + write!(f, "{}{}", sign, (0..).map(|_| { + let m = x % self.radix; + x /= self.radix; + (x, std::char::from_digit(m, self.radix).unwrap()) + }) + .take_while(|a| a.0 > 0).map(|b| b.1).collect::() + .chars().rev().collect::() + )?; + Ok(()) + } +} +*/