From 1dae2ecb2623d55c88a237d55198efd51e0fd8fe Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 29 Jun 2025 22:59:12 +0100 Subject: [PATCH] fix: now checks to see what is needed to be added or removed. This is more complex than the ``replace`` option but it should give more reliable results --- src/lib.rs | 109 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ba3aac2..333a5b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ use sqlx::{ sqlite::{SqliteConnectOptions, SqlitePoolOptions}, Error, Pool, Sqlite, }; +use std::collections::HashSet; use std::{ env, str::FromStr, @@ -215,28 +216,23 @@ pub async fn update_group(config: &Config, group: &str, users: &[String], replac // use the admin account ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw)?.success()?; - let dn = format!("cn={},ou=groups,dc=skynet,dc=ie", group); - let members = users.iter().map(|uid| uid_to_dn(uid)).collect(); - let mods = if replace { - vec![Mod::Replace("member".to_string(), members)] - } else { - vec![Mod::Add("member".to_string(), members)] - }; + // we have two groups in teh LDAP that folks must be a member of + // ``groupname`` is the main group that we use to grant folks access to services + // ``groupname-linux`` is specifically so that we can store some of the key linux data in the ldap, such as uid's - if let Err(x) = ldap.modify(&dn, mods) { - println!("{:?}", x); + { + let dn = format!("cn={},ou=groups,dc=skynet,dc=ie", group); + let in_wolves = users.iter().map(|uid| uid_to_dn(uid)).collect::>(); + let attribute = "member"; + update_group_sub(&mut ldap, &dn, attribute, &in_wolves, replace); } - let dn_linux = format!("cn={}-linux,ou=groups,dc=skynet,dc=ie", group); - let members_linux = users.iter().map(|uid| uid.to_string()).collect(); - let mods = if replace { - vec![Mod::Replace("memberUid".to_string(), members_linux)] - } else { - vec![Mod::Add("memberUid".to_string(), members_linux)] - }; - if let Err(x) = ldap.modify(&dn_linux, mods) { - println!("{:?}", x); - }; + { + let dn = format!("cn={}-linux,ou=groups,dc=skynet,dc=ie", group); + let in_wolves = users.iter().map(|uid| uid.to_string()).collect::>(); + let attribute = "memberUid"; + update_group_sub(&mut ldap, &dn, attribute, &in_wolves, replace); + } // tidy up ldap.unbind()?; @@ -244,6 +240,81 @@ pub async fn update_group(config: &Config, group: &str, users: &[String], replac Ok(()) } +fn update_group_sub(ldap: &mut LdapConn, dn: &str, attribute: &str, in_wolves: &HashSet, replace: bool) { + if replace { + let in_ldap = get_ldap_group(ldap, dn, attribute); + let modifications = members_to_modifications(in_wolves, &in_ldap, attribute); + + // uid=gamesdev,ou=users,dc=skynet,dc=ie + // uid=gamesdev,ou=users,dc=skynet,dc=ie + // dbg!(&dn, &modifications); + if !modifications.is_empty() { + if let Err(x) = ldap.modify(dn, modifications) { + println!("{:?}", x); + } + } + } else if let Err(x) = ldap.modify(dn, vec![Mod::Add(attribute.to_string(), in_wolves.to_owned())]) { + println!("{:?}", x); + }; +} + +fn get_ldap_group(ldap: &mut LdapConn, dn: &str, attribute: &str) -> HashSet { + match ldap.search(dn, Scope::Base, "(objectClass=*)", vec!["cn", attribute]) { + Ok(x) => { + if let Ok((rs, _res)) = x.success() { + for entry in rs { + let tmp = SearchEntry::construct(entry); + if tmp.attrs.contains_key(attribute) && !tmp.attrs[attribute].is_empty() { + return HashSet::from_iter(tmp.attrs[attribute].iter().cloned()); + } + } + } + } + Err(e) => { + dbg!(e); + } + }; + HashSet::new() +} +fn members_to_modifications(in_wolves: &HashSet, in_ldap: &HashSet, attribute: &str) -> Vec> { + let mut add = HashSet::new(); + let mut remove = HashSet::new(); + + // now we need a vec + let mut joined = HashSet::new(); + for member in in_ldap { + joined.insert(member.to_owned()); + } + for member in in_wolves { + joined.insert(member.to_owned()); + } + + for member in &joined { + // if its already in both then we dont need to do anything, nothign changes + if in_ldap.contains(member) && in_wolves.contains(member) { + continue; + } + // if its in the ldap but not the database (from wolves) then we will need to remove from teh LDAP + if in_ldap.contains(member) && !in_wolves.contains(member) { + remove.insert(member.to_owned()); + } + // if the person is member on wolves but not ldap then we need to add it to the LDAP + if !in_ldap.contains(member) && in_wolves.contains(member) { + add.insert(member.to_owned()); + } + } + + let mut modifications = vec![]; + if !add.is_empty() { + modifications.push(Mod::Add(attribute.to_string(), add)); + } + if !remove.is_empty() { + modifications.push(Mod::Delete(attribute.to_string(), remove)); + } + + modifications +} + #[derive(Debug, Deserialize)] pub struct LdapAuth { user: String,