From 4955b7fac16ac3bb4707654194f5c4869b43076f Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Tue, 6 Feb 2024 22:23:10 +0100 Subject: [PATCH] server: Add support for the custom LDAP object classes in LDAP filters --- server/src/domain/ldap/group.rs | 13 ++++++---- server/src/domain/ldap/user.rs | 18 ++++++++++---- server/src/infra/ldap_handler.rs | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/server/src/domain/ldap/group.rs b/server/src/domain/ldap/group.rs index b481a7c..ea5cc40 100644 --- a/server/src/domain/ldap/group.rs +++ b/server/src/domain/ldap/group.rs @@ -9,7 +9,7 @@ use crate::domain::{ handler::{GroupListerBackendHandler, GroupRequestFilter}, ldap::error::LdapError, schema::{PublicSchema, SchemaGroupAttributeExtractor}, - types::{AttributeName, AttributeType, Group, UserId, Uuid}, + types::{AttributeName, AttributeType, Group, LdapObjectClass, UserId, Uuid}, }; use super::{ @@ -177,10 +177,13 @@ fn convert_group_filter( )?; Ok(GroupRequestFilter::Member(user_name)) } - GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from(matches!( - value.as_str(), - "groupofuniquenames" | "groupofnames" - ))), + GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from( + matches!(value.as_str(), "groupofuniquenames" | "groupofnames") + || schema + .get_schema() + .extra_group_object_classes + .contains(&LdapObjectClass::from(value)), + )), GroupFieldType::Dn | GroupFieldType::EntryDn => { Ok(get_group_id_from_distinguished_name( value.as_str(), diff --git a/server/src/domain/ldap/user.rs b/server/src/domain/ldap/user.rs index b381ec4..dc26a38 100644 --- a/server/src/domain/ldap/user.rs +++ b/server/src/domain/ldap/user.rs @@ -15,7 +15,10 @@ use crate::domain::{ }, }, schema::{PublicSchema, SchemaUserAttributeExtractor}, - types::{AttributeName, AttributeType, GroupDetails, User, UserAndGroups, UserColumn, UserId}, + types::{ + AttributeName, AttributeType, GroupDetails, LdapObjectClass, User, UserAndGroups, + UserColumn, UserId, + }, }; pub fn get_user_attribute( @@ -206,10 +209,15 @@ fn convert_user_filter( } Ok(UserRequestFilter::from(false)) } - UserFieldType::ObjectClass => Ok(UserRequestFilter::from(matches!( - value.as_str(), - "person" | "inetorgperson" | "posixaccount" | "mailaccount" - ))), + UserFieldType::ObjectClass => Ok(UserRequestFilter::from( + matches!( + value.as_str(), + "person" | "inetorgperson" | "posixaccount" | "mailaccount" + ) || schema + .get_schema() + .extra_user_object_classes + .contains(&LdapObjectClass::from(value)), + )), UserFieldType::MemberOf => Ok(UserRequestFilter::MemberOf( get_group_id_from_distinguished_name( &value, diff --git a/server/src/infra/ldap_handler.rs b/server/src/infra/ldap_handler.rs index 8fe88fe..f21d12b 100644 --- a/server/src/infra/ldap_handler.rs +++ b/server/src/infra/ldap_handler.rs @@ -1931,6 +1931,47 @@ mod tests { ); } + #[tokio::test] + async fn test_search_filters_custom_object_class() { + let mut mock = MockTestBackendHandler::new(); + mock.expect_list_users() + .with(eq(Some(UserRequestFilter::from(true))), eq(false)) + .times(1) + .return_once(|_, _| { + Ok(vec![UserAndGroups { + user: User { + user_id: UserId::new("bob_1"), + ..Default::default() + }, + groups: None, + }]) + }); + let mut ldap_handler = setup_bound_admin_handler(mock).await; + let request = make_user_search_request( + LdapFilter::Equality("objectClass".to_owned(), "CUSTOMuserCLASS".to_owned()), + vec!["objectclass"], + ); + assert_eq!( + ldap_handler.do_search_or_dse(&request).await, + Ok(vec![ + LdapOp::SearchResultEntry(LdapSearchResultEntry { + dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(), + attributes: vec![LdapPartialAttribute { + atype: "objectclass".to_string(), + vals: vec![ + b"inetOrgPerson".to_vec(), + b"posixAccount".to_vec(), + b"mailAccount".to_vec(), + b"person".to_vec(), + b"customUserClass".to_vec(), + ] + },] + }), + make_search_success() + ]) + ); + } + #[tokio::test] async fn test_search_both() { let mut mock = MockTestBackendHandler::new();