feat: Search queries by mail attribute

This commit is contained in:
selfhoster selfhoster 2026-09-03 15:21:26 +02:00
commit c77223048c
9 changed files with 592 additions and 78 deletions

View file

@ -1,8 +1,6 @@
use dn_escape::dn_escape;
use ldap3_proto::LdapResultCode;
use crate::db::UserRef;
use crate::ldap::LdapReturnError;
use crate::db::User;
/// A simple multi-value Map powered by a vector, to respect
/// order of first appearance of keys.
@ -52,30 +50,7 @@ impl VecMap {
}
#[derive(Clone, Debug)]
pub struct InvalidDnError(pub String);
impl LdapReturnError for InvalidDnError {
fn code(&self) -> LdapResultCode {
LdapResultCode::InvalidDNSyntax
}
fn message(&self) -> String {
format!("Invalid dn: {}", self.0)
}
}
#[derive(Clone, Debug)]
pub struct NotUserDnError(pub String);
impl LdapReturnError for NotUserDnError {
fn code(&self) -> LdapResultCode {
LdapResultCode::InvalidDNSyntax
}
fn message(&self) -> String {
format!("Not a user dn containing uid/dc: {}", self.0)
}
}
pub struct MalformedDn;
/// A Dn is a key-value mapping which can contain the same key several times.
///
@ -96,7 +71,7 @@ impl Dn {
///
/// However, if we find a really funny request such as `dc=foo=bar`, then
/// we return an error to the client.
pub fn from_dn_str(input: &str) -> Result<Self, InvalidDnError> {
pub fn from_dn_str(input: &str) -> Result<Self, MalformedDn> {
let mut keys = VecMap::new();
if input.is_empty() {
@ -108,7 +83,7 @@ impl Dn {
// Here we have key=val pairs
if query_parts.clone().count() != 2 {
// Bad request
return Err(InvalidDnError(input.to_string()));
return Err(MalformedDn);
}
let key = query_parts.next().unwrap();
@ -169,21 +144,15 @@ impl Dn {
self.keys.insert("dc", domain_components, force);
}
pub fn to_user_ref(&self) -> Result<UserRef, NotUserDnError> {
let Some(username_vals) = self.keys.get("uid") else {
return Err(NotUserDnError(self.to_dn_string()));
};
if username_vals.len() != 1 {
return Err(NotUserDnError(self.to_dn_string()));
pub fn from_user(user: &User) -> Self {
let mut keys = VecMap::new();
keys.insert_or_append("uid", &user.username);
keys.insert_or_append("ou", "people");
for domain_component in user.domain.split('.') {
keys.insert_or_append("dc", domain_component);
}
let username = username_vals[0].clone();
let Some(domain_vals) = self.keys.get("dc") else {
return Err(NotUserDnError(self.to_dn_string()));
};
let domain = domain_vals.join(".");
Ok(UserRef { username, domain })
Self { keys }
}
}