| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- use core::fmt;
- use std::{collections::{HashMap, HashSet}, vec};
- use base64::{engine::general_purpose::STANDARD, Engine as _};
- use deadpool::managed::Pool;
- use ldap3::{Mod, SearchEntry};
- use regex::{Captures, Regex};
- use ring::rand::{self, SecureRandom};
- use serde_derive::Serialize;
- use sha2::{Sha512, Digest};
- pub mod pool;
- use pool::{get_ldap_pool, LdapConfig, LdapManager};
- # [derive(Serialize)]
- pub enum Error {
- Authfailed {message: String},
- LdapServerError {message: String},
- }
- impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Error::Authfailed { message } => write!(f, "{}", message),
- Error::LdapServerError { message } => write!(f, "LDAP server error: {}", message),
- }
- }
- }
- pub enum ManageSSHOps {
- ADD,
- DEL
- }
- #[derive(Clone)]
- pub struct LdapWrapper {
- ldap_pool: Pool<LdapManager>,
- config: LdapConfig,
- }
- pub struct User {
- pub uid: String,
- pub is_ssh: bool,
- pub groups: Vec<String>,
- }
- impl LdapWrapper {
- pub fn new(config: LdapConfig) -> LdapWrapper {
- let ldap_pool = get_ldap_pool(config.clone());
- LdapWrapper{ldap_pool, config}
- }
- async fn get_user_infos(&self, uid: &String) -> Result<(bool, Vec<String>), Error> {
- let mut ldap = self.ldap_pool.get().await.unwrap();
- let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(uid={})", uid).as_str(), vec!["memberOf", "objectClass"])
- .await.map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
- let (result_entry, _ldap_res) = search_res.success()
- .map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
- if result_entry.is_empty() {
- return Err(Error::LdapServerError {message: format!("Error contact admins: dn shouldn't be empty")});
- }
- let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
- let is_ssh = records.attrs.get("objectClass").unwrap().contains(&"ldapPublicKey".to_string());
- let groups: Vec<String> = match records.attrs.get("memberOf") {
- Some(memberof_records) => memberof_records.iter()
- .map(|elem| {
- elem.to_lowercase().replace(&format!(",{}", self.config.groupsdn.to_lowercase()).to_string(), "")
- .replace("cn=", "")
- }).collect(),
- None => Vec::new(),
- };
- Ok((is_ssh, groups))
- }
-
- pub async fn auth(&self, username: String, passwd: String) -> Result<User, Error> {
- 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(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
- let (result_entry, _ldap_res) = search_res.success().map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
- if result_entry.is_empty() {
- return Err(Error::Authfailed {message: format!("Wrong username or password")});
- }
- if result_entry.len() > 1 {
- return Err(Error::Authfailed {message: format!("Error contact admins")});
- }
- 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 {message: format!("Wrong username or password")}
- })
- .and_then(|r| {
- r.success().map_err(|_| {
- Error::Authfailed {message: format!("Wrong username or password")}
- })
- })?;
- let (is_ssh, groups) = self.get_user_infos(&uid).await?;
- Ok(User{uid: uid.clone(), is_ssh, 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 {message: format!("Wrong current password")}
- }).and_then(|r| {
- r.success().map_err(|_| {
- Error::Authfailed {message: 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 {message: format!("An error occured, contact admins: {}", e)}
- })
- .and_then(|r| {
- r.success().map_err(|e| {
- Error::LdapServerError {message: format!("An error occured, contact admins: {}", e)}
- })
- })?;
- Ok(())
- }
- pub async fn get_ssh_keys(&self, username: String) -> Result<Vec<HashMap<String, String>>, Error> {
- let mut ldap = self.ldap_pool.get().await.unwrap();
- let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(uid={})", username).as_str(), vec!["ldapPublicKey"])
- .await.map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
- let (result_entry, _ldap_res) = search_res.success()
- .map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
- if result_entry.is_empty() {
- return Err(Error::LdapServerError {message: format!("Error contact admins: dn shouldn't be empty")});
- }
- let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
- let ssh_keys: Vec<HashMap<String, String>> = match records.attrs.get("sshPublicKey") {
- Some(keys) => {
- let mut keys_res = Vec::new();
- let key_name_re = Regex::new(r"\S+").unwrap();
- for key in keys {
- let key_split: Vec<Captures> = key_name_re.captures_iter(&key).collect();
- let key_name: String;
- if key_split.len() < 3 {
- key_name = String::from("unamed key");
- } else {
- key_name = key_split[2][0].to_string();
- }
- keys_res.push(HashMap::from([(key_name, key.to_string())]));
- }
- keys_res
- },
- None => vec![HashMap::new()],
- };
- Ok(ssh_keys)
- }
- pub async fn manage_ssh_key(&self, username: String, ssh_key: String, op: ManageSSHOps) -> Result<(), Error> {
- let mut ldap = self.ldap_pool.get().await.unwrap();
- let mods: Vec<Mod<&str>>;
- match op {
- ManageSSHOps::ADD => {
- mods = vec![Mod::Add("sshPublicKey", HashSet::from([ssh_key.as_str()]))];
- },
- ManageSSHOps::DEL => {
- mods = vec![Mod::Delete("sshPublicKey", HashSet::from([ssh_key.as_str()]))];
- },
- }
- let res = ldap
- .modify(format!("uid={},{}", username, self.config.basedn).as_str(), mods).await;
- if let Err(e) = res {
- return Err(Error::LdapServerError { message: format!("An error occured, contact admins: {}", e)});
- }
- match res.unwrap().success() {
- Ok(_) => Ok(()),
- Err(e) => Err(Error::LdapServerError { message: format!("An error occured, contact admins: {}", e)}),
- }
- }
- }
|