lib.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. use core::fmt;
  2. use std::{collections::{HashMap, HashSet}, vec};
  3. use base64::{engine::general_purpose::STANDARD, Engine as _};
  4. use deadpool::managed::Pool;
  5. use ldap3::{Mod, SearchEntry};
  6. use regex::{Captures, Regex};
  7. use ring::rand::{self, SecureRandom};
  8. use serde_derive::Serialize;
  9. use sha2::{Sha512, Digest};
  10. pub mod pool;
  11. use pool::{get_ldap_pool, LdapConfig, LdapManager};
  12. # [derive(Serialize)]
  13. pub enum Error {
  14. Authfailed {message: String},
  15. LdapServerError {message: String},
  16. }
  17. impl fmt::Display for Error {
  18. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  19. match self {
  20. Error::Authfailed { message } => write!(f, "{}", message),
  21. Error::LdapServerError { message } => write!(f, "LDAP server error: {}", message),
  22. }
  23. }
  24. }
  25. pub enum ManageSSHOps {
  26. ADD,
  27. DEL
  28. }
  29. #[derive(Clone)]
  30. pub struct LdapWrapper {
  31. ldap_pool: Pool<LdapManager>,
  32. config: LdapConfig,
  33. }
  34. pub struct User {
  35. pub uid: String,
  36. pub is_ssh: bool,
  37. pub groups: Vec<String>,
  38. }
  39. impl LdapWrapper {
  40. pub fn new(config: LdapConfig) -> LdapWrapper {
  41. let ldap_pool = get_ldap_pool(config.clone());
  42. LdapWrapper{ldap_pool, config}
  43. }
  44. async fn get_user_infos(&self, uid: &String) -> Result<(bool, Vec<String>), Error> {
  45. let mut ldap = self.ldap_pool.get().await.unwrap();
  46. let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(uid={})", uid).as_str(), vec!["memberOf", "objectClass"])
  47. .await.map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
  48. let (result_entry, _ldap_res) = search_res.success()
  49. .map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
  50. if result_entry.is_empty() {
  51. return Err(Error::LdapServerError {message: format!("Error contact admins: dn shouldn't be empty")});
  52. }
  53. let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
  54. let is_ssh = records.attrs.get("objectClass").unwrap().contains(&"ldapPublicKey".to_string());
  55. let groups: Vec<String> = match records.attrs.get("memberOf") {
  56. Some(memberof_records) => memberof_records.iter()
  57. .map(|elem| {
  58. elem.to_lowercase().replace(&format!(",{}", self.config.groupsdn.to_lowercase()).to_string(), "")
  59. .replace("cn=", "")
  60. }).collect(),
  61. None => Vec::new(),
  62. };
  63. Ok((is_ssh, groups))
  64. }
  65. pub async fn auth(&self, username: String, passwd: String) -> Result<User, Error> {
  66. let mut ldap = self.ldap_pool.get().await.unwrap();
  67. let mut uid = username.clone();
  68. let email = Regex::new(r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}*").unwrap();
  69. if email.is_match(&username) {
  70. let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(mail={})", username).as_str(), vec!["uid"])
  71. .await.map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
  72. let (result_entry, _ldap_res) = search_res.success().map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
  73. if result_entry.is_empty() {
  74. return Err(Error::Authfailed {message: format!("Wrong username or password")});
  75. }
  76. if result_entry.len() > 1 {
  77. return Err(Error::Authfailed {message: format!("Error contact admins")});
  78. }
  79. let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
  80. uid = records.attrs.get("uid").unwrap().first().unwrap().to_owned();
  81. }
  82. ldap
  83. .simple_bind(format!("uid={},{}", uid, self.config.basedn).as_str(), &passwd)
  84. .await
  85. .map_err(|_| {
  86. Error::Authfailed {message: format!("Wrong username or password")}
  87. })
  88. .and_then(|r| {
  89. r.success().map_err(|_| {
  90. Error::Authfailed {message: format!("Wrong username or password")}
  91. })
  92. })?;
  93. let (is_ssh, groups) = self.get_user_infos(&uid).await?;
  94. Ok(User{uid: uid.clone(), is_ssh, groups})
  95. }
  96. pub async fn change_password(&self, username: String, password: String, new_password: String) -> Result<(), Error> {
  97. let mut salt = [0u8; 4];
  98. let salt_generator = rand::SystemRandom::new();
  99. let _ = salt_generator.fill(&mut salt);
  100. let mut password_bytes = new_password.into_bytes();
  101. //adding salt to the end of the password
  102. salt.map(|elem| {
  103. password_bytes.push(elem);
  104. });
  105. //Getting sha512 sum
  106. let mut hasher = Sha512::new();
  107. hasher.update(password_bytes);
  108. let mut hash = hasher.finalize().as_slice().to_vec();
  109. //Adding salt to the end of hash
  110. salt.map(|elem| {
  111. hash.push(elem);
  112. });
  113. let b64_new_password = format!("{{SSHA512}}{}", STANDARD.encode(hash));
  114. let mut ldap = self.ldap_pool.get().await.unwrap();
  115. let _ = ldap.simple_bind(format!("uid={},{}", username, self.config.basedn).as_str(), &password)
  116. .await
  117. .map_err(|_| {
  118. Error::Authfailed {message: format!("Wrong current password")}
  119. }).and_then(|r| {
  120. r.success().map_err(|_| {
  121. Error::Authfailed {message: format!("Wrong current password")}
  122. })
  123. })?;
  124. let hashset= HashSet::from([b64_new_password.as_str()]);
  125. let data = vec![ldap3::Mod::Replace("userPassword", hashset)];
  126. ldap.modify(format!("uid={},{}", username, self.config.basedn).as_str(), data)
  127. .await
  128. .map_err(|e| {
  129. Error::LdapServerError {message: format!("An error occured, contact admins: {}", e)}
  130. })
  131. .and_then(|r| {
  132. r.success().map_err(|e| {
  133. Error::LdapServerError {message: format!("An error occured, contact admins: {}", e)}
  134. })
  135. })?;
  136. Ok(())
  137. }
  138. pub async fn get_ssh_keys(&self, username: String) -> Result<Vec<HashMap<String, String>>, Error> {
  139. let mut ldap = self.ldap_pool.get().await.unwrap();
  140. let search_res = ldap.search(&self.config.basedn, ldap3::Scope::Subtree, format!("(uid={})", username).as_str(), vec!["ldapPublicKey"])
  141. .await.map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
  142. let (result_entry, _ldap_res) = search_res.success()
  143. .map_err(|e| {Error::LdapServerError {message: format!("Error: {}", e)}})?;
  144. if result_entry.is_empty() {
  145. return Err(Error::LdapServerError {message: format!("Error contact admins: dn shouldn't be empty")});
  146. }
  147. let records = SearchEntry::construct(result_entry.first().unwrap().to_owned());
  148. let ssh_keys: Vec<HashMap<String, String>> = match records.attrs.get("sshPublicKey") {
  149. Some(keys) => {
  150. let mut keys_res = Vec::new();
  151. let key_name_re = Regex::new(r"\S+").unwrap();
  152. for key in keys {
  153. let key_split: Vec<Captures> = key_name_re.captures_iter(&key).collect();
  154. let key_name: String;
  155. if key_split.len() < 3 {
  156. key_name = String::from("unamed key");
  157. } else {
  158. key_name = key_split[2][0].to_string();
  159. }
  160. keys_res.push(HashMap::from([(key_name, key.to_string())]));
  161. }
  162. keys_res
  163. },
  164. None => vec![HashMap::new()],
  165. };
  166. Ok(ssh_keys)
  167. }
  168. pub async fn manage_ssh_key(&self, username: String, ssh_key: String, op: ManageSSHOps) -> Result<(), Error> {
  169. let mut ldap = self.ldap_pool.get().await.unwrap();
  170. let mods: Vec<Mod<&str>>;
  171. match op {
  172. ManageSSHOps::ADD => {
  173. mods = vec![Mod::Add("sshPublicKey", HashSet::from([ssh_key.as_str()]))];
  174. },
  175. ManageSSHOps::DEL => {
  176. mods = vec![Mod::Delete("sshPublicKey", HashSet::from([ssh_key.as_str()]))];
  177. },
  178. }
  179. let res = ldap
  180. .modify(format!("uid={},{}", username, self.config.basedn).as_str(), mods).await;
  181. if let Err(e) = res {
  182. return Err(Error::LdapServerError { message: format!("An error occured, contact admins: {}", e)});
  183. }
  184. match res.unwrap().success() {
  185. Ok(_) => Ok(()),
  186. Err(e) => Err(Error::LdapServerError { message: format!("An error occured, contact admins: {}", e)}),
  187. }
  188. }
  189. }