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,
|
||||
value: &str,
|
||||
) -> GroupRequestFilter {
|
||||
deserialize_attribute_value(&[value.to_owned()], typ, is_list)
|
||||
.map(|v| GroupRequestFilter::AttributeEquality(field.clone(), v))
|
||||
.unwrap_or_else(|e| {
|
||||
let value_lc = value.to_ascii_lowercase();
|
||||
let serialized_value = deserialize_attribute_value(&[value.to_owned()], typ, is_list);
|
||||
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);
|
||||
GroupRequestFilter::from(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_group_filter(
|
||||
@@ -171,24 +182,24 @@ fn convert_group_filter(
|
||||
match filter {
|
||||
LdapFilter::Equality(field, value) => {
|
||||
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) {
|
||||
GroupFieldType::GroupId => Ok(value
|
||||
GroupFieldType::GroupId => Ok(value_lc
|
||||
.parse::<i32>()
|
||||
.map(|id| GroupRequestFilter::GroupId(GroupId(id)))
|
||||
.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)
|
||||
})),
|
||||
GroupFieldType::DisplayName => Ok(GroupRequestFilter::DisplayName(value.into())),
|
||||
GroupFieldType::Uuid => Uuid::try_from(value.as_str())
|
||||
GroupFieldType::DisplayName => Ok(GroupRequestFilter::DisplayName(value_lc.into())),
|
||||
GroupFieldType::Uuid => Uuid::try_from(value_lc.as_str())
|
||||
.map(GroupRequestFilter::Uuid)
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::Other,
|
||||
message: format!("Invalid UUID: {:#}", e),
|
||||
}),
|
||||
GroupFieldType::Member => Ok(get_user_id_from_distinguished_name_or_plain_name(
|
||||
&value,
|
||||
&value_lc,
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)
|
||||
@@ -198,21 +209,21 @@ fn convert_group_filter(
|
||||
GroupRequestFilter::from(false)
|
||||
})),
|
||||
GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from(
|
||||
matches!(value.as_str(), "groupofuniquenames" | "groupofnames")
|
||||
matches!(value_lc.as_str(), "groupofuniquenames" | "groupofnames")
|
||||
|| schema
|
||||
.get_schema()
|
||||
.extra_group_object_classes
|
||||
.contains(&LdapObjectClass::from(value)),
|
||||
.contains(&LdapObjectClass::from(value_lc)),
|
||||
)),
|
||||
GroupFieldType::Dn | GroupFieldType::EntryDn => {
|
||||
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_str,
|
||||
)
|
||||
.map(GroupRequestFilter::DisplayName)
|
||||
.unwrap_or_else(|_| {
|
||||
warn!("Invalid dn filter on group: {}", value);
|
||||
warn!("Invalid dn filter on group: {}", value_lc);
|
||||
GroupRequestFilter::from(false)
|
||||
}))
|
||||
}
|
||||
@@ -227,7 +238,7 @@ fn convert_group_filter(
|
||||
Ok(GroupRequestFilter::from(false))
|
||||
}
|
||||
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 {
|
||||
code: LdapResultCode::UnwillingToPerform,
|
||||
|
||||
@@ -174,12 +174,23 @@ fn get_user_attribute_equality_filter(
|
||||
is_list: bool,
|
||||
value: &str,
|
||||
) -> UserRequestFilter {
|
||||
deserialize_attribute_value(&[value.to_owned()], typ, is_list)
|
||||
.map(|v| UserRequestFilter::AttributeEquality(field.clone(), v))
|
||||
.unwrap_or_else(|e| {
|
||||
let value_lc = value.to_ascii_lowercase();
|
||||
let serialized_value = deserialize_attribute_value(&[value.to_owned()], typ, is_list);
|
||||
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);
|
||||
UserRequestFilter::from(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_user_filter(
|
||||
@@ -198,18 +209,20 @@ fn convert_user_filter(
|
||||
LdapFilter::Not(filter) => Ok(UserRequestFilter::Not(Box::new(rec(filter)?))),
|
||||
LdapFilter::Equality(field, value) => {
|
||||
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) {
|
||||
UserFieldType::PrimaryField(UserColumn::UserId) => {
|
||||
Ok(UserRequestFilter::UserId(UserId::new(&value)))
|
||||
Ok(UserRequestFilter::UserId(UserId::new(&value_lc)))
|
||||
}
|
||||
UserFieldType::PrimaryField(UserColumn::Email) => Ok(UserRequestFilter::Equality(
|
||||
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(
|
||||
get_user_attribute_equality_filter(&field, typ, is_list, &value),
|
||||
get_user_attribute_equality_filter(&field, typ, is_list, value),
|
||||
),
|
||||
UserFieldType::NoMatch => {
|
||||
if !ldap_info.ignored_user_attributes.contains(&field) {
|
||||
@@ -223,15 +236,15 @@ fn convert_user_filter(
|
||||
}
|
||||
UserFieldType::ObjectClass => Ok(UserRequestFilter::from(
|
||||
matches!(
|
||||
value.as_str(),
|
||||
value_lc.as_str(),
|
||||
"person" | "inetorgperson" | "posixaccount" | "mailaccount"
|
||||
) || schema
|
||||
.get_schema()
|
||||
.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(
|
||||
&value,
|
||||
&value_lc,
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)
|
||||
@@ -242,13 +255,13 @@ fn convert_user_filter(
|
||||
})),
|
||||
UserFieldType::EntryDn | UserFieldType::Dn => {
|
||||
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_str,
|
||||
)
|
||||
.map(UserRequestFilter::UserId)
|
||||
.unwrap_or_else(|_| {
|
||||
warn!("Invalid dn filter on user: {}", value);
|
||||
warn!("Invalid dn filter on user: {}", value_lc);
|
||||
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]
|
||||
async fn test_search_group_as_scope() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
@@ -1789,10 +1857,16 @@ mod tests {
|
||||
true.into(),
|
||||
true.into(),
|
||||
false.into(),
|
||||
UserRequestFilter::AttributeEquality(
|
||||
AttributeName::from("first_name"),
|
||||
Serialized::from("firstname"),
|
||||
),
|
||||
UserRequestFilter::Or(vec![
|
||||
UserRequestFilter::AttributeEquality(
|
||||
AttributeName::from("first_name"),
|
||||
Serialized::from("FirstName"),
|
||||
),
|
||||
UserRequestFilter::AttributeEquality(
|
||||
AttributeName::from("first_name"),
|
||||
Serialized::from("firstname"),
|
||||
),
|
||||
]),
|
||||
false.into(),
|
||||
UserRequestFilter::UserIdSubString(SubStringFilter {
|
||||
initial: Some("iNIt".to_owned()),
|
||||
@@ -1833,7 +1907,7 @@ mod tests {
|
||||
LdapFilter::Present("objectClass".to_string()),
|
||||
LdapFilter::Present("uid".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::Substring(
|
||||
"uid".to_owned(),
|
||||
|
||||
Reference in New Issue
Block a user