use std::collections::HashSet; use ldap3::result::Result; use ldap3::{LdapConn, Scope, SearchEntry, Mod}; use base64::{Engine as _, engine::general_purpose}; use crypto::{sha2::Sha512, digest::Digest}; // for teh webserver use sqlx::{Pool, Sqlite}; use std::env; use dotenv::dotenv; use tide::prelude::*; use tide::{Request, Response}; use skynet_ldap_server::db_init; #[derive(Clone)] struct State { db: Pool, config: Config, } #[async_std::main] async fn main() -> tide::Result<()> { let config = get_config(); let db = db_init(&config.database).await?; let host_port = config.host_port.clone(); tide::log::start(); let state = State { db, config, }; let mut app = tide::with_state(state); app.at("/ldap/update").post(post_update_ldap); app.listen(host_port).await?; Ok(()) } #[derive(Debug, Clone)] struct Config { ldap_host: String, database: String, host_port: String, } fn get_config() -> Config { dotenv().ok(); // reasonable defaults let mut config = Config { ldap_host: "".to_string(), database: "database.db".to_string(), host_port: "127.0.0.1:8087".to_string(), }; if let Ok(x) = env::var("LDAP_HOST") { config.ldap_host = x.trim().to_string(); } if let Ok(x) = env::var("DATABASE") { config.database = x.trim().to_string(); } if let Ok(x) = env::var("HOST_PORT") { config.host_port = x.trim().to_string(); } config } //https://stackoverflow.com/a/44532957 pub fn hex_to_base64(hex: &str) -> String { // Make vector of bytes from octets let mut bytes = Vec::new(); for i in 0..(hex.len()/2) { let res = u8::from_str_radix(&hex[2*i .. 2*i+2], 16); match res { Ok(v) => bytes.push(v), Err(e) => println!("Problem with hex: {}", e), }; }; general_purpose::STANDARD.encode(&bytes) // now convert from Vec to b64-encoded String } #[derive(Debug, Deserialize)] struct LdapUpdate { user: String, pass: String, field: String, value: String } async fn post_update_ldap(mut req: Request) -> tide::Result { let LdapUpdate { user, pass, field, value } = req.body_json().await?; let config = &req.state().config; // easier to give each request its own connection let mut ldap = LdapConn::new(&config.ldap_host)?; let dn = format!("uid={},ou=users,dc=skynet,dc=ie", user); ldap.simple_bind(&dn, &pass)?.success()?; // always assume insecure let mut secure = false; // get the users current password hash let (rs, _res) = ldap.search(&dn,Scope::Base,"(objectClass=*)",vec!["userPassword"])?.success()?; if !rs.is_empty() { let tmp = SearchEntry::construct(rs[0].clone()); if !tmp.attrs["userPassword"].is_empty() && tmp.attrs["userPassword"][0].starts_with("{SHA512}") { secure = true; } } let mut mods = vec![ Mod::Replace(field, HashSet::from([value])) ]; if !secure { let mut hasher = Sha512::new(); hasher.input_str(&pass); // get it as hex string let hex = hasher.result_str(); // convert it to b64 let pass_tmp = format!("{{SHA512}}{}", hex_to_base64(&hex)); mods.push(Mod::Replace(String::from("userPassword"), HashSet::from([pass_tmp]))); }; ldap.modify(&dn, mods)?.success()?; ldap.unbind()?; Ok(format!("Hello, {}! I've put in an order for {} shoes", "name", "legs").into()) }