mirror of
https://github.com/lldap/lldap.git
synced 2026-04-05 14:48:10 +00:00
server: include preserved case in user attribute value search
Extends the generated UserRequestFilter with an OR'ed clause for the attribute value in both it's original case and lowercased.
This commit is contained in:
committed by
GitHub
parent
f5fbb31e6e
commit
0799b6bc26
@@ -154,12 +154,23 @@ fn get_group_attribute_equality_filter(
|
|||||||
is_list: bool,
|
is_list: bool,
|
||||||
value: &str,
|
value: &str,
|
||||||
) -> GroupRequestFilter {
|
) -> GroupRequestFilter {
|
||||||
deserialize_attribute_value(&[value.to_owned()], typ, is_list)
|
let value_lc = value.to_ascii_lowercase();
|
||||||
.map(|v| GroupRequestFilter::AttributeEquality(field.clone(), v))
|
let serialized_value = deserialize_attribute_value(&[value.to_owned()], typ, is_list);
|
||||||
.unwrap_or_else(|e| {
|
let serialized_value_lc = deserialize_attribute_value(&[value_lc.to_owned()], typ, is_list);
|
||||||
|
match (serialized_value, serialized_value_lc) {
|
||||||
|
(Ok(v), Ok(v_lc)) => GroupRequestFilter::Or(vec![
|
||||||
|
GroupRequestFilter::AttributeEquality(field.clone(), v),
|
||||||
|
GroupRequestFilter::AttributeEquality(field.clone(), v_lc),
|
||||||
|
]),
|
||||||
|
(Ok(_), Err(e)) => {
|
||||||
|
warn!("Invalid value for attribute {} (lowercased): {}", field, e);
|
||||||
|
GroupRequestFilter::from(false)
|
||||||
|
}
|
||||||
|
(Err(e), _) => {
|
||||||
warn!("Invalid value for attribute {}: {}", field, e);
|
warn!("Invalid value for attribute {}: {}", field, e);
|
||||||
GroupRequestFilter::from(false)
|
GroupRequestFilter::from(false)
|
||||||
})
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_group_filter(
|
fn convert_group_filter(
|
||||||
@@ -171,24 +182,24 @@ fn convert_group_filter(
|
|||||||
match filter {
|
match filter {
|
||||||
LdapFilter::Equality(field, value) => {
|
LdapFilter::Equality(field, value) => {
|
||||||
let field = AttributeName::from(field.as_str());
|
let field = AttributeName::from(field.as_str());
|
||||||
let value = value.to_ascii_lowercase();
|
let value_lc = value.to_ascii_lowercase();
|
||||||
match map_group_field(&field, schema) {
|
match map_group_field(&field, schema) {
|
||||||
GroupFieldType::GroupId => Ok(value
|
GroupFieldType::GroupId => Ok(value_lc
|
||||||
.parse::<i32>()
|
.parse::<i32>()
|
||||||
.map(|id| GroupRequestFilter::GroupId(GroupId(id)))
|
.map(|id| GroupRequestFilter::GroupId(GroupId(id)))
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
warn!("Given group id is not a valid integer: {}", value);
|
warn!("Given group id is not a valid integer: {}", value_lc);
|
||||||
GroupRequestFilter::from(false)
|
GroupRequestFilter::from(false)
|
||||||
})),
|
})),
|
||||||
GroupFieldType::DisplayName => Ok(GroupRequestFilter::DisplayName(value.into())),
|
GroupFieldType::DisplayName => Ok(GroupRequestFilter::DisplayName(value_lc.into())),
|
||||||
GroupFieldType::Uuid => Uuid::try_from(value.as_str())
|
GroupFieldType::Uuid => Uuid::try_from(value_lc.as_str())
|
||||||
.map(GroupRequestFilter::Uuid)
|
.map(GroupRequestFilter::Uuid)
|
||||||
.map_err(|e| LdapError {
|
.map_err(|e| LdapError {
|
||||||
code: LdapResultCode::Other,
|
code: LdapResultCode::Other,
|
||||||
message: format!("Invalid UUID: {:#}", e),
|
message: format!("Invalid UUID: {:#}", e),
|
||||||
}),
|
}),
|
||||||
GroupFieldType::Member => Ok(get_user_id_from_distinguished_name_or_plain_name(
|
GroupFieldType::Member => Ok(get_user_id_from_distinguished_name_or_plain_name(
|
||||||
&value,
|
&value_lc,
|
||||||
&ldap_info.base_dn,
|
&ldap_info.base_dn,
|
||||||
&ldap_info.base_dn_str,
|
&ldap_info.base_dn_str,
|
||||||
)
|
)
|
||||||
@@ -198,21 +209,21 @@ fn convert_group_filter(
|
|||||||
GroupRequestFilter::from(false)
|
GroupRequestFilter::from(false)
|
||||||
})),
|
})),
|
||||||
GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from(
|
GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from(
|
||||||
matches!(value.as_str(), "groupofuniquenames" | "groupofnames")
|
matches!(value_lc.as_str(), "groupofuniquenames" | "groupofnames")
|
||||||
|| schema
|
|| schema
|
||||||
.get_schema()
|
.get_schema()
|
||||||
.extra_group_object_classes
|
.extra_group_object_classes
|
||||||
.contains(&LdapObjectClass::from(value)),
|
.contains(&LdapObjectClass::from(value_lc)),
|
||||||
)),
|
)),
|
||||||
GroupFieldType::Dn | GroupFieldType::EntryDn => {
|
GroupFieldType::Dn | GroupFieldType::EntryDn => {
|
||||||
Ok(get_group_id_from_distinguished_name_or_plain_name(
|
Ok(get_group_id_from_distinguished_name_or_plain_name(
|
||||||
value.as_str(),
|
value_lc.as_str(),
|
||||||
&ldap_info.base_dn,
|
&ldap_info.base_dn,
|
||||||
&ldap_info.base_dn_str,
|
&ldap_info.base_dn_str,
|
||||||
)
|
)
|
||||||
.map(GroupRequestFilter::DisplayName)
|
.map(GroupRequestFilter::DisplayName)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
warn!("Invalid dn filter on group: {}", value);
|
warn!("Invalid dn filter on group: {}", value_lc);
|
||||||
GroupRequestFilter::from(false)
|
GroupRequestFilter::from(false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -227,7 +238,7 @@ fn convert_group_filter(
|
|||||||
Ok(GroupRequestFilter::from(false))
|
Ok(GroupRequestFilter::from(false))
|
||||||
}
|
}
|
||||||
GroupFieldType::Attribute(field, typ, is_list) => Ok(
|
GroupFieldType::Attribute(field, typ, is_list) => Ok(
|
||||||
get_group_attribute_equality_filter(&field, typ, is_list, &value),
|
get_group_attribute_equality_filter(&field, typ, is_list, value),
|
||||||
),
|
),
|
||||||
GroupFieldType::CreationDate => Err(LdapError {
|
GroupFieldType::CreationDate => Err(LdapError {
|
||||||
code: LdapResultCode::UnwillingToPerform,
|
code: LdapResultCode::UnwillingToPerform,
|
||||||
|
|||||||
@@ -174,12 +174,23 @@ fn get_user_attribute_equality_filter(
|
|||||||
is_list: bool,
|
is_list: bool,
|
||||||
value: &str,
|
value: &str,
|
||||||
) -> UserRequestFilter {
|
) -> UserRequestFilter {
|
||||||
deserialize_attribute_value(&[value.to_owned()], typ, is_list)
|
let value_lc = value.to_ascii_lowercase();
|
||||||
.map(|v| UserRequestFilter::AttributeEquality(field.clone(), v))
|
let serialized_value = deserialize_attribute_value(&[value.to_owned()], typ, is_list);
|
||||||
.unwrap_or_else(|e| {
|
let serialized_value_lc = deserialize_attribute_value(&[value_lc.to_owned()], typ, is_list);
|
||||||
|
match (serialized_value, serialized_value_lc) {
|
||||||
|
(Ok(v), Ok(v_lc)) => UserRequestFilter::Or(vec![
|
||||||
|
UserRequestFilter::AttributeEquality(field.clone(), v),
|
||||||
|
UserRequestFilter::AttributeEquality(field.clone(), v_lc),
|
||||||
|
]),
|
||||||
|
(Ok(_), Err(e)) => {
|
||||||
|
warn!("Invalid value for attribute {} (lowercased): {}", field, e);
|
||||||
|
UserRequestFilter::from(false)
|
||||||
|
}
|
||||||
|
(Err(e), _) => {
|
||||||
warn!("Invalid value for attribute {}: {}", field, e);
|
warn!("Invalid value for attribute {}: {}", field, e);
|
||||||
UserRequestFilter::from(false)
|
UserRequestFilter::from(false)
|
||||||
})
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_user_filter(
|
fn convert_user_filter(
|
||||||
@@ -198,18 +209,20 @@ fn convert_user_filter(
|
|||||||
LdapFilter::Not(filter) => Ok(UserRequestFilter::Not(Box::new(rec(filter)?))),
|
LdapFilter::Not(filter) => Ok(UserRequestFilter::Not(Box::new(rec(filter)?))),
|
||||||
LdapFilter::Equality(field, value) => {
|
LdapFilter::Equality(field, value) => {
|
||||||
let field = AttributeName::from(field.as_str());
|
let field = AttributeName::from(field.as_str());
|
||||||
let value = value.to_ascii_lowercase();
|
let value_lc = value.to_ascii_lowercase();
|
||||||
match map_user_field(&field, schema) {
|
match map_user_field(&field, schema) {
|
||||||
UserFieldType::PrimaryField(UserColumn::UserId) => {
|
UserFieldType::PrimaryField(UserColumn::UserId) => {
|
||||||
Ok(UserRequestFilter::UserId(UserId::new(&value)))
|
Ok(UserRequestFilter::UserId(UserId::new(&value_lc)))
|
||||||
}
|
}
|
||||||
UserFieldType::PrimaryField(UserColumn::Email) => Ok(UserRequestFilter::Equality(
|
UserFieldType::PrimaryField(UserColumn::Email) => Ok(UserRequestFilter::Equality(
|
||||||
UserColumn::LowercaseEmail,
|
UserColumn::LowercaseEmail,
|
||||||
value,
|
value_lc,
|
||||||
)),
|
)),
|
||||||
UserFieldType::PrimaryField(field) => Ok(UserRequestFilter::Equality(field, value)),
|
UserFieldType::PrimaryField(field) => {
|
||||||
|
Ok(UserRequestFilter::Equality(field, value_lc))
|
||||||
|
}
|
||||||
UserFieldType::Attribute(field, typ, is_list) => Ok(
|
UserFieldType::Attribute(field, typ, is_list) => Ok(
|
||||||
get_user_attribute_equality_filter(&field, typ, is_list, &value),
|
get_user_attribute_equality_filter(&field, typ, is_list, value),
|
||||||
),
|
),
|
||||||
UserFieldType::NoMatch => {
|
UserFieldType::NoMatch => {
|
||||||
if !ldap_info.ignored_user_attributes.contains(&field) {
|
if !ldap_info.ignored_user_attributes.contains(&field) {
|
||||||
@@ -223,15 +236,15 @@ fn convert_user_filter(
|
|||||||
}
|
}
|
||||||
UserFieldType::ObjectClass => Ok(UserRequestFilter::from(
|
UserFieldType::ObjectClass => Ok(UserRequestFilter::from(
|
||||||
matches!(
|
matches!(
|
||||||
value.as_str(),
|
value_lc.as_str(),
|
||||||
"person" | "inetorgperson" | "posixaccount" | "mailaccount"
|
"person" | "inetorgperson" | "posixaccount" | "mailaccount"
|
||||||
) || schema
|
) || schema
|
||||||
.get_schema()
|
.get_schema()
|
||||||
.extra_user_object_classes
|
.extra_user_object_classes
|
||||||
.contains(&LdapObjectClass::from(value)),
|
.contains(&LdapObjectClass::from(value_lc)),
|
||||||
)),
|
)),
|
||||||
UserFieldType::MemberOf => Ok(get_group_id_from_distinguished_name_or_plain_name(
|
UserFieldType::MemberOf => Ok(get_group_id_from_distinguished_name_or_plain_name(
|
||||||
&value,
|
&value_lc,
|
||||||
&ldap_info.base_dn,
|
&ldap_info.base_dn,
|
||||||
&ldap_info.base_dn_str,
|
&ldap_info.base_dn_str,
|
||||||
)
|
)
|
||||||
@@ -242,13 +255,13 @@ fn convert_user_filter(
|
|||||||
})),
|
})),
|
||||||
UserFieldType::EntryDn | UserFieldType::Dn => {
|
UserFieldType::EntryDn | UserFieldType::Dn => {
|
||||||
Ok(get_user_id_from_distinguished_name_or_plain_name(
|
Ok(get_user_id_from_distinguished_name_or_plain_name(
|
||||||
value.as_str(),
|
value_lc.as_str(),
|
||||||
&ldap_info.base_dn,
|
&ldap_info.base_dn,
|
||||||
&ldap_info.base_dn_str,
|
&ldap_info.base_dn_str,
|
||||||
)
|
)
|
||||||
.map(UserRequestFilter::UserId)
|
.map(UserRequestFilter::UserId)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
warn!("Invalid dn filter on user: {}", value);
|
warn!("Invalid dn filter on user: {}", value_lc);
|
||||||
UserRequestFilter::from(false)
|
UserRequestFilter::from(false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1659,6 +1659,74 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_search_groups_filter_3() {
|
||||||
|
let mut mock = MockTestBackendHandler::new();
|
||||||
|
mock.expect_list_groups()
|
||||||
|
.with(eq(Some(GroupRequestFilter::Or(vec![
|
||||||
|
GroupRequestFilter::AttributeEquality(
|
||||||
|
AttributeName::from("attr"),
|
||||||
|
Serialized::from("TEST"),
|
||||||
|
),
|
||||||
|
GroupRequestFilter::AttributeEquality(
|
||||||
|
AttributeName::from("attr"),
|
||||||
|
Serialized::from("test"),
|
||||||
|
),
|
||||||
|
]))))
|
||||||
|
.times(1)
|
||||||
|
.return_once(|_| {
|
||||||
|
Ok(vec![Group {
|
||||||
|
display_name: "group_1".into(),
|
||||||
|
id: GroupId(1),
|
||||||
|
creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
|
||||||
|
users: vec![],
|
||||||
|
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
|
||||||
|
attributes: vec![AttributeValue {
|
||||||
|
name: "Attr".into(),
|
||||||
|
value: Serialized::from("TEST"),
|
||||||
|
}],
|
||||||
|
}])
|
||||||
|
});
|
||||||
|
mock.expect_get_schema().returning(|| {
|
||||||
|
Ok(crate::domain::handler::Schema {
|
||||||
|
user_attributes: AttributeList {
|
||||||
|
attributes: Vec::new(),
|
||||||
|
},
|
||||||
|
group_attributes: AttributeList {
|
||||||
|
attributes: vec![AttributeSchema {
|
||||||
|
name: "Attr".into(),
|
||||||
|
attribute_type: AttributeType::String,
|
||||||
|
is_list: false,
|
||||||
|
is_visible: true,
|
||||||
|
is_editable: true,
|
||||||
|
is_hardcoded: false,
|
||||||
|
is_readonly: false,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
extra_user_object_classes: Vec::new(),
|
||||||
|
extra_group_object_classes: Vec::new(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
|
let request = make_group_search_request(
|
||||||
|
LdapFilter::Equality("Attr".to_string(), "TEST".to_string()),
|
||||||
|
vec!["cn"],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_search_or_dse(&request).await,
|
||||||
|
Ok(vec![
|
||||||
|
LdapOp::SearchResultEntry(LdapSearchResultEntry {
|
||||||
|
dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(),
|
||||||
|
attributes: vec![LdapPartialAttribute {
|
||||||
|
atype: "cn".to_string(),
|
||||||
|
vals: vec![b"group_1".to_vec()]
|
||||||
|
},],
|
||||||
|
}),
|
||||||
|
make_search_success(),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search_group_as_scope() {
|
async fn test_search_group_as_scope() {
|
||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
@@ -1789,10 +1857,16 @@ mod tests {
|
|||||||
true.into(),
|
true.into(),
|
||||||
true.into(),
|
true.into(),
|
||||||
false.into(),
|
false.into(),
|
||||||
UserRequestFilter::AttributeEquality(
|
UserRequestFilter::Or(vec![
|
||||||
AttributeName::from("first_name"),
|
UserRequestFilter::AttributeEquality(
|
||||||
Serialized::from("firstname"),
|
AttributeName::from("first_name"),
|
||||||
),
|
Serialized::from("FirstName"),
|
||||||
|
),
|
||||||
|
UserRequestFilter::AttributeEquality(
|
||||||
|
AttributeName::from("first_name"),
|
||||||
|
Serialized::from("firstname"),
|
||||||
|
),
|
||||||
|
]),
|
||||||
false.into(),
|
false.into(),
|
||||||
UserRequestFilter::UserIdSubString(SubStringFilter {
|
UserRequestFilter::UserIdSubString(SubStringFilter {
|
||||||
initial: Some("iNIt".to_owned()),
|
initial: Some("iNIt".to_owned()),
|
||||||
@@ -1833,7 +1907,7 @@ mod tests {
|
|||||||
LdapFilter::Present("objectClass".to_string()),
|
LdapFilter::Present("objectClass".to_string()),
|
||||||
LdapFilter::Present("uid".to_string()),
|
LdapFilter::Present("uid".to_string()),
|
||||||
LdapFilter::Present("unknown".to_string()),
|
LdapFilter::Present("unknown".to_string()),
|
||||||
LdapFilter::Equality("givenname".to_string(), "firstname".to_string()),
|
LdapFilter::Equality("givenname".to_string(), "FirstName".to_string()),
|
||||||
LdapFilter::Equality("unknown_attribute".to_string(), "randomValue".to_string()),
|
LdapFilter::Equality("unknown_attribute".to_string(), "randomValue".to_string()),
|
||||||
LdapFilter::Substring(
|
LdapFilter::Substring(
|
||||||
"uid".to_owned(),
|
"uid".to_owned(),
|
||||||
|
|||||||
Reference in New Issue
Block a user