283 lines
8.9 KiB
Rust
283 lines
8.9 KiB
Rust
use crate::skey::SKey;
|
|
use crate::structs::Mode;
|
|
use crate::utils::date::Date;
|
|
use std::{cell::RefCell, rc::Rc};
|
|
|
|
pub type Name = String;
|
|
pub type Prefix = Option<String>;
|
|
pub type Comment = Option<String>;
|
|
pub type PasswordRef = Rc<RefCell<Password>>;
|
|
pub type Parent = Option<PasswordRef>;
|
|
pub type Length = Option<u32>;
|
|
pub type Seq = u32;
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
pub struct Password {
|
|
pub parent: Parent,
|
|
pub prefix: Prefix,
|
|
pub name: Name,
|
|
pub length: Length,
|
|
pub mode: Mode,
|
|
pub seq: Seq,
|
|
pub date: Date,
|
|
pub comment: Comment,
|
|
}
|
|
|
|
impl Password {
|
|
pub fn new(
|
|
prefix: Prefix,
|
|
name: Name,
|
|
length: Length,
|
|
mode: Mode,
|
|
seq: Seq,
|
|
date: Date,
|
|
comment: Comment,
|
|
) -> Password {
|
|
Password {
|
|
prefix,
|
|
name: name,
|
|
length,
|
|
mode,
|
|
date,
|
|
comment,
|
|
parent: None,
|
|
seq,
|
|
}
|
|
}
|
|
|
|
pub fn from_password(password: &Password) -> PasswordRef {
|
|
Rc::new(RefCell::new(Self {
|
|
parent: password.parent.clone(),
|
|
prefix: password.prefix.clone(),
|
|
name: password.name.clone(),
|
|
length: password.length.clone(),
|
|
mode: password.mode.clone(),
|
|
seq: password.seq,
|
|
date: password.date.clone(),
|
|
comment: password.comment.clone(),
|
|
}))
|
|
}
|
|
|
|
pub fn encode(&self, secret: &str) -> String {
|
|
let skey = SKey::new(&self.name, self.seq, secret);
|
|
let (sep, len) = match (&self.length, &self.mode) {
|
|
(Some(n), Mode::NoSpace | Mode::NoSpaceUpcase) => ("", n),
|
|
(Some(n), Mode::Base64 | Mode::Base64Upcase | Mode::Hex | Mode::HexUpcase) => ("", n),
|
|
(Some(n), _) => ("", n),
|
|
(None, Mode::NoSpace | Mode::NoSpaceUpcase) => ("-", &0_u32),
|
|
(None, Mode::Base64 | Mode::Base64Upcase | Mode::Hex | Mode::HexUpcase | Mode::NoSpaceCamel) => {
|
|
("", &0_u32)
|
|
}
|
|
(None, _) => (" ", &0_u32),
|
|
};
|
|
let result = match self.mode {
|
|
Mode::Regular => skey.to_words().join(sep),
|
|
Mode::RegularUpcase => skey.to_words().join(sep).to_uppercase(),
|
|
Mode::NoSpace => skey.to_words().join(sep),
|
|
Mode::NoSpaceUpcase => skey.to_words().join(sep).to_uppercase(),
|
|
Mode::NoSpaceCamel => camel_case(skey.to_words()),
|
|
Mode::Hex => skey.to_hex(),
|
|
Mode::HexUpcase => skey.to_hex().to_uppercase(),
|
|
Mode::Base64 => skey.to_b64(),
|
|
Mode::Base64Upcase => skey.to_b64().to_uppercase(),
|
|
Mode::Decimal => skey.to_dec().map(|v| v.to_string()).join(sep),
|
|
};
|
|
let result = match &self.prefix {
|
|
Some(p) => (p.to_owned() + sep + &result).to_string(),
|
|
None => result,
|
|
};
|
|
if len > &0_u32 {
|
|
result.chars().take(*len as usize).collect()
|
|
} else {
|
|
result
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::string::ToString for Password {
|
|
fn to_string(&self) -> String {
|
|
let prefix = match self.prefix.as_ref() {
|
|
Some(s) => format!("{} ", s),
|
|
None => "".to_string(),
|
|
};
|
|
let length = match self.length {
|
|
Some(l) => format!("{}", l),
|
|
None => "".to_string(),
|
|
};
|
|
let comment = match self.comment.as_ref() {
|
|
Some(s) => format!(" {}", s),
|
|
None => "".to_string(),
|
|
};
|
|
let parent = match &self.parent {
|
|
Some(s) => format!(" ^{}", s.borrow().name),
|
|
None => "".to_string(),
|
|
};
|
|
format!("{:>6}{} {}{} {} {}{}{}", prefix, self.name, length, self.mode, self.seq, self.date, comment, parent)
|
|
}
|
|
}
|
|
|
|
fn camel_case(words: [&str; 6]) -> String {
|
|
let mut camel_case_string = String::new();
|
|
|
|
for word in words.iter() {
|
|
let mut chars = word.chars();
|
|
camel_case_string.push(chars.next().unwrap().to_uppercase().next().unwrap());
|
|
camel_case_string.extend(chars);
|
|
}
|
|
|
|
camel_case_string
|
|
}
|
|
|
|
pub fn fix_password_recursion(entry: Rc<RefCell<Password>>) {
|
|
let mut t1 = entry.clone();
|
|
let mut t2 = entry;
|
|
let mut t3: Option<Rc<RefCell<Password>>> = None;
|
|
loop {
|
|
t2 = match &t2.clone().borrow().parent {
|
|
Some(o) => o.clone(),
|
|
None => break,
|
|
};
|
|
if std::ptr::eq(&*t1.borrow(), &*t2.borrow()) {
|
|
t3 = Some(t2.clone());
|
|
break;
|
|
}
|
|
t1 = match &t1.clone().borrow().parent {
|
|
Some(o) => o.clone(),
|
|
None => break,
|
|
};
|
|
t2 = match &t2.clone().borrow().parent {
|
|
Some(o) => o.clone(),
|
|
None => break,
|
|
};
|
|
if std::ptr::eq(&*t1.borrow(), &*t2.borrow()) {
|
|
t3 = Some(t2.clone());
|
|
break;
|
|
}
|
|
}
|
|
match t3 {
|
|
Some(o) => o.borrow_mut().parent = None,
|
|
None => (),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn exec_recursion_test() {
|
|
let p1 = Rc::new(RefCell::new(Password::new(
|
|
None,
|
|
"p1".to_string(),
|
|
None,
|
|
Mode::Regular,
|
|
99,
|
|
Date::new(2022, 12, 3),
|
|
None,
|
|
)));
|
|
|
|
p1.borrow_mut().parent = Some(p1.clone());
|
|
fix_password_recursion(p1.clone());
|
|
assert_eq!(p1.borrow().parent, None);
|
|
|
|
let p2 = Rc::new(RefCell::new(Password::new(
|
|
None,
|
|
"p2".to_string(),
|
|
None,
|
|
Mode::Regular,
|
|
99,
|
|
Date::new(2022, 12, 3),
|
|
None,
|
|
)));
|
|
p2.borrow_mut().parent = Some(p1.clone());
|
|
let p3 = Rc::new(RefCell::new(Password::new(
|
|
None,
|
|
"p3".to_string(),
|
|
None,
|
|
Mode::Regular,
|
|
99,
|
|
Date::new(2022, 12, 3),
|
|
None,
|
|
)));
|
|
p3.borrow_mut().parent = Some(p2.clone());
|
|
let p4 = Rc::new(RefCell::new(Password::new(
|
|
None,
|
|
"p4".to_string(),
|
|
None,
|
|
Mode::Regular,
|
|
99,
|
|
Date::new(2022, 12, 3),
|
|
None,
|
|
)));
|
|
p4.borrow_mut().parent = Some(p3.clone());
|
|
let p5 = Rc::new(RefCell::new(Password::new(
|
|
None,
|
|
"p5".to_string(),
|
|
None,
|
|
Mode::Regular,
|
|
99,
|
|
Date::new(2022, 12, 3),
|
|
None,
|
|
)));
|
|
p5.borrow_mut().parent = Some(p4.clone());
|
|
|
|
p1.borrow_mut().parent = Some(p3.clone());
|
|
fix_password_recursion(p5.clone());
|
|
assert_eq!(p3.borrow().parent, None);
|
|
}
|
|
|
|
#[test]
|
|
fn exec_encode_test() {
|
|
let sec = "my secret";
|
|
let dat = Date::new(2022, 12, 3);
|
|
|
|
let mut pwd = Password::new(None, "test1".to_string(), None, Mode::Regular, 99, dat, None);
|
|
assert_eq!(pwd.encode(sec), "ross beau week held yoga anti");
|
|
pwd.mode = Mode::NoSpaceCamel;
|
|
assert_eq!(pwd.encode(sec), "RossBeauWeekHeldYogaAnti");
|
|
pwd.mode = Mode::Decimal;
|
|
assert_eq!(pwd.encode(sec), "1684 680 1995 1203 2046 619");
|
|
pwd.mode = Mode::RegularUpcase;
|
|
assert_eq!(pwd.encode(sec), "ROSS BEAU WEEK HELD YOGA ANTI");
|
|
pwd.mode = Mode::Regular;
|
|
pwd.prefix = Some("#Q3a".to_string());
|
|
assert_eq!(pwd.encode(sec), "#Q3a ross beau week held yoga anti");
|
|
pwd.mode = Mode::NoSpaceCamel;
|
|
assert_eq!(pwd.encode(sec), "#Q3aRossBeauWeekHeldYogaAnti");
|
|
pwd.mode = Mode::NoSpace;
|
|
assert_eq!(pwd.encode(sec), "#Q3a-ross-beau-week-held-yoga-anti");
|
|
pwd.mode = Mode::Base64;
|
|
assert_eq!(pwd.encode(sec), "#Q3a0oqj5cs//Jo");
|
|
pwd.mode = Mode::Base64Upcase;
|
|
assert_eq!(pwd.encode(sec), "#Q3a0OQJ5CS//JO");
|
|
pwd.mode = Mode::Hex;
|
|
assert_eq!(pwd.encode(sec), "#Q3ae5a38ad29afc3fcb");
|
|
pwd.mode = Mode::HexUpcase;
|
|
assert_eq!(pwd.encode(sec), "#Q3aE5A38AD29AFC3FCB");
|
|
pwd.mode = Mode::Decimal;
|
|
assert_eq!(pwd.encode(sec), "#Q3a 1684 680 1995 1203 2046 619");
|
|
|
|
let mut pwd = Password::new(None, "test1".to_string(), Some(6), Mode::Regular, 99, dat, None);
|
|
assert_eq!(pwd.encode(sec), "rossbe");
|
|
pwd.mode = Mode::NoSpaceCamel;
|
|
assert_eq!(pwd.encode(sec), "RossBe");
|
|
pwd.mode = Mode::Decimal;
|
|
assert_eq!(pwd.encode(sec), "168468");
|
|
pwd.mode = Mode::Regular;
|
|
pwd.prefix = Some("#Q3a".to_string());
|
|
assert_eq!(pwd.encode(sec), "#Q3aro");
|
|
pwd.mode = Mode::NoSpace;
|
|
assert_eq!(pwd.encode(sec), "#Q3aro");
|
|
pwd.mode = Mode::Base64;
|
|
assert_eq!(pwd.encode(sec), "#Q3a0o");
|
|
pwd.mode = Mode::Hex;
|
|
assert_eq!(pwd.encode(sec), "#Q3ae5");
|
|
pwd.mode = Mode::Decimal;
|
|
assert_eq!(pwd.encode(sec), "#Q3a16");
|
|
pwd.length = Some(10);
|
|
assert_eq!(pwd.encode(sec), "#Q3a168468");
|
|
pwd.mode = Mode::NoSpaceCamel;
|
|
assert_eq!(pwd.encode(sec), "#Q3aRossBe");
|
|
}
|
|
}
|