mirror of
https://github.com/lldap/lldap.git
synced 2024-10-23 04:16:24 +00:00
server: clean up the attributes, relax the substring filter conditions
This consolidates both user and group attributes in their map_{user,group}_attribute as the only point of parsing. It adds support for custom attribute filters for groups, and makes a SubString filter on an unknown attribute resolve to just false.
This commit is contained in:
parent
4adb636d53
commit
bd0a58b476
7 changed files with 283 additions and 163 deletions
|
@ -83,6 +83,7 @@ pub enum GroupRequestFilter {
|
|||
GroupId(GroupId),
|
||||
// Check if the group contains a user identified by uid.
|
||||
Member(UserId),
|
||||
AttributeEquality(AttributeName, Serialized),
|
||||
}
|
||||
|
||||
impl From<bool> for GroupRequestFilter {
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use chrono::TimeZone;
|
||||
use ldap3_proto::{
|
||||
proto::LdapOp, LdapFilter, LdapPartialAttribute, LdapResultCode, LdapSearchResultEntry,
|
||||
};
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
use crate::domain::{
|
||||
deserialize::deserialize_attribute_value,
|
||||
handler::{GroupListerBackendHandler, GroupRequestFilter},
|
||||
ldap::error::LdapError,
|
||||
schema::{PublicSchema, SchemaGroupAttributeExtractor},
|
||||
types::{AttributeName, Group, UserId, Uuid},
|
||||
types::{AttributeName, AttributeType, Group, UserId, Uuid},
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -27,21 +29,29 @@ pub fn get_group_attribute(
|
|||
schema: &PublicSchema,
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let attribute = AttributeName::from(attribute);
|
||||
let attribute_values = match attribute.as_str() {
|
||||
"objectclass" => vec![b"groupOfUniqueNames".to_vec()],
|
||||
let attribute_values = match map_group_field(&attribute, schema) {
|
||||
GroupFieldType::ObjectClass => vec![b"groupOfUniqueNames".to_vec()],
|
||||
// Always returned as part of the base response.
|
||||
"dn" | "distinguishedname" => return None,
|
||||
"entrydn" => {
|
||||
GroupFieldType::Dn => return None,
|
||||
GroupFieldType::EntryDn => {
|
||||
vec![format!("uid={},ou=groups,{}", group.display_name, base_dn_str).into_bytes()]
|
||||
}
|
||||
"cn" | "uid" | "id" => vec![group.display_name.to_string().into_bytes()],
|
||||
"entryuuid" | "uuid" => vec![group.uuid.to_string().into_bytes()],
|
||||
"member" | "uniquemember" => group
|
||||
GroupFieldType::DisplayName => vec![group.display_name.to_string().into_bytes()],
|
||||
GroupFieldType::CreationDate => vec![chrono::Utc
|
||||
.from_utc_datetime(&group.creation_date)
|
||||
.to_rfc3339()
|
||||
.into_bytes()],
|
||||
GroupFieldType::Member => group
|
||||
.users
|
||||
.iter()
|
||||
.filter(|u| user_filter.as_ref().map(|f| *u == f).unwrap_or(true))
|
||||
.map(|u| format!("uid={},ou=people,{}", u, base_dn_str).into_bytes())
|
||||
.collect(),
|
||||
GroupFieldType::Uuid => vec![group.uuid.to_string().into_bytes()],
|
||||
GroupFieldType::Attribute(attr, _, _) => {
|
||||
get_custom_attribute::<SchemaGroupAttributeExtractor>(&group.attributes, &attr, schema)?
|
||||
}
|
||||
GroupFieldType::NoMatch => match attribute.as_str() {
|
||||
"1.1" => return None,
|
||||
// We ignore the operational attribute wildcard
|
||||
"+" => return None,
|
||||
|
@ -52,22 +62,20 @@ pub fn get_group_attribute(
|
|||
)
|
||||
}
|
||||
_ => {
|
||||
if !ignored_group_attributes.contains(&attribute) {
|
||||
match get_custom_attribute::<SchemaGroupAttributeExtractor>(
|
||||
if ignored_group_attributes.contains(&attribute) {
|
||||
return None;
|
||||
}
|
||||
get_custom_attribute::<SchemaGroupAttributeExtractor>(
|
||||
&group.attributes,
|
||||
&attribute,
|
||||
schema,
|
||||
) {
|
||||
Some(v) => return Some(v),
|
||||
None => warn!(
|
||||
).or_else(||{warn!(
|
||||
r#"Ignoring unrecognized group attribute: {}\n\
|
||||
To disable this warning, add it to "ignored_group_attributes" in the config."#,
|
||||
attribute
|
||||
),
|
||||
};
|
||||
}
|
||||
return None;
|
||||
);None})?
|
||||
}
|
||||
},
|
||||
};
|
||||
if attribute_values.len() == 1 && attribute_values[0].is_empty() {
|
||||
None
|
||||
|
@ -121,6 +129,20 @@ fn make_ldap_search_group_result_entry(
|
|||
}
|
||||
}
|
||||
|
||||
fn get_group_attribute_equality_filter(
|
||||
field: &AttributeName,
|
||||
typ: AttributeType,
|
||||
is_list: bool,
|
||||
value: &str,
|
||||
) -> LdapResult<GroupRequestFilter> {
|
||||
deserialize_attribute_value(&[value.to_owned()], typ, is_list)
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::Other,
|
||||
message: format!("Invalid value for attribute {}: {}", field, e),
|
||||
})
|
||||
.map(|v| GroupRequestFilter::AttributeEquality(field.clone(), v))
|
||||
}
|
||||
|
||||
fn convert_group_filter(
|
||||
ldap_info: &LdapInfo,
|
||||
filter: &LdapFilter,
|
||||
|
@ -131,8 +153,15 @@ fn convert_group_filter(
|
|||
LdapFilter::Equality(field, value) => {
|
||||
let field = AttributeName::from(field.as_str());
|
||||
let value = value.to_ascii_lowercase();
|
||||
match field.as_str() {
|
||||
"member" | "uniquemember" => {
|
||||
match map_group_field(&field, schema) {
|
||||
GroupFieldType::DisplayName => Ok(GroupRequestFilter::DisplayName(value.into())),
|
||||
GroupFieldType::Uuid => Ok(GroupRequestFilter::Uuid(
|
||||
Uuid::try_from(value.as_str()).map_err(|e| LdapError {
|
||||
code: LdapResultCode::InappropriateMatching,
|
||||
message: format!("Invalid UUID: {:#}", e),
|
||||
})?,
|
||||
)),
|
||||
GroupFieldType::Member => {
|
||||
let user_name = get_user_id_from_distinguished_name(
|
||||
&value,
|
||||
&ldap_info.base_dn,
|
||||
|
@ -140,12 +169,13 @@ fn convert_group_filter(
|
|||
)?;
|
||||
Ok(GroupRequestFilter::Member(user_name))
|
||||
}
|
||||
"objectclass" => Ok(GroupRequestFilter::from(matches!(
|
||||
GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from(matches!(
|
||||
value.as_str(),
|
||||
"groupofuniquenames" | "groupofnames"
|
||||
))),
|
||||
"dn" => Ok(get_group_id_from_distinguished_name(
|
||||
value.to_ascii_lowercase().as_str(),
|
||||
GroupFieldType::Dn | GroupFieldType::EntryDn => {
|
||||
Ok(get_group_id_from_distinguished_name(
|
||||
value.as_str(),
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)
|
||||
|
@ -153,18 +183,9 @@ fn convert_group_filter(
|
|||
.unwrap_or_else(|_| {
|
||||
warn!("Invalid dn filter on group: {}", value);
|
||||
GroupRequestFilter::from(false)
|
||||
})),
|
||||
_ => match map_group_field(&field, schema) {
|
||||
GroupFieldType::DisplayName => {
|
||||
Ok(GroupRequestFilter::DisplayName(value.into()))
|
||||
}))
|
||||
}
|
||||
GroupFieldType::Uuid => Ok(GroupRequestFilter::Uuid(
|
||||
Uuid::try_from(value.as_str()).map_err(|e| LdapError {
|
||||
code: LdapResultCode::InappropriateMatching,
|
||||
message: format!("Invalid UUID: {:#}", e),
|
||||
})?,
|
||||
)),
|
||||
_ => {
|
||||
GroupFieldType::NoMatch => {
|
||||
if !ldap_info.ignored_group_attributes.contains(&field) {
|
||||
warn!(
|
||||
r#"Ignoring unknown group attribute "{}" in filter.\n\
|
||||
|
@ -174,7 +195,13 @@ fn convert_group_filter(
|
|||
}
|
||||
Ok(GroupRequestFilter::from(false))
|
||||
}
|
||||
},
|
||||
GroupFieldType::Attribute(field, typ, is_list) => {
|
||||
get_group_attribute_equality_filter(&field, typ, is_list, &value)
|
||||
}
|
||||
GroupFieldType::CreationDate => Err(LdapError {
|
||||
code: LdapResultCode::UnwillingToPerform,
|
||||
message: "Creation date filter for groups not supported".to_owned(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
LdapFilter::And(filters) => Ok(GroupRequestFilter::And(
|
||||
|
@ -186,12 +213,10 @@ fn convert_group_filter(
|
|||
LdapFilter::Not(filter) => Ok(GroupRequestFilter::Not(Box::new(rec(filter)?))),
|
||||
LdapFilter::Present(field) => {
|
||||
let field = AttributeName::from(field.as_str());
|
||||
Ok(GroupRequestFilter::from(
|
||||
field.as_str() == "objectclass"
|
||||
|| field.as_str() == "dn"
|
||||
|| field.as_str() == "distinguishedname"
|
||||
|| !matches!(map_group_field(&field, schema), GroupFieldType::NoMatch),
|
||||
))
|
||||
Ok(GroupRequestFilter::from(!matches!(
|
||||
map_group_field(&field, schema),
|
||||
GroupFieldType::NoMatch
|
||||
)))
|
||||
}
|
||||
LdapFilter::Substring(field, substring_filter) => {
|
||||
let field = AttributeName::from(field.as_str());
|
||||
|
@ -199,6 +224,7 @@ fn convert_group_filter(
|
|||
GroupFieldType::DisplayName => Ok(GroupRequestFilter::DisplayNameSubString(
|
||||
substring_filter.clone().into(),
|
||||
)),
|
||||
GroupFieldType::NoMatch => Ok(GroupRequestFilter::from(false)),
|
||||
_ => Err(LdapError {
|
||||
code: LdapResultCode::UnwillingToPerform,
|
||||
message: format!(
|
||||
|
|
|
@ -27,52 +27,47 @@ pub fn get_user_attribute(
|
|||
schema: &PublicSchema,
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let attribute = AttributeName::from(attribute);
|
||||
let attribute_values = match attribute.as_str() {
|
||||
"objectclass" => vec![
|
||||
let attribute_values = match map_user_field(&attribute, schema) {
|
||||
UserFieldType::ObjectClass => vec![
|
||||
b"inetOrgPerson".to_vec(),
|
||||
b"posixAccount".to_vec(),
|
||||
b"mailAccount".to_vec(),
|
||||
b"person".to_vec(),
|
||||
],
|
||||
// dn is always returned as part of the base response.
|
||||
"dn" | "distinguishedname" => return None,
|
||||
"entrydn" => {
|
||||
UserFieldType::Dn => return None,
|
||||
UserFieldType::EntryDn => {
|
||||
vec![format!("uid={},ou=people,{}", &user.user_id, base_dn_str).into_bytes()]
|
||||
}
|
||||
"uid" | "user_id" | "id" => vec![user.user_id.to_string().into_bytes()],
|
||||
"entryuuid" | "uuid" => vec![user.uuid.to_string().into_bytes()],
|
||||
"mail" | "email" => vec![user.email.to_string().into_bytes()],
|
||||
"givenname" | "first_name" | "firstname" => {
|
||||
get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
&"first_name".into(),
|
||||
schema,
|
||||
)?
|
||||
}
|
||||
"sn" | "last_name" | "lastname" => get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
&"last_name".into(),
|
||||
schema,
|
||||
)?,
|
||||
"jpegphoto" | "avatar" => get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
&"avatar".into(),
|
||||
schema,
|
||||
)?,
|
||||
"memberof" => groups
|
||||
UserFieldType::MemberOf => groups
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|id_and_name| {
|
||||
format!("cn={},ou=groups,{}", &id_and_name.display_name, base_dn_str).into_bytes()
|
||||
})
|
||||
.collect(),
|
||||
"cn" | "displayname" => vec![user.display_name.clone()?.into_bytes()],
|
||||
"creationdate" | "creation_date" | "createtimestamp" | "modifytimestamp" => {
|
||||
vec![chrono::Utc
|
||||
UserFieldType::PrimaryField(UserColumn::UserId) => {
|
||||
vec![user.user_id.to_string().into_bytes()]
|
||||
}
|
||||
UserFieldType::PrimaryField(UserColumn::Email) => vec![user.email.to_string().into_bytes()],
|
||||
UserFieldType::PrimaryField(
|
||||
UserColumn::LowercaseEmail
|
||||
| UserColumn::PasswordHash
|
||||
| UserColumn::TotpSecret
|
||||
| UserColumn::MfaType,
|
||||
) => panic!("Should not get here"),
|
||||
UserFieldType::PrimaryField(UserColumn::Uuid) => vec![user.uuid.to_string().into_bytes()],
|
||||
UserFieldType::PrimaryField(UserColumn::DisplayName) => {
|
||||
vec![user.display_name.clone()?.into_bytes()]
|
||||
}
|
||||
UserFieldType::PrimaryField(UserColumn::CreationDate) => vec![chrono::Utc
|
||||
.from_utc_datetime(&user.creation_date)
|
||||
.to_rfc3339()
|
||||
.into_bytes()]
|
||||
.into_bytes()],
|
||||
UserFieldType::Attribute(attr, _, _) => {
|
||||
get_custom_attribute::<SchemaUserAttributeExtractor>(&user.attributes, &attr, schema)?
|
||||
}
|
||||
UserFieldType::NoMatch => match attribute.as_str() {
|
||||
"1.1" => return None,
|
||||
// We ignore the operational attribute wildcard.
|
||||
"+" => return None,
|
||||
|
@ -82,23 +77,25 @@ pub fn get_user_attribute(
|
|||
attribute
|
||||
)
|
||||
}
|
||||
attr => {
|
||||
if !ignored_user_attributes.contains(&attribute) {
|
||||
match get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
_ => {
|
||||
if ignored_user_attributes.contains(&attribute) {
|
||||
return None;
|
||||
}
|
||||
get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
&attribute,
|
||||
schema,
|
||||
) {
|
||||
Some(v) => return Some(v),
|
||||
None => warn!(
|
||||
)
|
||||
.or_else(|| {
|
||||
warn!(
|
||||
r#"Ignoring unrecognized group attribute: {}\n\
|
||||
To disable this warning, add it to "ignored_user_attributes" in the config."#,
|
||||
attr
|
||||
),
|
||||
};
|
||||
}
|
||||
return None;
|
||||
attribute
|
||||
);
|
||||
None
|
||||
})?
|
||||
}
|
||||
},
|
||||
};
|
||||
if attribute_values.len() == 1 && attribute_values[0].is_empty() {
|
||||
None
|
||||
|
@ -181,37 +178,14 @@ 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());
|
||||
match field.as_str() {
|
||||
"memberof" => Ok(UserRequestFilter::MemberOf(
|
||||
get_group_id_from_distinguished_name(
|
||||
&value.to_ascii_lowercase(),
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)?,
|
||||
)),
|
||||
"objectclass" => Ok(UserRequestFilter::from(matches!(
|
||||
value.to_ascii_lowercase().as_str(),
|
||||
"person" | "inetorgperson" | "posixaccount" | "mailaccount"
|
||||
))),
|
||||
"dn" => Ok(get_user_id_from_distinguished_name(
|
||||
value.to_ascii_lowercase().as_str(),
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)
|
||||
.map(UserRequestFilter::UserId)
|
||||
.unwrap_or_else(|_| {
|
||||
warn!("Invalid dn filter on user: {}", value);
|
||||
UserRequestFilter::from(false)
|
||||
})),
|
||||
_ => match map_user_field(&field, schema) {
|
||||
let value = value.to_ascii_lowercase();
|
||||
match map_user_field(&field, schema) {
|
||||
UserFieldType::PrimaryField(UserColumn::UserId) => {
|
||||
Ok(UserRequestFilter::UserId(UserId::new(value)))
|
||||
}
|
||||
UserFieldType::PrimaryField(field) => {
|
||||
Ok(UserRequestFilter::Equality(field, value.clone()))
|
||||
Ok(UserRequestFilter::UserId(UserId::new(&value)))
|
||||
}
|
||||
UserFieldType::PrimaryField(field) => Ok(UserRequestFilter::Equality(field, value)),
|
||||
UserFieldType::Attribute(field, typ, is_list) => {
|
||||
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,7 +197,29 @@ fn convert_user_filter(
|
|||
}
|
||||
Ok(UserRequestFilter::from(false))
|
||||
}
|
||||
},
|
||||
UserFieldType::ObjectClass => Ok(UserRequestFilter::from(matches!(
|
||||
value.as_str(),
|
||||
"person" | "inetorgperson" | "posixaccount" | "mailaccount"
|
||||
))),
|
||||
UserFieldType::MemberOf => Ok(UserRequestFilter::MemberOf(
|
||||
get_group_id_from_distinguished_name(
|
||||
&value,
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)?,
|
||||
)),
|
||||
UserFieldType::EntryDn | UserFieldType::Dn => {
|
||||
Ok(get_user_id_from_distinguished_name(
|
||||
value.as_str(),
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)
|
||||
.map(UserRequestFilter::UserId)
|
||||
.unwrap_or_else(|_| {
|
||||
warn!("Invalid dn filter on user: {}", value);
|
||||
UserRequestFilter::from(false)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
LdapFilter::Present(field) => {
|
||||
|
@ -242,8 +238,11 @@ fn convert_user_filter(
|
|||
UserFieldType::PrimaryField(UserColumn::UserId) => Ok(
|
||||
UserRequestFilter::UserIdSubString(substring_filter.clone().into()),
|
||||
),
|
||||
UserFieldType::NoMatch
|
||||
| UserFieldType::Attribute(_, _, _)
|
||||
UserFieldType::Attribute(_, _, _)
|
||||
| UserFieldType::ObjectClass
|
||||
| UserFieldType::MemberOf
|
||||
| UserFieldType::Dn
|
||||
| UserFieldType::EntryDn
|
||||
| UserFieldType::PrimaryField(UserColumn::CreationDate)
|
||||
| UserFieldType::PrimaryField(UserColumn::Uuid) => Err(LdapError {
|
||||
code: LdapResultCode::UnwillingToPerform,
|
||||
|
@ -252,6 +251,7 @@ fn convert_user_filter(
|
|||
field
|
||||
),
|
||||
}),
|
||||
UserFieldType::NoMatch => Ok(UserRequestFilter::from(false)),
|
||||
UserFieldType::PrimaryField(field) => Ok(UserRequestFilter::SubString(
|
||||
field,
|
||||
substring_filter.clone().into(),
|
||||
|
|
|
@ -158,12 +158,20 @@ pub fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)])
|
|||
|
||||
pub enum UserFieldType {
|
||||
NoMatch,
|
||||
ObjectClass,
|
||||
MemberOf,
|
||||
Dn,
|
||||
EntryDn,
|
||||
PrimaryField(UserColumn),
|
||||
Attribute(AttributeName, AttributeType, bool),
|
||||
}
|
||||
|
||||
pub fn map_user_field(field: &AttributeName, schema: &PublicSchema) -> UserFieldType {
|
||||
match field.as_str() {
|
||||
"memberof" | "ismemberof" => UserFieldType::MemberOf,
|
||||
"objectclass" => UserFieldType::ObjectClass,
|
||||
"dn" | "distinguishedname" => UserFieldType::Dn,
|
||||
"entrydn" => UserFieldType::EntryDn,
|
||||
"uid" | "user_id" | "id" => UserFieldType::PrimaryField(UserColumn::UserId),
|
||||
"mail" | "email" => UserFieldType::PrimaryField(UserColumn::Email),
|
||||
"cn" | "displayname" | "display_name" => {
|
||||
|
@ -201,16 +209,25 @@ pub enum GroupFieldType {
|
|||
NoMatch,
|
||||
DisplayName,
|
||||
CreationDate,
|
||||
ObjectClass,
|
||||
Dn,
|
||||
// Like Dn, but returned as part of the attributes.
|
||||
EntryDn,
|
||||
Member,
|
||||
Uuid,
|
||||
Attribute(AttributeName, AttributeType, bool),
|
||||
}
|
||||
|
||||
pub fn map_group_field(field: &AttributeName, schema: &PublicSchema) -> GroupFieldType {
|
||||
match field.as_str() {
|
||||
"cn" | "displayname" | "uid" | "display_name" => GroupFieldType::DisplayName,
|
||||
"dn" | "distinguishedname" => GroupFieldType::Dn,
|
||||
"entrydn" => GroupFieldType::EntryDn,
|
||||
"objectclass" => GroupFieldType::ObjectClass,
|
||||
"cn" | "displayname" | "uid" | "display_name" | "id" => GroupFieldType::DisplayName,
|
||||
"creationdate" | "createtimestamp" | "modifytimestamp" | "creation_date" => {
|
||||
GroupFieldType::CreationDate
|
||||
}
|
||||
"member" | "uniquemember" => GroupFieldType::Member,
|
||||
"entryuuid" | "uuid" => GroupFieldType::Uuid,
|
||||
_ => schema
|
||||
.get_schema()
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::domain::{
|
|||
},
|
||||
model::{self, GroupColumn, MembershipColumn},
|
||||
sql_backend_handler::SqlBackendHandler,
|
||||
types::{AttributeValue, Group, GroupDetails, GroupId, Uuid},
|
||||
types::{AttributeName, AttributeValue, Group, GroupDetails, GroupId, Serialized, Uuid},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use sea_orm::{
|
||||
|
@ -16,6 +16,19 @@ use sea_orm::{
|
|||
};
|
||||
use tracing::instrument;
|
||||
|
||||
fn attribute_condition(name: AttributeName, value: Serialized) -> Cond {
|
||||
Expr::in_subquery(
|
||||
Expr::col(GroupColumn::GroupId.as_column_ref()),
|
||||
model::GroupAttributes::find()
|
||||
.select_only()
|
||||
.column(model::GroupAttributesColumn::GroupId)
|
||||
.filter(model::GroupAttributesColumn::AttributeName.eq(name))
|
||||
.filter(model::GroupAttributesColumn::Value.eq(value))
|
||||
.into_query(),
|
||||
)
|
||||
.into_condition()
|
||||
}
|
||||
|
||||
fn get_group_filter_expr(filter: GroupRequestFilter) -> Cond {
|
||||
use GroupRequestFilter::*;
|
||||
let group_table = Alias::new("groups");
|
||||
|
@ -58,6 +71,7 @@ fn get_group_filter_expr(filter: GroupRequestFilter) -> Cond {
|
|||
))))
|
||||
.like(filter.to_sql_filter())
|
||||
.into_condition(),
|
||||
AttributeEquality(name, value) => attribute_condition(name, value),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,6 +419,46 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_groups_other_filter() {
|
||||
let fixture = TestFixture::new().await;
|
||||
fixture
|
||||
.handler
|
||||
.add_group_attribute(CreateAttributeRequest {
|
||||
name: "gid".into(),
|
||||
attribute_type: AttributeType::Integer,
|
||||
is_list: false,
|
||||
is_visible: true,
|
||||
is_editable: true,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
fixture
|
||||
.handler
|
||||
.update_group(UpdateGroupRequest {
|
||||
group_id: fixture.groups[0],
|
||||
display_name: None,
|
||||
delete_attributes: Vec::new(),
|
||||
insert_attributes: vec![AttributeValue {
|
||||
name: "gid".into(),
|
||||
value: Serialized::from(&512),
|
||||
}],
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
get_group_ids(
|
||||
&fixture.handler,
|
||||
Some(GroupRequestFilter::AttributeEquality(
|
||||
AttributeName::from("gid"),
|
||||
Serialized::from(&512),
|
||||
)),
|
||||
)
|
||||
.await,
|
||||
vec![fixture.groups[0]]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_group_details() {
|
||||
let fixture = TestFixture::new().await;
|
||||
|
|
|
@ -70,6 +70,10 @@ impl RequestFilter {
|
|||
UserFieldType::Attribute(_, _, true) => {
|
||||
Err("Equality not supported for list fields".into())
|
||||
}
|
||||
UserFieldType::MemberOf => Ok(DomainRequestFilter::MemberOf(eq.value.into())),
|
||||
UserFieldType::ObjectClass | UserFieldType::Dn | UserFieldType::EntryDn => {
|
||||
Err("Ldap fields not supported in request filter".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
(None, Some(any), None, None, None, None) => Ok(DomainRequestFilter::Or(
|
||||
|
|
|
@ -1613,7 +1613,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_search_groups_unsupported_substring() {
|
||||
let mut ldap_handler = setup_bound_admin_handler(MockTestBackendHandler::new()).await;
|
||||
let mut ldap_handler = setup_bound_readonly_handler(MockTestBackendHandler::new()).await;
|
||||
let request = make_group_search_request(
|
||||
LdapFilter::Substring("member".to_owned(), LdapSubstringFilter::default()),
|
||||
vec!["cn"],
|
||||
|
@ -1627,6 +1627,24 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_groups_missing_attribute_substring() {
|
||||
let request = make_group_search_request(
|
||||
LdapFilter::Substring("nonexistent".to_owned(), LdapSubstringFilter::default()),
|
||||
vec!["cn"],
|
||||
);
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_list_groups()
|
||||
.with(eq(Some(false.into())))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(vec![]));
|
||||
let mut ldap_handler = setup_bound_readonly_handler(mock).await;
|
||||
assert_eq!(
|
||||
ldap_handler.do_search_or_dse(&request).await,
|
||||
Ok(vec![make_search_success()]),
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_groups_error() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
|
|
Loading…
Reference in a new issue