use crate::{methods::account_new::email::get_wolves_mail, update_group, Config, State}; use ldap3::{exop::PasswordModify, LdapConn, Mod, Scope, SearchEntry}; use sqlx::{Pool, Sqlite}; use std::collections::{HashMap, HashSet}; use tide::{ prelude::{json, Deserialize, Serialize}, Request, }; #[derive(Debug, Deserialize)] pub struct LdapUpdate { user: String, pass: String, field: String, value: String, } #[derive(Debug, Serialize)] pub struct ModifyResult { mail: Option, #[serde(rename = "sshPublicKey")] ssh_public_key: Option, cn: Option, #[serde(rename = "skDiscord")] sk_discord: Option, } /// Handles updating a single field with the users own password pub async fn submit(mut req: Request) -> tide::Result { let LdapUpdate { user, pass, field, value, } = req.body_json().await?; // check that any mail is not using @skynet.ie if field == "mail" && value.trim().ends_with("@skynet.ie") { return Ok(json!({"result": "error", "error": "Skynet email not valid contact address"}).into()); } let config = &req.state().config; let db = &req.state().db; // 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 pw_keep_same = false; let mut is_skynet_user = false; // get the users current password hash let (rs, _res) = ldap.search(&dn, Scope::Base, "(objectClass=*)", vec!["userPassword", "memberOf"])?.success()?; if !rs.is_empty() { let tmp = SearchEntry::construct(rs[0].clone()); if tmp.attrs.contains_key("userPassword") && !tmp.attrs["userPassword"].is_empty() && tmp.attrs["userPassword"][0].starts_with("{SSHA512}") { pw_keep_same = true; } if tmp.attrs.contains_key("memberOf") { for group in tmp.attrs["memberOf"].clone() { if group.contains("skynet-users") { is_skynet_user = true; } } } } // check if the password field itself is being updated let pass_new = if &field != "userPassword" { if !is_skynet_user && &field == "mail" { activate_group(db, config, &user, &value).await; } // if password is not being updated then just update the required field let mods = vec![ // the value we are updating Mod::Replace(field, HashSet::from([value])), ]; ldap.modify(&dn, mods)?.success()?; // pass back the "old" and "new" passwords // using this means we can create teh vars without them needing to be mutable pass.clone() } else { // password is going to be updated, even if the old value is not starting with "{SSHA512}" pw_keep_same = false; value }; // changing teh password because of an explicit request or upgrading teh security. if !pw_keep_same { // really easy to update password once ye know how let tmp = PasswordModify { // none as we are staying on the same connection user_id: None, old_pass: Some(&pass), new_pass: Some(&pass_new), }; ldap.extended(tmp)?.success()?; }; let result = get_result(&mut ldap, &dn); ldap.unbind()?; Ok(json!({"result": "success", "success": result}).into()) } fn get_result(ldap: &mut LdapConn, dn: &str) -> ModifyResult { let mut result = ModifyResult { mail: None, ssh_public_key: None, cn: None, sk_discord: None, }; if let Ok(temp) = ldap.search(dn, Scope::Base, "(objectClass=*)", vec!["mail", "sshPublicKey", "cn", "skDiscord"]) { if let Ok((rs, _res)) = temp.success() { if !rs.is_empty() { let tmp = SearchEntry::construct(rs[0].clone()); result.mail = get_result_values(&tmp.attrs, "mail"); result.ssh_public_key = get_result_values(&tmp.attrs, "sshPublicKey"); result.cn = get_result_values(&tmp.attrs, "cn"); result.sk_discord = get_result_values(&tmp.attrs, "skDiscord"); } } } result } fn get_result_values(attrs: &HashMap>, field: &str) -> Option { if let Some(field) = attrs.get(field) { if !field.is_empty() { return Some(field[0].clone()); } } None } async fn activate_group(db: &Pool, config: &Config, user: &str, mail: &str) { // check if user has this mail in teh wolves db if !get_wolves_mail(db, mail).await.is_empty() { // if so then activate if let Err(e) = update_group(config, "skynet-users", &vec![user.to_string()], false).await { println!("Couldnt add {} to skynet-users: {:?}", user, e) } } }