server: return custom attributes when asked for all attributes
Some checks failed
Rust / pre_job (push) Has been cancelled
Rust / cargo test (push) Has been cancelled
Rust / cargo clippy (push) Has been cancelled
Rust / cargo fmt (push) Has been cancelled
Rust / Code coverage (push) Has been cancelled

This commit is contained in:
Valentin Tolmer 2024-10-22 01:34:15 +02:00 committed by nitnelave
parent df188ee83f
commit a190fe7ddf
4 changed files with 210 additions and 180 deletions

View file

@ -7,31 +7,28 @@ use tracing::{debug, instrument, warn};
use crate::domain::{ use crate::domain::{
deserialize::deserialize_attribute_value, deserialize::deserialize_attribute_value,
handler::{GroupListerBackendHandler, GroupRequestFilter}, handler::{GroupListerBackendHandler, GroupRequestFilter},
ldap::error::LdapError, ldap::{
error::{LdapError, LdapResult},
utils::{
expand_attribute_wildcards, get_custom_attribute,
get_group_id_from_distinguished_name_or_plain_name,
get_user_id_from_distinguished_name_or_plain_name, map_group_field, ExpandedAttributes,
GroupFieldType, LdapInfo,
},
},
schema::{PublicSchema, SchemaGroupAttributeExtractor}, schema::{PublicSchema, SchemaGroupAttributeExtractor},
types::{AttributeName, AttributeType, Group, LdapObjectClass, UserId, Uuid}, types::{AttributeName, AttributeType, Group, LdapObjectClass, UserId, Uuid},
}; };
use super::{
error::LdapResult,
utils::{
expand_attribute_wildcards, get_custom_attribute,
get_group_id_from_distinguished_name_or_plain_name,
get_user_id_from_distinguished_name_or_plain_name, map_group_field, GroupFieldType,
LdapInfo,
},
};
pub fn get_group_attribute( pub fn get_group_attribute(
group: &Group, group: &Group,
base_dn_str: &str, base_dn_str: &str,
attribute: &str, attribute: &AttributeName,
user_filter: &Option<UserId>, user_filter: &Option<UserId>,
ignored_group_attributes: &[AttributeName], ignored_group_attributes: &[AttributeName],
schema: &PublicSchema, schema: &PublicSchema,
) -> Option<Vec<Vec<u8>>> { ) -> Option<Vec<Vec<u8>>> {
let attribute = AttributeName::from(attribute); let attribute_values = match map_group_field(attribute, schema) {
let attribute_values = match map_group_field(&attribute, schema) {
GroupFieldType::ObjectClass => { GroupFieldType::ObjectClass => {
let mut classes = vec![b"groupOfUniqueNames".to_vec()]; let mut classes = vec![b"groupOfUniqueNames".to_vec()];
classes.extend( classes.extend(
@ -74,12 +71,12 @@ pub fn get_group_attribute(
) )
} }
_ => { _ => {
if ignored_group_attributes.contains(&attribute) { if ignored_group_attributes.contains(attribute) {
return None; return None;
} }
get_custom_attribute::<SchemaGroupAttributeExtractor>( get_custom_attribute::<SchemaGroupAttributeExtractor>(
&group.attributes, &group.attributes,
&attribute, attribute,
schema, schema,
).or_else(||{warn!( ).or_else(||{warn!(
r#"Ignoring unrecognized group attribute: {}\n\ r#"Ignoring unrecognized group attribute: {}\n\
@ -105,33 +102,42 @@ const ALL_GROUP_ATTRIBUTE_KEYS: &[&str] = &[
"entryuuid", "entryuuid",
]; ];
fn expand_group_attribute_wildcards(attributes: &[String]) -> Vec<&str> { fn expand_group_attribute_wildcards(attributes: &[String]) -> ExpandedAttributes {
expand_attribute_wildcards(attributes, ALL_GROUP_ATTRIBUTE_KEYS) expand_attribute_wildcards(attributes, ALL_GROUP_ATTRIBUTE_KEYS)
} }
fn make_ldap_search_group_result_entry( fn make_ldap_search_group_result_entry(
group: Group, group: Group,
base_dn_str: &str, base_dn_str: &str,
expanded_attributes: &[&str], mut expanded_attributes: ExpandedAttributes,
user_filter: &Option<UserId>, user_filter: &Option<UserId>,
ignored_group_attributes: &[AttributeName], ignored_group_attributes: &[AttributeName],
schema: &PublicSchema, schema: &PublicSchema,
) -> LdapSearchResultEntry { ) -> LdapSearchResultEntry {
if expanded_attributes.include_custom_attributes {
expanded_attributes.attribute_keys.extend(
group
.attributes
.iter()
.map(|a| (a.name.clone(), a.name.to_string())),
);
}
LdapSearchResultEntry { LdapSearchResultEntry {
dn: format!("cn={},ou=groups,{}", group.display_name, base_dn_str), dn: format!("cn={},ou=groups,{}", group.display_name, base_dn_str),
attributes: expanded_attributes attributes: expanded_attributes
.iter() .attribute_keys
.filter_map(|a| { .into_iter()
.filter_map(|(attribute, name)| {
let values = get_group_attribute( let values = get_group_attribute(
&group, &group,
base_dn_str, base_dn_str,
a, &attribute,
user_filter, user_filter,
ignored_group_attributes, ignored_group_attributes,
schema, schema,
)?; )?;
Some(LdapPartialAttribute { Some(LdapPartialAttribute {
atype: a.to_string(), atype: name,
vals: values, vals: values,
}) })
}) })
@ -295,7 +301,7 @@ pub fn convert_groups_to_ldap_op<'a>(
LdapOp::SearchResultEntry(make_ldap_search_group_result_entry( LdapOp::SearchResultEntry(make_ldap_search_group_result_entry(
g, g,
&ldap_info.base_dn_str, &ldap_info.base_dn_str,
expanded_attributes.as_ref().unwrap(), expanded_attributes.clone().unwrap(),
user_filter, user_filter,
&ldap_info.ignored_group_attributes, &ldap_info.ignored_group_attributes,
schema, schema,

View file

@ -12,8 +12,8 @@ use crate::domain::{
utils::{ utils::{
expand_attribute_wildcards, get_custom_attribute, expand_attribute_wildcards, get_custom_attribute,
get_group_id_from_distinguished_name_or_plain_name, get_group_id_from_distinguished_name_or_plain_name,
get_user_id_from_distinguished_name_or_plain_name, map_user_field, LdapInfo, get_user_id_from_distinguished_name_or_plain_name, map_user_field, ExpandedAttributes,
UserFieldType, LdapInfo, UserFieldType,
}, },
}, },
schema::{PublicSchema, SchemaUserAttributeExtractor}, schema::{PublicSchema, SchemaUserAttributeExtractor},
@ -25,14 +25,13 @@ use crate::domain::{
pub fn get_user_attribute( pub fn get_user_attribute(
user: &User, user: &User,
attribute: &str, attribute: &AttributeName,
base_dn_str: &str, base_dn_str: &str,
groups: Option<&[GroupDetails]>, groups: Option<&[GroupDetails]>,
ignored_user_attributes: &[AttributeName], ignored_user_attributes: &[AttributeName],
schema: &PublicSchema, schema: &PublicSchema,
) -> Option<Vec<Vec<u8>>> { ) -> Option<Vec<Vec<u8>>> {
let attribute = AttributeName::from(attribute); let attribute_values = match map_user_field(attribute, schema) {
let attribute_values = match map_user_field(&attribute, schema) {
UserFieldType::ObjectClass => { UserFieldType::ObjectClass => {
let mut classes = vec![ let mut classes = vec![
b"inetOrgPerson".to_vec(), b"inetOrgPerson".to_vec(),
@ -93,12 +92,12 @@ pub fn get_user_attribute(
) )
} }
_ => { _ => {
if ignored_user_attributes.contains(&attribute) { if ignored_user_attributes.contains(attribute) {
return None; return None;
} }
get_custom_attribute::<SchemaUserAttributeExtractor>( get_custom_attribute::<SchemaUserAttributeExtractor>(
&user.attributes, &user.attributes,
&attribute, attribute,
schema, schema,
) )
.or_else(|| { .or_else(|| {
@ -134,27 +133,34 @@ const ALL_USER_ATTRIBUTE_KEYS: &[&str] = &[
fn make_ldap_search_user_result_entry( fn make_ldap_search_user_result_entry(
user: User, user: User,
base_dn_str: &str, base_dn_str: &str,
expanded_attributes: &[&str], mut expanded_attributes: ExpandedAttributes,
groups: Option<&[GroupDetails]>, groups: Option<&[GroupDetails]>,
ignored_user_attributes: &[AttributeName], ignored_user_attributes: &[AttributeName],
schema: &PublicSchema, schema: &PublicSchema,
) -> LdapSearchResultEntry { ) -> LdapSearchResultEntry {
let dn = format!("uid={},ou=people,{}", user.user_id.as_str(), base_dn_str); if expanded_attributes.include_custom_attributes {
expanded_attributes.attribute_keys.extend(
user.attributes
.iter()
.map(|a| (a.name.clone(), a.name.to_string())),
);
}
LdapSearchResultEntry { LdapSearchResultEntry {
dn, dn: format!("uid={},ou=people,{}", user.user_id.as_str(), base_dn_str),
attributes: expanded_attributes attributes: expanded_attributes
.iter() .attribute_keys
.filter_map(|a| { .into_iter()
.filter_map(|(attribute, name)| {
let values = get_user_attribute( let values = get_user_attribute(
&user, &user,
a, &attribute,
base_dn_str, base_dn_str,
groups, groups,
ignored_user_attributes, ignored_user_attributes,
schema, schema,
)?; )?;
Some(LdapPartialAttribute { Some(LdapPartialAttribute {
atype: a.to_string(), atype: name,
vals: values, vals: values,
}) })
}) })
@ -295,7 +301,7 @@ fn convert_user_filter(
} }
} }
fn expand_user_attribute_wildcards(attributes: &[String]) -> Vec<&str> { fn expand_user_attribute_wildcards(attributes: &[String]) -> ExpandedAttributes {
expand_attribute_wildcards(attributes, ALL_USER_ATTRIBUTE_KEYS) expand_attribute_wildcards(attributes, ALL_USER_ATTRIBUTE_KEYS)
} }
@ -334,7 +340,7 @@ pub fn convert_users_to_ldap_op<'a>(
LdapOp::SearchResultEntry(make_ldap_search_user_result_entry( LdapOp::SearchResultEntry(make_ldap_search_user_result_entry(
u.user, u.user,
&ldap_info.base_dn_str, &ldap_info.base_dn_str,
expanded_attributes.as_ref().unwrap(), expanded_attributes.clone().unwrap(),
u.groups.as_deref(), u.groups.as_deref(),
&ldap_info.ignored_user_attributes, &ldap_info.ignored_user_attributes,
schema, schema,

View file

@ -1,5 +1,6 @@
use std::collections::BTreeMap;
use chrono::{NaiveDateTime, TimeZone}; use chrono::{NaiveDateTime, TimeZone};
use itertools::Itertools;
use ldap3_proto::{proto::LdapSubstringFilter, LdapResultCode}; use ldap3_proto::{proto::LdapSubstringFilter, LdapResultCode};
use tracing::{debug, instrument, warn}; use tracing::{debug, instrument, warn};
@ -137,30 +138,39 @@ pub fn get_group_id_from_distinguished_name_or_plain_name(
} }
} }
#[derive(Clone)]
pub struct ExpandedAttributes {
// Lowercase name to original name.
pub attribute_keys: BTreeMap<AttributeName, String>,
pub include_custom_attributes: bool,
}
#[instrument(skip(all_attribute_keys), level = "debug")] #[instrument(skip(all_attribute_keys), level = "debug")]
pub fn expand_attribute_wildcards<'a>( pub fn expand_attribute_wildcards(
ldap_attributes: &'a [String], ldap_attributes: &[String],
all_attribute_keys: &'a [&'static str], all_attribute_keys: &[&'static str],
) -> Vec<&'a str> { ) -> ExpandedAttributes {
let extra_attributes = let mut include_custom_attributes = false;
let mut attributes_out: BTreeMap<_, _> = ldap_attributes
.iter()
.filter(|&s| s != "*" && s != "+" && s != "1.1")
.map(|s| (AttributeName::from(s), s.to_string()))
.collect();
attributes_out.extend(
if ldap_attributes.iter().any(|x| x == "*") || ldap_attributes.is_empty() { if ldap_attributes.iter().any(|x| x == "*") || ldap_attributes.is_empty() {
include_custom_attributes = true;
all_attribute_keys all_attribute_keys
} else { } else {
&[] &[]
} }
.iter() .iter()
.copied(); .map(|&s| (AttributeName::from(s), s.to_string())),
let attributes_out = ldap_attributes );
.iter() debug!(?attributes_out);
.map(|s| s.as_str()) ExpandedAttributes {
.filter(|&s| s != "*" && s != "+" && s != "1.1"); attribute_keys: attributes_out,
include_custom_attributes,
// Deduplicate, preserving order }
let resolved_attributes = itertools::chain(attributes_out, extra_attributes)
.unique_by(|a| a.to_ascii_lowercase())
.collect_vec();
debug!(?resolved_attributes);
resolved_attributes
} }
pub fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)]) -> bool { pub fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)]) -> bool {

View file

@ -775,13 +775,13 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
message: "Too many search results".to_string(), message: "Too many search results".to_string(),
}); });
} }
let requested_attribute = AttributeName::from(&request.atype);
match entries.first() { match entries.first() {
Some(LdapOp::SearchResultEntry(entry)) => { Some(LdapOp::SearchResultEntry(entry)) => {
let available = entry let available = entry.attributes.iter().any(|attr| {
.attributes AttributeName::from(&attr.atype) == requested_attribute
.iter() && attr.vals.contains(&request.val)
.any(|attr| attr.atype == request.atype && attr.vals.contains(&request.val)); });
Ok(vec![LdapOp::CompareResult(LdapResultOp { Ok(vec![LdapOp::CompareResult(LdapResultOp {
code: if available { code: if available {
LdapResultCode::CompareTrue LdapResultCode::CompareTrue
@ -1287,32 +1287,6 @@ mod tests {
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(), dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(),
attributes: vec![ 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(),
]
},
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"bob_1".to_vec()]
},
LdapPartialAttribute {
atype: "mail".to_string(),
vals: vec![b"bob@bobmail.bob".to_vec()]
},
LdapPartialAttribute {
atype: "givenName".to_string(),
vals: vec!["Bôb".to_string().into_bytes()]
},
LdapPartialAttribute {
atype: "sn".to_string(),
vals: vec!["Böbberson".to_string().into_bytes()]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "cn".to_string(), atype: "cn".to_string(),
vals: vec!["Bôb Böbberson".to_string().into_bytes()] vals: vec!["Bôb Böbberson".to_string().into_bytes()]
@ -1325,11 +1299,14 @@ mod tests {
atype: "entryUuid".to_string(), atype: "entryUuid".to_string(),
vals: vec![b"698e1d5f-7a40-3151-8745-b9b8a37839da".to_vec()] vals: vec![b"698e1d5f-7a40-3151-8745-b9b8a37839da".to_vec()]
}, },
], LdapPartialAttribute {
}), atype: "givenName".to_string(),
LdapOp::SearchResultEntry(LdapSearchResultEntry { vals: vec!["Bôb".to_string().into_bytes()]
dn: "uid=jim,ou=people,dc=example,dc=com".to_string(), },
attributes: vec![ LdapPartialAttribute {
atype: "mail".to_string(),
vals: vec![b"bob@bobmail.bob".to_vec()]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "objectClass".to_string(), atype: "objectClass".to_string(),
vals: vec![ vals: vec![
@ -1340,22 +1317,19 @@ mod tests {
b"customUserClass".to_vec(), b"customUserClass".to_vec(),
] ]
}, },
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"jim".to_vec()]
},
LdapPartialAttribute {
atype: "mail".to_string(),
vals: vec![b"jim@cricket.jim".to_vec()]
},
LdapPartialAttribute {
atype: "givenName".to_string(),
vals: vec![b"Jim".to_vec()]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "sn".to_string(), atype: "sn".to_string(),
vals: vec![b"Cricket".to_vec()] vals: vec!["Böbberson".to_string().into_bytes()]
}, },
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"bob_1".to_vec()]
},
],
}),
LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "uid=jim,ou=people,dc=example,dc=com".to_string(),
attributes: vec![
LdapPartialAttribute { LdapPartialAttribute {
atype: "cn".to_string(), atype: "cn".to_string(),
vals: vec![b"Jimminy Cricket".to_vec()] vals: vec![b"Jimminy Cricket".to_vec()]
@ -1368,10 +1342,36 @@ mod tests {
atype: "entryUuid".to_string(), atype: "entryUuid".to_string(),
vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()] vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()]
}, },
LdapPartialAttribute {
atype: "givenName".to_string(),
vals: vec![b"Jim".to_vec()]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "jpegPhoto".to_string(), atype: "jpegPhoto".to_string(),
vals: vec![JpegPhoto::for_tests().into_bytes()] vals: vec![JpegPhoto::for_tests().into_bytes()]
}, },
LdapPartialAttribute {
atype: "mail".to_string(),
vals: vec![b"jim@cricket.jim".to_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(),
]
},
LdapPartialAttribute {
atype: "sn".to_string(),
vals: vec![b"Cricket".to_vec()]
},
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"jim".to_vec()]
},
], ],
}), }),
make_search_success(), make_search_success(),
@ -1423,14 +1423,22 @@ mod tests {
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(), dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(),
attributes: vec![ attributes: vec![
LdapPartialAttribute {
atype: "objectClass".to_string(),
vals: vec![b"groupOfUniqueNames".to_vec(),]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "cn".to_string(), atype: "cn".to_string(),
vals: vec![b"group_1".to_vec()] vals: vec![b"group_1".to_vec()]
}, },
LdapPartialAttribute {
atype: "entryDN".to_string(),
vals: vec![b"uid=group_1,ou=groups,dc=example,dc=com".to_vec()],
},
LdapPartialAttribute {
atype: "entryUuid".to_string(),
vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()],
},
LdapPartialAttribute {
atype: "objectClass".to_string(),
vals: vec![b"groupOfUniqueNames".to_vec(),]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "uniqueMember".to_string(), atype: "uniqueMember".to_string(),
vals: vec![ vals: vec![
@ -1438,38 +1446,30 @@ mod tests {
b"uid=john,ou=people,dc=example,dc=com".to_vec(), b"uid=john,ou=people,dc=example,dc=com".to_vec(),
] ]
}, },
LdapPartialAttribute {
atype: "entryUuid".to_string(),
vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()],
},
LdapPartialAttribute {
atype: "entryDN".to_string(),
vals: vec![b"uid=group_1,ou=groups,dc=example,dc=com".to_vec()],
},
], ],
}), }),
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "cn=BestGroup,ou=groups,dc=example,dc=com".to_string(), dn: "cn=BestGroup,ou=groups,dc=example,dc=com".to_string(),
attributes: vec![ attributes: vec![
LdapPartialAttribute {
atype: "objectClass".to_string(),
vals: vec![b"groupOfUniqueNames".to_vec(),]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "cn".to_string(), atype: "cn".to_string(),
vals: vec![b"BestGroup".to_vec()] vals: vec![b"BestGroup".to_vec()]
}, },
LdapPartialAttribute { LdapPartialAttribute {
atype: "uniqueMember".to_string(), atype: "entryDN".to_string(),
vals: vec![b"uid=john,ou=people,dc=example,dc=com".to_vec()] vals: vec![b"uid=BestGroup,ou=groups,dc=example,dc=com".to_vec()],
}, },
LdapPartialAttribute { LdapPartialAttribute {
atype: "entryUuid".to_string(), atype: "entryUuid".to_string(),
vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()], vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()],
}, },
LdapPartialAttribute { LdapPartialAttribute {
atype: "entryDN".to_string(), atype: "objectClass".to_string(),
vals: vec![b"uid=BestGroup,ou=groups,dc=example,dc=com".to_vec()], vals: vec![b"groupOfUniqueNames".to_vec(),]
},
LdapPartialAttribute {
atype: "uniqueMember".to_string(),
vals: vec![b"uid=john,ou=people,dc=example,dc=com".to_vec()]
}, },
], ],
}), }),
@ -2028,6 +2028,10 @@ mod tests {
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(), dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(),
attributes: vec![ attributes: vec![
LdapPartialAttribute {
atype: "cn".to_string(),
vals: vec!["Bôb Böbberson".to_string().into_bytes()]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "objectClass".to_string(), atype: "objectClass".to_string(),
vals: vec![ vals: vec![
@ -2036,25 +2040,21 @@ mod tests {
b"mailAccount".to_vec(), b"mailAccount".to_vec(),
b"person".to_vec(), b"person".to_vec(),
b"customUserClass".to_vec(), b"customUserClass".to_vec(),
] ],
},
LdapPartialAttribute {
atype: "cn".to_string(),
vals: vec!["Bôb Böbberson".to_string().into_bytes()]
}, },
], ],
}), }),
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(), dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(),
attributes: vec![ attributes: vec![
LdapPartialAttribute {
atype: "objectClass".to_string(),
vals: vec![b"groupOfUniqueNames".to_vec(),]
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "cn".to_string(), atype: "cn".to_string(),
vals: vec![b"group_1".to_vec()] vals: vec![b"group_1".to_vec()]
}, },
LdapPartialAttribute {
atype: "objectClass".to_string(),
vals: vec![b"groupOfUniqueNames".to_vec(),]
},
], ],
}), }),
make_search_success(), make_search_success(),
@ -2114,35 +2114,13 @@ mod tests {
dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(), dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(),
attributes: vec![ attributes: vec![
LdapPartialAttribute { LdapPartialAttribute {
atype: "objectclass".to_string(), atype: "avatar".to_string(),
vals: vec![ vals: vec![JpegPhoto::for_tests().into_bytes()],
b"inetOrgPerson".to_vec(),
b"posixAccount".to_vec(),
b"mailAccount".to_vec(),
b"person".to_vec(),
b"customUserClass".to_vec(),
],
},
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"bob_1".to_vec()],
},
LdapPartialAttribute {
atype: "mail".to_string(),
vals: vec![b"bob@bobmail.bob".to_vec()],
},
LdapPartialAttribute {
atype: "sn".to_string(),
vals: vec!["Böbberson".to_string().into_bytes()],
}, },
LdapPartialAttribute { LdapPartialAttribute {
atype: "cn".to_string(), atype: "cn".to_string(),
vals: vec!["Bôb Böbberson".to_string().into_bytes()], vals: vec!["Bôb Böbberson".to_string().into_bytes()],
}, },
LdapPartialAttribute {
atype: "jpegPhoto".to_string(),
vals: vec![JpegPhoto::for_tests().into_bytes()],
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "createtimestamp".to_string(), atype: "createtimestamp".to_string(),
vals: vec![chrono::Utc vals: vec![chrono::Utc
@ -2155,25 +2133,50 @@ mod tests {
atype: "entryuuid".to_string(), atype: "entryuuid".to_string(),
vals: vec![b"b4ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()], vals: vec![b"b4ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()],
}, },
LdapPartialAttribute {
atype: "jpegPhoto".to_string(),
vals: vec![JpegPhoto::for_tests().into_bytes()],
},
LdapPartialAttribute {
atype: "last_name".to_string(),
vals: vec!["Böbberson".to_string().into_bytes()],
},
LdapPartialAttribute {
atype: "mail".to_string(),
vals: vec![b"bob@bobmail.bob".to_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(),
],
},
LdapPartialAttribute {
atype: "sn".to_string(),
vals: vec!["Böbberson".to_string().into_bytes()],
},
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"bob_1".to_vec()],
},
], ],
}), }),
// "objectclass", "dn", "uid", "cn", "member", "uniquemember" // "objectclass", "dn", "uid", "cn", "member", "uniquemember"
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(), dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(),
attributes: vec![ attributes: vec![
LdapPartialAttribute {
atype: "objectclass".to_string(),
vals: vec![b"groupOfUniqueNames".to_vec()],
},
// UID
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"group_1".to_vec()],
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "cn".to_string(), atype: "cn".to_string(),
vals: vec![b"group_1".to_vec()], vals: vec![b"group_1".to_vec()],
}, },
LdapPartialAttribute {
atype: "entryuuid".to_string(),
vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()],
},
//member / uniquemember : "uid={},ou=people,{}" //member / uniquemember : "uid={},ou=people,{}"
LdapPartialAttribute { LdapPartialAttribute {
atype: "member".to_string(), atype: "member".to_string(),
@ -2182,6 +2185,15 @@ mod tests {
b"uid=john,ou=people,dc=example,dc=com".to_vec(), b"uid=john,ou=people,dc=example,dc=com".to_vec(),
], ],
}, },
LdapPartialAttribute {
atype: "objectclass".to_string(),
vals: vec![b"groupOfUniqueNames".to_vec()],
},
// UID
LdapPartialAttribute {
atype: "uid".to_string(),
vals: vec![b"group_1".to_vec()],
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "uniquemember".to_string(), atype: "uniquemember".to_string(),
vals: vec![ vals: vec![
@ -2189,10 +2201,6 @@ mod tests {
b"uid=john,ou=people,dc=example,dc=com".to_vec(), b"uid=john,ou=people,dc=example,dc=com".to_vec(),
], ],
}, },
LdapPartialAttribute {
atype: "entryuuid".to_string(),
vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()],
},
], ],
}), }),
make_search_success(), make_search_success(),
@ -2924,27 +2932,27 @@ mod tests {
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "uid=test,ou=people,dc=example,dc=com".to_string(), dn: "uid=test,ou=people,dc=example,dc=com".to_string(),
attributes: vec![ attributes: vec![
LdapPartialAttribute {
atype: "uid".to_owned(),
vals: vec![b"test".to_vec()],
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "nickname".to_owned(), atype: "nickname".to_owned(),
vals: vec![b"Bob the Builder".to_vec()], vals: vec![b"Bob the Builder".to_vec()],
}, },
LdapPartialAttribute {
atype: "uid".to_owned(),
vals: vec![b"test".to_vec()],
},
], ],
}), }),
LdapOp::SearchResultEntry(LdapSearchResultEntry { LdapOp::SearchResultEntry(LdapSearchResultEntry {
dn: "cn=group,ou=groups,dc=example,dc=com".to_owned(), dn: "cn=group,ou=groups,dc=example,dc=com".to_owned(),
attributes: vec![ attributes: vec![
LdapPartialAttribute {
atype: "uid".to_owned(),
vals: vec![b"group".to_vec()],
},
LdapPartialAttribute { LdapPartialAttribute {
atype: "club_name".to_owned(), atype: "club_name".to_owned(),
vals: vec![b"Breakfast Club".to_vec()], vals: vec![b"Breakfast Club".to_vec()],
}, },
LdapPartialAttribute {
atype: "uid".to_owned(),
vals: vec![b"group".to_vec()],
},
], ],
}), }),
make_search_success() make_search_success()