use rand::{thread_rng, Rng};
use bcrypt::{DEFAULT_COST, hash as bhash};
use ::log::*; use std::collections::HashMap;
use std::process::Command;
use std::thread;
use regex::Regex;
use crate::messages::Msg;
pub fn generate_token() -> String {
let token_length: usize = super::app_config("token_length").parse().unwrap();
let characters = super::app_config("token_chars");
let chars_len: usize = characters.as_bytes().len();
let charset: &[u8] = characters.as_bytes();
let mut rng = thread_rng();
let result: String = (0..token_length).map(|_| {
let index = rng.gen_range(0, chars_len);
charset[index] as char
}).collect();
result
}
pub fn generate_id() -> String { let len: usize = super::app_config("id_length").parse().unwrap();
let chars = super::app_config("id_chars");
let chars_len: usize = chars.as_bytes().len();
let charset: &[u8] = chars.as_bytes();
let mut rng = thread_rng();
let result: String = (0..len).map(|_| {
let index = rng.gen_range(0, chars_len);
charset[index] as char
}).collect();
result
}
pub fn hash(an_item: &str) -> String {
bhash(an_item, DEFAULT_COST).unwrap()
}
pub fn str_to_map(data: &str, pair_separator: char, value_separator: char) -> HashMap<String, String> {
let pairs: Vec<&str> = data.split(|c| c == pair_separator).collect();
debug!("Pairs are: {:?}", &pairs);
let mut map = HashMap::new();
for pair in pairs {
let result: Vec<&str> = pair.split(|c| c == value_separator).collect();
map.insert(result[0].to_string(), result[1].to_string());
}
debug!("Hashmap is: {:?}", &map);
map
}
#[derive(Debug)]
pub struct Email {
from: String,
to: String,
subject: String,
body: String,
}
impl Email {
pub fn new(from: &str, to: &str, subject: &str, body: &str) -> Email {
Email {
from: from.to_string(),
to: to.to_string(),
subject: subject.to_string(),
body: body.to_string()
}
}
pub fn send(&self) { let from = self.from.clone();
let to = self.to.clone();
let subject = self.subject.clone();
let body = self.body.clone();
thread::spawn(move || {
let mail_cmd = format!("echo -e 'From: {}\nSubject: {}\n\n{}' | msmtp {}", from, subject, body, to);
let output = Command::new("sh")
.arg("-c")
.arg(mail_cmd)
.output()
.expect("failed to execute sh command echo hello!!!");
debug!("msmtp output: {:?}", output);
});
}
}
#[derive(Debug)]
pub struct Validator {}
impl Validator {
pub fn validate_password(key: String) -> Vec<Msg> {
let password = Password { key };
password.validate_pattern()
}
pub fn validate_email_id_pattern(id: String) -> bool {
let email_id = EmailID { id };
email_id.validate_pattern()
}
}
#[derive(Debug)]
struct EmailID {
id: String
}
impl EmailID {
fn validate_pattern(&self) -> bool {
let re = Regex::new(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$");
match re {
Ok(re) => re.is_match(&self.id),
Err(error) => {
error!("{:?} occurred in utils::EmailID::validate_pattern()", error);
false
}
}
}
}
#[derive(Debug)]
struct Password {
key: String,
}
impl Password {
fn validate_pattern(&self) -> Vec<Msg> {
let mut errors = vec![];
if !self.has_lowercase() {
errors.push(Msg::PasswordHasNoLowercase)
}
if !self.has_uppercase() {
errors.push(Msg::PasswordHasNoUppercase)
}
if !self.has_number() {
errors.push(Msg::PasswordHasNoNumber)
}
if !self.has_spl_chars() {
errors.push(Msg::PasswordHasNoSplChar)
}
if !self.length_ok() {
errors.push(Msg::PasswordLengthNotOk)
}
errors
}
fn has_lowercase(&self) -> bool { match Regex::new(r"^.*[a-z]+.*$") {
Ok(re) => re.is_match(&self.key),
Err(error) => {
error!("{:?} occurred in utils::Password::has_lowercase()", error);
false
}
}
}
fn has_uppercase(&self) -> bool { match Regex::new(r"^.*[A-Z]+.*$") {
Ok(re) => re.is_match(&self.key),
Err(error) => {
error!("{:?} occurred in utils::Password::has_uppercase()", error);
false
}
}
}
fn has_number(&self) -> bool { match Regex::new(r"^.*[0-9]+.*$") {
Ok(re) => re.is_match(&self.key),
Err(error) => {
error!("{:?} occurred in utils::Password::has_number()", error);
false
}
}
}
fn has_spl_chars(&self) -> bool { let spl_chars = super::app_config("permitted_special_characters_in_password"); let re_str = r"^.*[".to_owned() + &spl_chars + "]+.*$";
match Regex::new(&re_str) {
Ok(re) => re.is_match(&self.key),
Err(error) => {
error!("{:?} occurred in utils::Password::has_spl_chars()", error);
false
}
}
}
fn length_ok(&self) -> bool {
let passwd_len = super::app_config("min_password_length");
let re_str = r"^[\s\S]{".to_owned() + &passwd_len + ",}$";
match Regex::new(&re_str) {
Ok(re) => re.is_match(&self.key),
Err(error) => {
error!("{:?} occurred in utils::Password::length_ok", error);
false
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn utest_valid_email_pattern() {
let email_id = EmailID{ id: "sample1_email@integration.test".to_string() };
assert_eq!(email_id.validate_pattern(), true)
}
#[test]
fn utest_invalid_email_pattern() {
let email_id = EmailID{ id: "aaaa".to_string() };
assert_eq!(email_id.validate_pattern(), false)
}
#[test]
fn utest_password_contains_no_lowercase() {
let result = Password{ key: "PASS123".to_string() }.has_lowercase();
assert_eq!(result, false)
}
#[test]
fn utest_password_contains_lowercase() {
let result = Password{ key: "pASS123".to_string() }.has_lowercase();
assert_eq!(result, true)
}
#[test]
fn utest_password_contains_no_uppercase() {
let result = Password{ key: "pass123".to_string() }.has_uppercase();
assert_eq!(result, false)
}
#[test]
fn utest_password_contains_uppercase() {
let result = Password{ key: "PASS123".to_string() }.has_uppercase();
assert_eq!(result, true)
}
#[test]
fn utest_password_contains_no_number() {
let result = Password{ key: "pass".to_string() }.has_number();
assert_eq!(result, false)
}
#[test]
fn utest_password_contains_number() {
let result = Password{ key: "pass2".to_string() }.has_number();
assert_eq!(result, true)
}
#[test]
fn utest_password_length_ok() {
let result = Password{ key: "pass1234".to_string() }.length_ok();
assert_eq!(result, true)
}
#[test]
fn utest_password_length_not_ok() {
let result = Password{ key: "pass34".to_string() }.length_ok();
assert_eq!(result, false)
}
#[test]
fn utest_password_contains_no_special_character() {
let result = Password{ key: "PASS123".to_string() }.has_spl_chars();
assert_eq!(result, false)
}
#[test]
fn utest_password_contains_special_character() {
let result = Password{ key: "pASS12*".to_string() }.has_spl_chars();
assert_eq!(result, true)
}
}