use std::collections::HashSet; use base64::{engine::general_purpose::STANDARD, Engine as _}; use deadpool::managed::Pool; use ldap3::SearchEntry; use regex::Regex; use ring::rand::{self, SecureRandom}; use sha2::{Sha512, Digest}; pub mod pool; use pool::{get_ldap_pool, LdapConfig, LdapManager}; pub enum Error { Authfailed(String), LdapServerError(String) } #[derive(Clone)] pub struct LdapWrapper { ldap_pool: Pool, config: LdapConfig, } pub struct User { pub uid: String, pub groups: Vec, } impl LdapWrapper { pub fn new(config: LdapConfig) -> LdapWrapper { let ldap_pool = get_ldap_pool(config.clone()); LdapWrapper{ldap_pool, config} } pub async fn auth(&self, username: String, passwd: String) -> Result { let mut ldap = self.ldap_pool.get().await.unwrap(); let mut uid = username.clone(); let email = Regex::new(r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}*").unwrap(); if email.is_match(&username) { let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(mail={})", username).as_str(), vec!["uid"]) .await.map_err(|_| {Error::Authfailed(format!("Wrong username or password"))})?; let (result_entry, _ldap_res) = search_res.success().map_err(|_| {Error::Authfailed(format!("Wrong username or password"))})?; if result_entry.is_empty() { return Err(Error::Authfailed(format!("No record found"))); } if result_entry.len() > 1 { return Err(Error::Authfailed(format!("Multiple records found"))); } let records = SearchEntry::construct(result_entry.first().unwrap().to_owned()); uid = records.attrs.get("uid").unwrap().first().unwrap().to_owned(); } ldap .simple_bind(format!("uid={},{}", uid, self.config.basedn).as_str(), &passwd) .await .map_err(|_| { Error::Authfailed(format!("Wrong username or password")) }) .and_then(|r| { r.success().map_err(|_| { Error::Authfailed(format!("Wrong username or password")) }) }) .and(Ok(User{uid: uid, groups: vec!("coucou".to_string())})) //TODO: add groups } pub async fn change_password(&self, username: String, password: String, new_password: String) -> Result<(), Error> { let mut salt = [0u8; 4]; let salt_generator = rand::SystemRandom::new(); let _ = salt_generator.fill(&mut salt); let mut password_bytes = new_password.into_bytes(); //adding salt to the end of the password salt.map(|elem| { password_bytes.push(elem); }); //Getting sha512 sum let mut hasher = Sha512::new(); hasher.update(password_bytes); let mut hash = hasher.finalize().as_slice().to_vec(); //Adding salt to the end of hash salt.map(|elem| { hash.push(elem); }); let b64_new_password = format!("{{SSHA512}}{}", STANDARD.encode(hash)); let mut ldap = self.ldap_pool.get().await.unwrap(); let _ = ldap.simple_bind(format!("uid={},{}", username, self.config.basedn).as_str(), &password) .await .map_err(|_| { Error::Authfailed(format!("Wrong current password")) }).and_then(|r| { r.success().map_err(|_| { Error::Authfailed(format!("Wrong current password")) }) }); let hashset= HashSet::from([b64_new_password.as_str()]); let data = vec![ldap3::Mod::Replace("userPassword", hashset)]; ldap.modify(format!("uid={},{}", username, self.config.basedn).as_str(), data) .await .map_err(|e| { Error::LdapServerError(format!("An error occured, contact admins: {}", e)) }) .and_then(|r| { r.success().map_err(|e| { Error::LdapServerError(format!("An error occured, contact admins: {}", e)) }) }) .and(Ok(())) } }