From e32b4bfb829cc3ba3cfd49af6333e9caaa018b0a Mon Sep 17 00:00:00 2001 From: selfhoster1312 Date: Mon, 21 Sep 2026 15:39:49 +0200 Subject: [PATCH] refactor: Reuse typed LDAP attributes in search results --- src/ldap/op/search.rs | 58 ++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/ldap/op/search.rs b/src/ldap/op/search.rs index 86ef63d..0a29122 100644 --- a/src/ldap/op/search.rs +++ b/src/ldap/op/search.rs @@ -148,30 +148,42 @@ pub async fn search_success( fn search_entry_from_user(user: &User, req_attrs: &[String]) -> LdapSearchResultEntry { let mut res: Vec = vec![]; - for attr in req_attrs { - if let Some(attr_values) = match attr.as_str() { - "uid" => Some(vec![user.username.clone()]), - "cn" | "mail" => Some(vec![user.mail.clone()]), - // TODO: group membership - "memberof" => Some(vec![]), - // Copied from lldap output, not sure if we want to add/remove some classes depending on context - "objectclass" => Some( - vec!["inetOrgPerson", "posixAccount", "mailAccount", "person"] - .into_iter() - .map(String::from) - .collect(), - ), - _ => { - tracing::warn!("Ignoring unknown attr in search query: {attr}"); - None + for attr_str in req_attrs { + // We keep a copy of the requested attribute so that we can answer as it was requested, + // eg. `CN` => `CN` (instead of normalizing to `cn`). + let Some(attr) = LdapAttribute::from_str(attr_str) + .inspect_err(|e| tracing::debug!("Unknown attribute in request: {e}")) + .ok() + else { + continue; + }; + + let str_values = match attr { + // TODO: should we normalize the value some more here? + LdapAttribute::Uid => vec![user.username.clone()], + // TODO: should CN be different than the mail? + // TODO: should we normalize values some more here? + LdapAttribute::CommonName | LdapAttribute::Mail | LdapAttribute::MailAlias => { + vec![user.mail.clone()] } - } { - res.push(LdapPartialAttribute { - atype: attr.clone(), - // LDAP response expects raw byte vec for each value - vals: attr_values.into_iter().map(Vec::from).collect(), - }); - } + // TODO: group membership + LdapAttribute::MemberOf => vec![], + // TODO: if we introduce mail permission, we need to remove mailaccount from here + LdapAttribute::ObjectClass => vec![ + "inetOrgPerson".to_string(), + "posixAccount".to_string(), + "mailAccount".to_string(), + "person".to_string(), + ], + }; + + res.push(LdapPartialAttribute { + // Reuse the requested attribute name, not the normalized form + // we would otherwise produce. + atype: attr_str.clone(), + // LDAP response expects raw byte vec for each value + vals: str_values.into_iter().map(Vec::from).collect(), + }); } LdapSearchResultEntry {