use std::collections::HashMap;
use std::fs::File;
use std::io::{prelude::*, BufReader};
use log::*;
use serde_derive::Deserialize;
use sha2::{Sha256,Digest};
#[derive(Deserialize, Debug)]
#[serde(default)]
pub struct UserConfig {
users: HashMap<String, (Box<[u8]>, bool)>,
}
impl Default for UserConfig {
fn default() -> Self {
UserConfig {
users: HashMap::default(),
}
}
}
impl UserConfig {
pub fn new() -> UserConfig {
UserConfig { ..Default::default() }
}
pub fn len(&self) -> usize {
self.users.len()
}
pub fn init(&mut self, user_file: &String) {
debug!("loading...");
let file = File::open(user_file).expect(format!("file not found {}", user_file).as_str());
let buf = BufReader::new(file);
buf.lines()
.map(|line| line.unwrap())
.for_each(|line| {
if line.len() == 0 || line.chars().next().unwrap() == '#' {
return;
}
let mut parts = line.split(':');
if let (Some(name), Some(hex)) = (parts.next(), parts.next()) {
if ! sanename::validate_sanename_word(name) {
info!("failed to load line \"{}\"", line);
return;
}
let admin;
match parts.next() {
Some(flags) => admin = flags.eq("true"),
_ => admin = false
}
self.users.insert(String::from(name.trim()), (self.decode_hex(hex.trim()), admin));
} else {
info!("failed to load line \"{}\"", line);
}
});
info!("loaded {} users from {}", self.users.len(), user_file);
}
pub fn check_passcode(&self, login: &str, passcode: &str) -> (bool, bool) {
if let Some(bin) = self.users.get(login) {
let mut sha256 = Sha256::new();
sha256.input(passcode.as_bytes());
sha256.input(b"\n");
return (sha256.result().iter().eq(bin.0.iter()), bin.1);
}
debug!("login not found {}", login);
(false, false)
}
fn decode_hex(&self, hex: &str) -> Box<[u8]> {
let decoded = base16::decode(hex).unwrap_or_else(|_| panic!("not a hex value"));
if decoded.len() != 256 / 8 {
panic!("not a sha256 hash");
}
let mut hash: Box<[u8]> = Box::new([0u8; 256 /8]);
hash.copy_from_slice(decoded.as_slice());
hash
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_init() {
let mut user_config = UserConfig::new();
user_config.init(&String::from("conf/romp.passwd"));
assert!(user_config.check_passcode("bob", "password").0);
assert!(user_config.check_passcode("alice", "password").0);
assert!( ! user_config.check_passcode("bob", "dE4Rfd").0);
assert!( ! user_config.check_passcode("alice", "ESIKEDSDD").0);
assert!(user_config.check_passcode("admin2", "password").0);
assert!(user_config.check_passcode("admin2", "password").1);
assert!( ! user_config.check_passcode("bob", "password").1);
}
}