server: Fix implementation of attribute present filter
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

Instead of just doing a schema check, this actually looks for users that have a value for this attribute.
This commit is contained in:
Valentin Tolmer 2024-08-16 23:48:15 +02:00 committed by nitnelave
parent ee7f9c9f41
commit 09c5d9f925
6 changed files with 72 additions and 18 deletions

View file

@ -61,6 +61,7 @@ pub enum UserRequestFilter {
MemberOf(GroupName),
// Same, by id.
MemberOfId(GroupId),
CustomAttributePresent(AttributeName),
}
impl From<bool> for UserRequestFilter {
@ -85,6 +86,7 @@ pub enum GroupRequestFilter {
// Check if the group contains a user identified by uid.
Member(UserId),
AttributeEquality(AttributeName, Serialized),
CustomAttributePresent(AttributeName),
}
impl From<bool> for GroupRequestFilter {

View file

@ -228,10 +228,13 @@ 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(!matches!(
map_group_field(&field, schema),
GroupFieldType::NoMatch
)))
Ok(match map_group_field(&field, schema) {
GroupFieldType::Attribute(name, _, _) => {
GroupRequestFilter::CustomAttributePresent(name)
}
GroupFieldType::NoMatch => GroupRequestFilter::from(false),
_ => GroupRequestFilter::from(true),
})
}
LdapFilter::Substring(field, substring_filter) => {
let field = AttributeName::from(field.as_str());

View file

@ -250,13 +250,13 @@ fn convert_user_filter(
}
LdapFilter::Present(field) => {
let field = AttributeName::from(field.as_str());
// Check that it's a field we support.
Ok(UserRequestFilter::from(
field.as_str() == "objectclass"
|| field.as_str() == "dn"
|| field.as_str() == "distinguishedname"
|| !matches!(map_user_field(&field, schema), UserFieldType::NoMatch),
))
Ok(match map_user_field(&field, schema) {
UserFieldType::Attribute(name, _, _) => {
UserRequestFilter::CustomAttributePresent(name)
}
UserFieldType::NoMatch => UserRequestFilter::from(false),
_ => UserRequestFilter::from(true),
})
}
LdapFilter::Substring(field, substring_filter) => {
let field = AttributeName::from(field.as_str());

View file

@ -16,14 +16,18 @@ use sea_orm::{
};
use tracing::instrument;
fn attribute_condition(name: AttributeName, value: Serialized) -> Cond {
fn attribute_condition(name: AttributeName, value: Option<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))
.filter(
value
.map(|value| model::GroupAttributesColumn::Value.eq(value))
.unwrap_or_else(|| SimpleExpr::Value(true.into())),
)
.into_query(),
)
.into_condition()
@ -71,7 +75,8 @@ fn get_group_filter_expr(filter: GroupRequestFilter) -> Cond {
))))
.like(filter.to_sql_filter())
.into_condition(),
AttributeEquality(name, value) => attribute_condition(name, value),
AttributeEquality(name, value) => attribute_condition(name, Some(value)),
CustomAttributePresent(name) => attribute_condition(name, None),
}
}

View file

@ -175,7 +175,9 @@ impl SqlBackendHandler {
mod tests {
use super::*;
use crate::domain::{
handler::AttributeList, sql_backend_handler::tests::*, types::AttributeType,
handler::{AttributeList, UpdateUserRequest, UserBackendHandler, UserRequestFilter},
sql_backend_handler::tests::*,
types::{AttributeType, AttributeValue, Serialized},
};
use pretty_assertions::assert_eq;
@ -268,6 +270,43 @@ mod tests {
.contains(&expected_value));
}
#[tokio::test]
async fn test_user_attribute_present_filter() {
let fixture = TestFixture::new().await;
let new_attribute = CreateAttributeRequest {
name: "new_attribute".into(),
attribute_type: AttributeType::Integer,
is_list: true,
is_visible: false,
is_editable: false,
};
fixture
.handler
.add_user_attribute(new_attribute)
.await
.unwrap();
fixture
.handler
.update_user(UpdateUserRequest {
user_id: "bob".into(),
insert_attributes: vec![AttributeValue {
name: "new_attribute".into(),
value: Serialized::from(&3),
}],
..Default::default()
})
.await
.unwrap();
let users = get_user_names(
&fixture.handler,
Some(UserRequestFilter::CustomAttributePresent(
"new_attribute".into(),
)),
)
.await;
assert_eq!(users, vec!["bob"]);
}
#[tokio::test]
async fn test_group_attribute_add_and_delete() {
let fixture = TestFixture::new().await;

View file

@ -22,14 +22,18 @@ use sea_orm::{
use std::collections::HashSet;
use tracing::instrument;
fn attribute_condition(name: AttributeName, value: Serialized) -> Cond {
fn attribute_condition(name: AttributeName, value: Option<Serialized>) -> Cond {
Expr::in_subquery(
Expr::col(UserColumn::UserId.as_column_ref()),
model::UserAttributes::find()
.select_only()
.column(model::UserAttributesColumn::UserId)
.filter(model::UserAttributesColumn::AttributeName.eq(name))
.filter(model::UserAttributesColumn::Value.eq(value))
.filter(
value
.map(|value| model::UserAttributesColumn::Value.eq(value))
.unwrap_or_else(|| SimpleExpr::Constant(true.into())),
)
.into_query(),
)
.into_condition()
@ -79,7 +83,7 @@ fn get_user_filter_expr(filter: UserRequestFilter) -> Cond {
ColumnTrait::eq(&column, value).into_condition()
}
}
AttributeEquality(column, value) => attribute_condition(column, value),
AttributeEquality(column, value) => attribute_condition(column, Some(value)),
MemberOf(group) => user_id_subcondition(
Expr::col((group_table, GroupColumn::LowercaseDisplayName))
.eq(group.as_str().to_lowercase())
@ -98,6 +102,7 @@ fn get_user_filter_expr(filter: UserRequestFilter) -> Cond {
.like(filter.to_sql_filter())
.into_condition()
}
CustomAttributePresent(name) => attribute_condition(name, None),
}
}