mirror of
				https://github.com/nitnelave/lldap.git
				synced 2023-04-12 14:25:13 +00:00 
			
		
		
		
	ldap: add support for memberOf attribute
The "memberOf" filter was already supported, but not the attribute. Fixes #179
This commit is contained in:
		
							parent
							
								
									1f632a8069
								
							
						
					
					
						commit
						5da01fce99
					
				@ -34,7 +34,7 @@ impl From<String> for UserId {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
 | 
					#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
 | 
					#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
 | 
				
			||||||
pub struct User {
 | 
					pub struct User {
 | 
				
			||||||
    pub user_id: UserId,
 | 
					    pub user_id: UserId,
 | 
				
			||||||
@ -134,9 +134,19 @@ pub struct GroupId(pub i32);
 | 
				
			|||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::FromRow)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::FromRow)]
 | 
				
			||||||
pub struct GroupIdAndName(pub GroupId, pub String);
 | 
					pub struct GroupIdAndName(pub GroupId, pub String);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq)]
 | 
				
			||||||
 | 
					pub struct UserAndGroups {
 | 
				
			||||||
 | 
					    pub user: User,
 | 
				
			||||||
 | 
					    pub groups: Option<Vec<GroupIdAndName>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[async_trait]
 | 
					#[async_trait]
 | 
				
			||||||
pub trait BackendHandler: Clone + Send {
 | 
					pub trait BackendHandler: Clone + Send {
 | 
				
			||||||
    async fn list_users(&self, filters: Option<UserRequestFilter>) -> Result<Vec<User>>;
 | 
					    async fn list_users(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        filters: Option<UserRequestFilter>,
 | 
				
			||||||
 | 
					        get_groups: bool,
 | 
				
			||||||
 | 
					    ) -> Result<Vec<UserAndGroups>>;
 | 
				
			||||||
    async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
					    async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
				
			||||||
    async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
					    async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
				
			||||||
    async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
					    async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
				
			||||||
@ -159,7 +169,7 @@ mockall::mock! {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    #[async_trait]
 | 
					    #[async_trait]
 | 
				
			||||||
    impl BackendHandler for TestBackendHandler {
 | 
					    impl BackendHandler for TestBackendHandler {
 | 
				
			||||||
        async fn list_users(&self, filters: Option<UserRequestFilter>) -> Result<Vec<User>>;
 | 
					        async fn list_users(&self, filters: Option<UserRequestFilter>, get_groups: bool) -> Result<Vec<UserAndGroups>>;
 | 
				
			||||||
        async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
					        async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
				
			||||||
        async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
					        async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
				
			||||||
        async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
					        async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ use crate::infra::configuration::Configuration;
 | 
				
			|||||||
use async_trait::async_trait;
 | 
					use async_trait::async_trait;
 | 
				
			||||||
use futures_util::StreamExt;
 | 
					use futures_util::StreamExt;
 | 
				
			||||||
use sea_query::{Expr, Iden, Order, Query, SimpleExpr};
 | 
					use sea_query::{Expr, Iden, Order, Query, SimpleExpr};
 | 
				
			||||||
use sqlx::Row;
 | 
					use sqlx::{FromRow, Row};
 | 
				
			||||||
use std::collections::HashSet;
 | 
					use std::collections::HashSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
@ -109,7 +109,11 @@ fn get_group_filter_expr(filter: GroupRequestFilter) -> SimpleExpr {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[async_trait]
 | 
					#[async_trait]
 | 
				
			||||||
impl BackendHandler for SqlBackendHandler {
 | 
					impl BackendHandler for SqlBackendHandler {
 | 
				
			||||||
    async fn list_users(&self, filters: Option<UserRequestFilter>) -> Result<Vec<User>> {
 | 
					    async fn list_users(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        filters: Option<UserRequestFilter>,
 | 
				
			||||||
 | 
					        get_groups: bool,
 | 
				
			||||||
 | 
					    ) -> Result<Vec<UserAndGroups>> {
 | 
				
			||||||
        let query = {
 | 
					        let query = {
 | 
				
			||||||
            let mut query_builder = Query::select()
 | 
					            let mut query_builder = Query::select()
 | 
				
			||||||
                .column((Users::Table, Users::UserId))
 | 
					                .column((Users::Table, Users::UserId))
 | 
				
			||||||
@ -122,6 +126,26 @@ impl BackendHandler for SqlBackendHandler {
 | 
				
			|||||||
                .from(Users::Table)
 | 
					                .from(Users::Table)
 | 
				
			||||||
                .order_by((Users::Table, Users::UserId), Order::Asc)
 | 
					                .order_by((Users::Table, Users::UserId), Order::Asc)
 | 
				
			||||||
                .to_owned();
 | 
					                .to_owned();
 | 
				
			||||||
 | 
					            let add_join_group_tables = |builder: &mut sea_query::SelectStatement| {
 | 
				
			||||||
 | 
					                builder
 | 
				
			||||||
 | 
					                    .left_join(
 | 
				
			||||||
 | 
					                        Memberships::Table,
 | 
				
			||||||
 | 
					                        Expr::tbl(Users::Table, Users::UserId)
 | 
				
			||||||
 | 
					                            .equals(Memberships::Table, Memberships::UserId),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    .left_join(
 | 
				
			||||||
 | 
					                        Groups::Table,
 | 
				
			||||||
 | 
					                        Expr::tbl(Memberships::Table, Memberships::GroupId)
 | 
				
			||||||
 | 
					                            .equals(Groups::Table, Groups::GroupId),
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            if get_groups {
 | 
				
			||||||
 | 
					                add_join_group_tables(&mut query_builder);
 | 
				
			||||||
 | 
					                query_builder
 | 
				
			||||||
 | 
					                    .column((Groups::Table, Groups::GroupId))
 | 
				
			||||||
 | 
					                    .column((Groups::Table, Groups::DisplayName))
 | 
				
			||||||
 | 
					                    .order_by((Groups::Table, Groups::DisplayName), Order::Asc);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if let Some(filter) = filters {
 | 
					            if let Some(filter) = filters {
 | 
				
			||||||
                if filter == UserRequestFilter::Not(Box::new(UserRequestFilter::And(Vec::new()))) {
 | 
					                if filter == UserRequestFilter::Not(Box::new(UserRequestFilter::And(Vec::new()))) {
 | 
				
			||||||
                    return Ok(Vec::new());
 | 
					                    return Ok(Vec::new());
 | 
				
			||||||
@ -131,31 +155,48 @@ impl BackendHandler for SqlBackendHandler {
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    let (RequiresGroup(requires_group), condition) = get_user_filter_expr(filter);
 | 
					                    let (RequiresGroup(requires_group), condition) = get_user_filter_expr(filter);
 | 
				
			||||||
                    query_builder.and_where(condition);
 | 
					                    query_builder.and_where(condition);
 | 
				
			||||||
                    if requires_group {
 | 
					                    if requires_group && !get_groups {
 | 
				
			||||||
                        query_builder
 | 
					                        add_join_group_tables(&mut query_builder);
 | 
				
			||||||
                            .left_join(
 | 
					 | 
				
			||||||
                                Memberships::Table,
 | 
					 | 
				
			||||||
                                Expr::tbl(Users::Table, Users::UserId)
 | 
					 | 
				
			||||||
                                    .equals(Memberships::Table, Memberships::UserId),
 | 
					 | 
				
			||||||
                            )
 | 
					 | 
				
			||||||
                            .left_join(
 | 
					 | 
				
			||||||
                                Groups::Table,
 | 
					 | 
				
			||||||
                                Expr::tbl(Memberships::Table, Memberships::GroupId)
 | 
					 | 
				
			||||||
                                    .equals(Groups::Table, Groups::GroupId),
 | 
					 | 
				
			||||||
                            );
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            query_builder.to_string(DbQueryBuilder {})
 | 
					            query_builder.to_string(DbQueryBuilder {})
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        log::error!("query: {}", &query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let results = sqlx::query_as::<_, User>(&query)
 | 
					        // For group_by.
 | 
				
			||||||
            .fetch(&self.sql_pool)
 | 
					        use itertools::Itertools;
 | 
				
			||||||
            .collect::<Vec<sqlx::Result<User>>>()
 | 
					        let mut users = Vec::new();
 | 
				
			||||||
            .await;
 | 
					        // The rows are returned sorted by user_id. We group them by
 | 
				
			||||||
 | 
					        // this key which gives us one element (`rows`) per group.
 | 
				
			||||||
 | 
					        for (_, rows) in &sqlx::query(&query)
 | 
				
			||||||
 | 
					            .fetch_all(&self.sql_pool)
 | 
				
			||||||
 | 
					            .await?
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .group_by(|row| row.get::<UserId, _>(&*Users::UserId.to_string()))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            let mut rows = rows.peekable();
 | 
				
			||||||
 | 
					            users.push(UserAndGroups {
 | 
				
			||||||
 | 
					                user: User::from_row(rows.peek().unwrap()).unwrap(),
 | 
				
			||||||
 | 
					                groups: if get_groups {
 | 
				
			||||||
 | 
					                    Some(
 | 
				
			||||||
 | 
					                        rows.map(|row| {
 | 
				
			||||||
 | 
					                            GroupIdAndName(
 | 
				
			||||||
 | 
					                                GroupId(row.get::<i32, _>(&*Groups::GroupId.to_string())),
 | 
				
			||||||
 | 
					                                row.get::<String, _>(&*Groups::DisplayName.to_string()),
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                        .filter(|g| !g.1.is_empty())
 | 
				
			||||||
 | 
					                        .collect(),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    None
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(results.into_iter().collect::<sqlx::Result<Vec<User>>>()?)
 | 
					        Ok(users)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>> {
 | 
					    async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>> {
 | 
				
			||||||
@ -486,6 +527,19 @@ mod tests {
 | 
				
			|||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn get_user_names(
 | 
				
			||||||
 | 
					        handler: &SqlBackendHandler,
 | 
				
			||||||
 | 
					        filters: Option<UserRequestFilter>,
 | 
				
			||||||
 | 
					    ) -> Vec<String> {
 | 
				
			||||||
 | 
					        handler
 | 
				
			||||||
 | 
					            .list_users(filters, false)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .map(|u| u.user.user_id.to_string())
 | 
				
			||||||
 | 
					            .collect::<Vec<_>>()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[tokio::test]
 | 
				
			||||||
    async fn test_bind_admin() {
 | 
					    async fn test_bind_admin() {
 | 
				
			||||||
        let sql_pool = get_in_memory_db().await;
 | 
					        let sql_pool = get_in_memory_db().await;
 | 
				
			||||||
@ -558,50 +612,70 @@ mod tests {
 | 
				
			|||||||
        insert_user(&handler, "bob", "bob00").await;
 | 
					        insert_user(&handler, "bob", "bob00").await;
 | 
				
			||||||
        insert_user(&handler, "patrick", "pass").await;
 | 
					        insert_user(&handler, "patrick", "pass").await;
 | 
				
			||||||
        insert_user(&handler, "John", "Pa33w0rd!").await;
 | 
					        insert_user(&handler, "John", "Pa33w0rd!").await;
 | 
				
			||||||
 | 
					        let group_1 = insert_group(&handler, "Best Group").await;
 | 
				
			||||||
 | 
					        let group_2 = insert_group(&handler, "Worst Group").await;
 | 
				
			||||||
 | 
					        insert_membership(&handler, group_1, "bob").await;
 | 
				
			||||||
 | 
					        insert_membership(&handler, group_1, "patrick").await;
 | 
				
			||||||
 | 
					        insert_membership(&handler, group_2, "patrick").await;
 | 
				
			||||||
 | 
					        insert_membership(&handler, group_2, "John").await;
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let users = handler
 | 
					            let users = get_user_names(&handler, None).await;
 | 
				
			||||||
                .list_users(None)
 | 
					 | 
				
			||||||
                .await
 | 
					 | 
				
			||||||
                .unwrap()
 | 
					 | 
				
			||||||
                .into_iter()
 | 
					 | 
				
			||||||
                .map(|u| u.user_id.to_string())
 | 
					 | 
				
			||||||
                .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
            assert_eq!(users, vec!["bob", "john", "patrick"]);
 | 
					            assert_eq!(users, vec!["bob", "john", "patrick"]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let users = handler
 | 
					            let users = get_user_names(
 | 
				
			||||||
                .list_users(Some(UserRequestFilter::UserId(UserId::new("bob"))))
 | 
					                &handler,
 | 
				
			||||||
                .await
 | 
					                Some(UserRequestFilter::UserId(UserId::new("bob"))),
 | 
				
			||||||
                .unwrap()
 | 
					            )
 | 
				
			||||||
                .into_iter()
 | 
					            .await;
 | 
				
			||||||
                .map(|u| u.user_id.to_string())
 | 
					 | 
				
			||||||
                .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
            assert_eq!(users, vec!["bob"]);
 | 
					            assert_eq!(users, vec!["bob"]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let users = handler
 | 
					            let users = get_user_names(
 | 
				
			||||||
                .list_users(Some(UserRequestFilter::Or(vec![
 | 
					                &handler,
 | 
				
			||||||
 | 
					                Some(UserRequestFilter::Or(vec![
 | 
				
			||||||
                    UserRequestFilter::UserId(UserId::new("bob")),
 | 
					                    UserRequestFilter::UserId(UserId::new("bob")),
 | 
				
			||||||
                    UserRequestFilter::UserId(UserId::new("John")),
 | 
					                    UserRequestFilter::UserId(UserId::new("John")),
 | 
				
			||||||
                ])))
 | 
					                ])),
 | 
				
			||||||
                .await
 | 
					            )
 | 
				
			||||||
                .unwrap()
 | 
					            .await;
 | 
				
			||||||
                .into_iter()
 | 
					 | 
				
			||||||
                .map(|u| u.user_id.to_string())
 | 
					 | 
				
			||||||
                .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
            assert_eq!(users, vec!["bob", "john"]);
 | 
					            assert_eq!(users, vec!["bob", "john"]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            let users = get_user_names(
 | 
				
			||||||
 | 
					                &handler,
 | 
				
			||||||
 | 
					                Some(UserRequestFilter::Not(Box::new(UserRequestFilter::UserId(
 | 
				
			||||||
 | 
					                    UserId::new("bob"),
 | 
				
			||||||
 | 
					                )))),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .await;
 | 
				
			||||||
 | 
					            assert_eq!(users, vec!["john", "patrick"]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let users = handler
 | 
					            let users = handler
 | 
				
			||||||
                .list_users(Some(UserRequestFilter::Not(Box::new(
 | 
					                .list_users(None, true)
 | 
				
			||||||
                    UserRequestFilter::UserId(UserId::new("bob")),
 | 
					 | 
				
			||||||
                ))))
 | 
					 | 
				
			||||||
                .await
 | 
					                .await
 | 
				
			||||||
                .unwrap()
 | 
					                .unwrap()
 | 
				
			||||||
                .into_iter()
 | 
					                .into_iter()
 | 
				
			||||||
                .map(|u| u.user_id.to_string())
 | 
					                .map(|u| {
 | 
				
			||||||
 | 
					                    (
 | 
				
			||||||
 | 
					                        u.user.user_id.to_string(),
 | 
				
			||||||
 | 
					                        u.groups
 | 
				
			||||||
 | 
					                            .unwrap()
 | 
				
			||||||
 | 
					                            .into_iter()
 | 
				
			||||||
 | 
					                            .map(|g| g.0)
 | 
				
			||||||
 | 
					                            .collect::<Vec<_>>(),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
                .collect::<Vec<_>>();
 | 
					                .collect::<Vec<_>>();
 | 
				
			||||||
            assert_eq!(users, vec!["john", "patrick"]);
 | 
					            assert_eq!(
 | 
				
			||||||
 | 
					                users,
 | 
				
			||||||
 | 
					                vec![
 | 
				
			||||||
 | 
					                    ("bob".to_string(), vec![group_1]),
 | 
				
			||||||
 | 
					                    ("john".to_string(), vec![group_2]),
 | 
				
			||||||
 | 
					                    ("patrick".to_string(), vec![group_1, group_2]),
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -763,29 +837,13 @@ mod tests {
 | 
				
			|||||||
        // Remove a user
 | 
					        // Remove a user
 | 
				
			||||||
        let _request_result = handler.delete_user(&UserId::new("Jennz")).await.unwrap();
 | 
					        let _request_result = handler.delete_user(&UserId::new("Jennz")).await.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let users = handler
 | 
					        assert_eq!(get_user_names(&handler, None).await, vec!["hector", "val"]);
 | 
				
			||||||
            .list_users(None)
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap()
 | 
					 | 
				
			||||||
            .into_iter()
 | 
					 | 
				
			||||||
            .map(|u| u.user_id.to_string())
 | 
					 | 
				
			||||||
            .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(users, vec!["hector", "val"]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Insert new user and remove two
 | 
					        // Insert new user and remove two
 | 
				
			||||||
        insert_user(&handler, "NewBoi", "Joni").await;
 | 
					        insert_user(&handler, "NewBoi", "Joni").await;
 | 
				
			||||||
        let _request_result = handler.delete_user(&UserId::new("Hector")).await.unwrap();
 | 
					        let _request_result = handler.delete_user(&UserId::new("Hector")).await.unwrap();
 | 
				
			||||||
        let _request_result = handler.delete_user(&UserId::new("NewBoi")).await.unwrap();
 | 
					        let _request_result = handler.delete_user(&UserId::new("NewBoi")).await.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let users = handler
 | 
					        assert_eq!(get_user_names(&handler, None).await, vec!["val"]);
 | 
				
			||||||
            .list_users(None)
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap()
 | 
					 | 
				
			||||||
            .into_iter()
 | 
					 | 
				
			||||||
            .map(|u| u.user_id.to_string())
 | 
					 | 
				
			||||||
            .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(users, vec!["val"]);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
 | 
				
			|||||||
type DomainRequestFilter = crate::domain::handler::UserRequestFilter;
 | 
					type DomainRequestFilter = crate::domain::handler::UserRequestFilter;
 | 
				
			||||||
type DomainUser = crate::domain::handler::User;
 | 
					type DomainUser = crate::domain::handler::User;
 | 
				
			||||||
type DomainGroup = crate::domain::handler::Group;
 | 
					type DomainGroup = crate::domain::handler::Group;
 | 
				
			||||||
 | 
					type DomainUserAndGroups = crate::domain::handler::UserAndGroups;
 | 
				
			||||||
use super::api::Context;
 | 
					use super::api::Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
 | 
					#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
 | 
				
			||||||
@ -126,7 +127,7 @@ impl<Handler: BackendHandler + Sync> Query<Handler> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(context
 | 
					        Ok(context
 | 
				
			||||||
            .handler
 | 
					            .handler
 | 
				
			||||||
            .list_users(filters.map(TryInto::try_into).transpose()?)
 | 
					            .list_users(filters.map(TryInto::try_into).transpose()?, false)
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
            .map(|v| v.into_iter().map(Into::into).collect())?)
 | 
					            .map(|v| v.into_iter().map(Into::into).collect())?)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -215,6 +216,15 @@ impl<Handler: BackendHandler> From<DomainUser> for User<Handler> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<Handler: BackendHandler> From<DomainUserAndGroups> for User<Handler> {
 | 
				
			||||||
 | 
					    fn from(user: DomainUserAndGroups) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            user: user.user,
 | 
				
			||||||
 | 
					            _phantom: std::marker::PhantomData,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
 | 
					#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
 | 
				
			||||||
/// Represents a single group.
 | 
					/// Represents a single group.
 | 
				
			||||||
pub struct Group<Handler: BackendHandler> {
 | 
					pub struct Group<Handler: BackendHandler> {
 | 
				
			||||||
@ -239,9 +249,10 @@ impl<Handler: BackendHandler + Sync> Group<Handler> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(context
 | 
					        Ok(context
 | 
				
			||||||
            .handler
 | 
					            .handler
 | 
				
			||||||
            .list_users(Some(DomainRequestFilter::MemberOfId(GroupId(
 | 
					            .list_users(
 | 
				
			||||||
                self.group_id,
 | 
					                Some(DomainRequestFilter::MemberOfId(GroupId(self.group_id))),
 | 
				
			||||||
            ))))
 | 
					                false,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
            .map(|v| v.into_iter().map(Into::into).collect())?)
 | 
					            .map(|v| v.into_iter().map(Into::into).collect())?)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -365,21 +376,33 @@ mod tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users()
 | 
					        mock.expect_list_users()
 | 
				
			||||||
            .with(eq(Some(UserRequestFilter::Or(vec![
 | 
					            .with(
 | 
				
			||||||
                UserRequestFilter::Equality("id".to_string(), "bob".to_string()),
 | 
					                eq(Some(UserRequestFilter::Or(vec![
 | 
				
			||||||
                UserRequestFilter::Equality("email".to_string(), "robert@bobbers.on".to_string()),
 | 
					                    UserRequestFilter::Equality("id".to_string(), "bob".to_string()),
 | 
				
			||||||
            ]))))
 | 
					                    UserRequestFilter::Equality(
 | 
				
			||||||
            .return_once(|_| {
 | 
					                        "email".to_string(),
 | 
				
			||||||
 | 
					                        "robert@bobbers.on".to_string(),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ]))),
 | 
				
			||||||
 | 
					                eq(false),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .return_once(|_, _| {
 | 
				
			||||||
                Ok(vec![
 | 
					                Ok(vec![
 | 
				
			||||||
                    DomainUser {
 | 
					                    DomainUserAndGroups {
 | 
				
			||||||
                        user_id: UserId::new("bob"),
 | 
					                        user: DomainUser {
 | 
				
			||||||
                        email: "bob@bobbers.on".to_string(),
 | 
					                            user_id: UserId::new("bob"),
 | 
				
			||||||
                        ..Default::default()
 | 
					                            email: "bob@bobbers.on".to_string(),
 | 
				
			||||||
 | 
					                            ..Default::default()
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        groups: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    DomainUser {
 | 
					                    DomainUserAndGroups {
 | 
				
			||||||
                        user_id: UserId::new("robert"),
 | 
					                        user: DomainUser {
 | 
				
			||||||
                        email: "robert@bobbers.on".to_string(),
 | 
					                            user_id: UserId::new("robert"),
 | 
				
			||||||
                        ..Default::default()
 | 
					                            email: "robert@bobbers.on".to_string(),
 | 
				
			||||||
 | 
					                            ..Default::default()
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        groups: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                ])
 | 
					                ])
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    domain::{
 | 
					    domain::{
 | 
				
			||||||
        handler::{
 | 
					        handler::{
 | 
				
			||||||
            BackendHandler, BindRequest, Group, GroupRequestFilter, LoginHandler, User, UserId,
 | 
					            BackendHandler, BindRequest, Group, GroupIdAndName, GroupRequestFilter, LoginHandler,
 | 
				
			||||||
            UserRequestFilter,
 | 
					            User, UserId, UserRequestFilter,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        opaque_handler::OpaqueHandler,
 | 
					        opaque_handler::OpaqueHandler,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -109,6 +109,8 @@ fn get_user_attribute(
 | 
				
			|||||||
    user: &User,
 | 
					    user: &User,
 | 
				
			||||||
    attribute: &str,
 | 
					    attribute: &str,
 | 
				
			||||||
    dn: &str,
 | 
					    dn: &str,
 | 
				
			||||||
 | 
					    base_dn_str: &str,
 | 
				
			||||||
 | 
					    groups: Option<&[GroupIdAndName]>,
 | 
				
			||||||
    ignored_user_attributes: &[String],
 | 
					    ignored_user_attributes: &[String],
 | 
				
			||||||
) -> Result<Option<Vec<String>>> {
 | 
					) -> Result<Option<Vec<String>>> {
 | 
				
			||||||
    let attribute = attribute.to_ascii_lowercase();
 | 
					    let attribute = attribute.to_ascii_lowercase();
 | 
				
			||||||
@ -124,6 +126,11 @@ fn get_user_attribute(
 | 
				
			|||||||
        "mail" => vec![user.email.clone()],
 | 
					        "mail" => vec![user.email.clone()],
 | 
				
			||||||
        "givenname" => vec![user.first_name.clone()],
 | 
					        "givenname" => vec![user.first_name.clone()],
 | 
				
			||||||
        "sn" => vec![user.last_name.clone()],
 | 
					        "sn" => vec![user.last_name.clone()],
 | 
				
			||||||
 | 
					        "memberof" => groups
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .flatten()
 | 
				
			||||||
 | 
					            .map(|id_and_name| format!("uid={},ou=groups,{}", &id_and_name.1, base_dn_str))
 | 
				
			||||||
 | 
					            .collect(),
 | 
				
			||||||
        "cn" | "displayname" => vec![user.display_name.clone()],
 | 
					        "cn" | "displayname" => vec![user.display_name.clone()],
 | 
				
			||||||
        "createtimestamp" | "modifytimestamp" => vec![user.creation_date.to_rfc3339()],
 | 
					        "createtimestamp" | "modifytimestamp" => vec![user.creation_date.to_rfc3339()],
 | 
				
			||||||
        "1.1" => return Ok(None),
 | 
					        "1.1" => return Ok(None),
 | 
				
			||||||
@ -179,17 +186,24 @@ fn make_ldap_search_user_result_entry(
 | 
				
			|||||||
    user: User,
 | 
					    user: User,
 | 
				
			||||||
    base_dn_str: &str,
 | 
					    base_dn_str: &str,
 | 
				
			||||||
    attributes: &[String],
 | 
					    attributes: &[String],
 | 
				
			||||||
 | 
					    groups: Option<&[GroupIdAndName]>,
 | 
				
			||||||
    ignored_user_attributes: &[String],
 | 
					    ignored_user_attributes: &[String],
 | 
				
			||||||
) -> Result<LdapSearchResultEntry> {
 | 
					) -> Result<LdapSearchResultEntry> {
 | 
				
			||||||
    let dn = format!("uid={},ou=people,{}", user.user_id.as_str(), base_dn_str);
 | 
					    let dn = format!("uid={},ou=people,{}", user.user_id.as_str(), base_dn_str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let expanded_attributes = expand_attribute_wildcards(attributes, ALL_USER_ATTRIBUTE_KEYS);
 | 
					 | 
				
			||||||
    Ok(LdapSearchResultEntry {
 | 
					    Ok(LdapSearchResultEntry {
 | 
				
			||||||
        dn: dn.clone(),
 | 
					        dn: dn.clone(),
 | 
				
			||||||
        attributes: expanded_attributes
 | 
					        attributes: attributes
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
            .filter_map(|a| {
 | 
					            .filter_map(|a| {
 | 
				
			||||||
                let values = match get_user_attribute(&user, a, &dn, ignored_user_attributes) {
 | 
					                let values = match get_user_attribute(
 | 
				
			||||||
 | 
					                    &user,
 | 
				
			||||||
 | 
					                    a,
 | 
				
			||||||
 | 
					                    &dn,
 | 
				
			||||||
 | 
					                    base_dn_str,
 | 
				
			||||||
 | 
					                    groups,
 | 
				
			||||||
 | 
					                    ignored_user_attributes,
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
                    Err(e) => return Some(Err(e)),
 | 
					                    Err(e) => return Some(Err(e)),
 | 
				
			||||||
                    Ok(v) => v,
 | 
					                    Ok(v) => v,
 | 
				
			||||||
                }?;
 | 
					                }?;
 | 
				
			||||||
@ -625,7 +639,16 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
 | 
				
			|||||||
                UserRequestFilter::And(vec![filters, UserRequestFilter::UserId((*u).clone())])
 | 
					                UserRequestFilter::And(vec![filters, UserRequestFilter::UserId((*u).clone())])
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let users = match self.backend_handler.list_users(Some(filters)).await {
 | 
					        let expanded_attributes =
 | 
				
			||||||
 | 
					            expand_attribute_wildcards(&request.attrs, ALL_USER_ATTRIBUTE_KEYS);
 | 
				
			||||||
 | 
					        let need_groups = expanded_attributes
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .any(|s| s.to_ascii_lowercase() == "memberof");
 | 
				
			||||||
 | 
					        let users = match self
 | 
				
			||||||
 | 
					            .backend_handler
 | 
				
			||||||
 | 
					            .list_users(Some(filters), need_groups)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            Ok(users) => users,
 | 
					            Ok(users) => users,
 | 
				
			||||||
            Err(e) => {
 | 
					            Err(e) => {
 | 
				
			||||||
                return vec![make_search_error(
 | 
					                return vec![make_search_error(
 | 
				
			||||||
@ -639,9 +662,10 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
 | 
				
			|||||||
            .into_iter()
 | 
					            .into_iter()
 | 
				
			||||||
            .map(|u| {
 | 
					            .map(|u| {
 | 
				
			||||||
                make_ldap_search_user_result_entry(
 | 
					                make_ldap_search_user_result_entry(
 | 
				
			||||||
                    u,
 | 
					                    u.user,
 | 
				
			||||||
                    &self.base_dn_str,
 | 
					                    &self.base_dn_str,
 | 
				
			||||||
                    &request.attrs,
 | 
					                    &expanded_attributes,
 | 
				
			||||||
 | 
					                    u.groups.as_deref(),
 | 
				
			||||||
                    &self.ignored_user_attributes,
 | 
					                    &self.ignored_user_attributes,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
@ -903,7 +927,7 @@ mod tests {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        #[async_trait]
 | 
					        #[async_trait]
 | 
				
			||||||
        impl BackendHandler for TestBackendHandler {
 | 
					        impl BackendHandler for TestBackendHandler {
 | 
				
			||||||
            async fn list_users(&self, filters: Option<UserRequestFilter>) -> Result<Vec<User>>;
 | 
					            async fn list_users(&self, filters: Option<UserRequestFilter>, get_groups: bool) -> Result<Vec<UserAndGroups>>;
 | 
				
			||||||
            async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
					            async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
				
			||||||
            async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
					            async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
				
			||||||
            async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
					            async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
				
			||||||
@ -1070,15 +1094,21 @@ mod tests {
 | 
				
			|||||||
    async fn test_search_regular_user() {
 | 
					    async fn test_search_regular_user() {
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users()
 | 
					        mock.expect_list_users()
 | 
				
			||||||
            .with(eq(Some(UserRequestFilter::And(vec![
 | 
					            .with(
 | 
				
			||||||
                UserRequestFilter::And(vec![]),
 | 
					                eq(Some(UserRequestFilter::And(vec![
 | 
				
			||||||
                UserRequestFilter::UserId(UserId::new("test")),
 | 
					                    UserRequestFilter::And(vec![]),
 | 
				
			||||||
            ]))))
 | 
					                    UserRequestFilter::UserId(UserId::new("test")),
 | 
				
			||||||
 | 
					                ]))),
 | 
				
			||||||
 | 
					                eq(false),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .times(1)
 | 
					            .times(1)
 | 
				
			||||||
            .return_once(|_| {
 | 
					            .return_once(|_, _| {
 | 
				
			||||||
                Ok(vec![User {
 | 
					                Ok(vec![UserAndGroups {
 | 
				
			||||||
                    user_id: UserId::new("test"),
 | 
					                    user: User {
 | 
				
			||||||
                    ..Default::default()
 | 
					                        user_id: UserId::new("test"),
 | 
				
			||||||
 | 
					                        ..Default::default()
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    groups: None,
 | 
				
			||||||
                }])
 | 
					                }])
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        let mut ldap_handler = setup_bound_handler_with_group(mock, "regular").await;
 | 
					        let mut ldap_handler = setup_bound_handler_with_group(mock, "regular").await;
 | 
				
			||||||
@ -1101,9 +1131,9 @@ mod tests {
 | 
				
			|||||||
    async fn test_search_readonly_user() {
 | 
					    async fn test_search_readonly_user() {
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users()
 | 
					        mock.expect_list_users()
 | 
				
			||||||
            .with(eq(Some(UserRequestFilter::And(vec![]))))
 | 
					            .with(eq(Some(UserRequestFilter::And(vec![]))), eq(false))
 | 
				
			||||||
            .times(1)
 | 
					            .times(1)
 | 
				
			||||||
            .return_once(|_| Ok(vec![]));
 | 
					            .return_once(|_, _| Ok(vec![]));
 | 
				
			||||||
        let mut ldap_handler = setup_bound_readonly_handler(mock).await;
 | 
					        let mut ldap_handler = setup_bound_readonly_handler(mock).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let request =
 | 
					        let request =
 | 
				
			||||||
@ -1114,6 +1144,42 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[tokio::test]
 | 
				
			||||||
 | 
					    async fn test_search_member_of() {
 | 
				
			||||||
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
 | 
					        mock.expect_list_users()
 | 
				
			||||||
 | 
					            .with(eq(Some(UserRequestFilter::And(vec![]))), eq(true))
 | 
				
			||||||
 | 
					            .times(1)
 | 
				
			||||||
 | 
					            .return_once(|_, _| {
 | 
				
			||||||
 | 
					                Ok(vec![UserAndGroups {
 | 
				
			||||||
 | 
					                    user: User {
 | 
				
			||||||
 | 
					                        user_id: UserId::new("bob"),
 | 
				
			||||||
 | 
					                        ..Default::default()
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    groups: Some(vec![GroupIdAndName(GroupId(42), "rockstars".to_string())]),
 | 
				
			||||||
 | 
					                }])
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        let mut ldap_handler = setup_bound_readonly_handler(mock).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let request = make_user_search_request::<String>(
 | 
				
			||||||
 | 
					            LdapFilter::And(vec![]),
 | 
				
			||||||
 | 
					            vec!["memberOf".to_string()],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            ldap_handler.do_search(&request).await,
 | 
				
			||||||
 | 
					            vec![
 | 
				
			||||||
 | 
					                LdapOp::SearchResultEntry(LdapSearchResultEntry {
 | 
				
			||||||
 | 
					                    dn: "uid=bob,ou=people,dc=example,dc=com".to_string(),
 | 
				
			||||||
 | 
					                    attributes: vec![LdapPartialAttribute {
 | 
				
			||||||
 | 
					                        atype: "memberOf".to_string(),
 | 
				
			||||||
 | 
					                        vals: vec!["uid=rockstars,ou=groups,dc=example,dc=com".to_string()]
 | 
				
			||||||
 | 
					                    }],
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					                make_search_success(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[tokio::test]
 | 
				
			||||||
    async fn test_bind_invalid_dn() {
 | 
					    async fn test_bind_invalid_dn() {
 | 
				
			||||||
        let mock = MockTestBackendHandler::new();
 | 
					        let mock = MockTestBackendHandler::new();
 | 
				
			||||||
@ -1199,23 +1265,29 @@ mod tests {
 | 
				
			|||||||
    async fn test_search_users() {
 | 
					    async fn test_search_users() {
 | 
				
			||||||
        use chrono::prelude::*;
 | 
					        use chrono::prelude::*;
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users().times(1).return_once(|_| {
 | 
					        mock.expect_list_users().times(1).return_once(|_, _| {
 | 
				
			||||||
            Ok(vec![
 | 
					            Ok(vec![
 | 
				
			||||||
                User {
 | 
					                UserAndGroups {
 | 
				
			||||||
                    user_id: UserId::new("bob_1"),
 | 
					                    user: User {
 | 
				
			||||||
                    email: "bob@bobmail.bob".to_string(),
 | 
					                        user_id: UserId::new("bob_1"),
 | 
				
			||||||
                    display_name: "Bôb Böbberson".to_string(),
 | 
					                        email: "bob@bobmail.bob".to_string(),
 | 
				
			||||||
                    first_name: "Bôb".to_string(),
 | 
					                        display_name: "Bôb Böbberson".to_string(),
 | 
				
			||||||
                    last_name: "Böbberson".to_string(),
 | 
					                        first_name: "Bôb".to_string(),
 | 
				
			||||||
                    ..Default::default()
 | 
					                        last_name: "Böbberson".to_string(),
 | 
				
			||||||
 | 
					                        ..Default::default()
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    groups: None,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                User {
 | 
					                UserAndGroups {
 | 
				
			||||||
                    user_id: UserId::new("jim"),
 | 
					                    user: User {
 | 
				
			||||||
                    email: "jim@cricket.jim".to_string(),
 | 
					                        user_id: UserId::new("jim"),
 | 
				
			||||||
                    display_name: "Jimminy Cricket".to_string(),
 | 
					                        email: "jim@cricket.jim".to_string(),
 | 
				
			||||||
                    first_name: "Jim".to_string(),
 | 
					                        display_name: "Jimminy Cricket".to_string(),
 | 
				
			||||||
                    last_name: "Cricket".to_string(),
 | 
					                        first_name: "Jim".to_string(),
 | 
				
			||||||
                    creation_date: Utc.ymd(2014, 7, 8).and_hms(9, 10, 11),
 | 
					                        last_name: "Cricket".to_string(),
 | 
				
			||||||
 | 
					                        creation_date: Utc.ymd(2014, 7, 8).and_hms(9, 10, 11),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    groups: None,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            ])
 | 
					            ])
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -1559,19 +1631,24 @@ mod tests {
 | 
				
			|||||||
    async fn test_search_filters() {
 | 
					    async fn test_search_filters() {
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users()
 | 
					        mock.expect_list_users()
 | 
				
			||||||
            .with(eq(Some(UserRequestFilter::And(vec![
 | 
					            .with(
 | 
				
			||||||
                UserRequestFilter::Or(vec![
 | 
					                eq(Some(UserRequestFilter::And(vec![UserRequestFilter::Or(
 | 
				
			||||||
                    UserRequestFilter::Not(Box::new(UserRequestFilter::UserId(UserId::new("bob")))),
 | 
					                    vec![
 | 
				
			||||||
                    UserRequestFilter::And(vec![]),
 | 
					                        UserRequestFilter::Not(Box::new(UserRequestFilter::UserId(UserId::new(
 | 
				
			||||||
                    UserRequestFilter::Not(Box::new(UserRequestFilter::And(vec![]))),
 | 
					                            "bob",
 | 
				
			||||||
                    UserRequestFilter::And(vec![]),
 | 
					                        )))),
 | 
				
			||||||
                    UserRequestFilter::And(vec![]),
 | 
					                        UserRequestFilter::And(vec![]),
 | 
				
			||||||
                    UserRequestFilter::Not(Box::new(UserRequestFilter::And(vec![]))),
 | 
					                        UserRequestFilter::Not(Box::new(UserRequestFilter::And(vec![]))),
 | 
				
			||||||
                    UserRequestFilter::Not(Box::new(UserRequestFilter::And(vec![]))),
 | 
					                        UserRequestFilter::And(vec![]),
 | 
				
			||||||
                ]),
 | 
					                        UserRequestFilter::And(vec![]),
 | 
				
			||||||
            ]))))
 | 
					                        UserRequestFilter::Not(Box::new(UserRequestFilter::And(vec![]))),
 | 
				
			||||||
 | 
					                        UserRequestFilter::Not(Box::new(UserRequestFilter::And(vec![]))),
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                )]))),
 | 
				
			||||||
 | 
					                eq(false),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .times(1)
 | 
					            .times(1)
 | 
				
			||||||
            .return_once(|_| Ok(vec![]));
 | 
					            .return_once(|_, _| Ok(vec![]));
 | 
				
			||||||
        let mut ldap_handler = setup_bound_admin_handler(mock).await;
 | 
					        let mut ldap_handler = setup_bound_admin_handler(mock).await;
 | 
				
			||||||
        let request = make_user_search_request(
 | 
					        let request = make_user_search_request(
 | 
				
			||||||
            LdapFilter::And(vec![LdapFilter::Or(vec![
 | 
					            LdapFilter::And(vec![LdapFilter::Or(vec![
 | 
				
			||||||
@ -1595,12 +1672,15 @@ mod tests {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test]
 | 
					    #[tokio::test]
 | 
				
			||||||
    async fn test_search_member_of() {
 | 
					    async fn test_search_member_of_filter() {
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users()
 | 
					        mock.expect_list_users()
 | 
				
			||||||
            .with(eq(Some(UserRequestFilter::MemberOf("group_1".to_string()))))
 | 
					            .with(
 | 
				
			||||||
 | 
					                eq(Some(UserRequestFilter::MemberOf("group_1".to_string()))),
 | 
				
			||||||
 | 
					                eq(false),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .times(1)
 | 
					            .times(1)
 | 
				
			||||||
            .return_once(|_| Ok(vec![]));
 | 
					            .return_once(|_, _| Ok(vec![]));
 | 
				
			||||||
        let mut ldap_handler = setup_bound_admin_handler(mock).await;
 | 
					        let mut ldap_handler = setup_bound_admin_handler(mock).await;
 | 
				
			||||||
        let request = make_user_search_request(
 | 
					        let request = make_user_search_request(
 | 
				
			||||||
            LdapFilter::Equality(
 | 
					            LdapFilter::Equality(
 | 
				
			||||||
@ -1644,16 +1724,22 @@ mod tests {
 | 
				
			|||||||
    async fn test_search_filters_lowercase() {
 | 
					    async fn test_search_filters_lowercase() {
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users()
 | 
					        mock.expect_list_users()
 | 
				
			||||||
            .with(eq(Some(UserRequestFilter::And(vec![
 | 
					            .with(
 | 
				
			||||||
                UserRequestFilter::Or(vec![UserRequestFilter::Not(Box::new(
 | 
					                eq(Some(UserRequestFilter::And(vec![UserRequestFilter::Or(
 | 
				
			||||||
                    UserRequestFilter::Equality("first_name".to_string(), "bob".to_string()),
 | 
					                    vec![UserRequestFilter::Not(Box::new(
 | 
				
			||||||
                ))]),
 | 
					                        UserRequestFilter::Equality("first_name".to_string(), "bob".to_string()),
 | 
				
			||||||
            ]))))
 | 
					                    ))],
 | 
				
			||||||
 | 
					                )]))),
 | 
				
			||||||
 | 
					                eq(false),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .times(1)
 | 
					            .times(1)
 | 
				
			||||||
            .return_once(|_| {
 | 
					            .return_once(|_, _| {
 | 
				
			||||||
                Ok(vec![User {
 | 
					                Ok(vec![UserAndGroups {
 | 
				
			||||||
                    user_id: UserId::new("bob_1"),
 | 
					                    user: User {
 | 
				
			||||||
                    ..Default::default()
 | 
					                        user_id: UserId::new("bob_1"),
 | 
				
			||||||
 | 
					                        ..Default::default()
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    groups: None,
 | 
				
			||||||
                }])
 | 
					                }])
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        let mut ldap_handler = setup_bound_admin_handler(mock).await;
 | 
					        let mut ldap_handler = setup_bound_admin_handler(mock).await;
 | 
				
			||||||
@ -1686,14 +1772,17 @@ mod tests {
 | 
				
			|||||||
    #[tokio::test]
 | 
					    #[tokio::test]
 | 
				
			||||||
    async fn test_search_both() {
 | 
					    async fn test_search_both() {
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
        mock.expect_list_users().times(1).return_once(|_| {
 | 
					        mock.expect_list_users().times(1).return_once(|_, _| {
 | 
				
			||||||
            Ok(vec![User {
 | 
					            Ok(vec![UserAndGroups {
 | 
				
			||||||
                user_id: UserId::new("bob_1"),
 | 
					                user: User {
 | 
				
			||||||
                email: "bob@bobmail.bob".to_string(),
 | 
					                    user_id: UserId::new("bob_1"),
 | 
				
			||||||
                display_name: "Bôb Böbberson".to_string(),
 | 
					                    email: "bob@bobmail.bob".to_string(),
 | 
				
			||||||
                first_name: "Bôb".to_string(),
 | 
					                    display_name: "Bôb Böbberson".to_string(),
 | 
				
			||||||
                last_name: "Böbberson".to_string(),
 | 
					                    first_name: "Bôb".to_string(),
 | 
				
			||||||
                ..Default::default()
 | 
					                    last_name: "Böbberson".to_string(),
 | 
				
			||||||
 | 
					                    ..Default::default()
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                groups: None,
 | 
				
			||||||
            }])
 | 
					            }])
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        mock.expect_list_groups()
 | 
					        mock.expect_list_groups()
 | 
				
			||||||
@ -1763,14 +1852,17 @@ mod tests {
 | 
				
			|||||||
        use chrono::TimeZone;
 | 
					        use chrono::TimeZone;
 | 
				
			||||||
        let mut mock = MockTestBackendHandler::new();
 | 
					        let mut mock = MockTestBackendHandler::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mock.expect_list_users().returning(|_| {
 | 
					        mock.expect_list_users().returning(|_, _| {
 | 
				
			||||||
            Ok(vec![User {
 | 
					            Ok(vec![UserAndGroups {
 | 
				
			||||||
                user_id: UserId::new("bob_1"),
 | 
					                user: User {
 | 
				
			||||||
                email: "bob@bobmail.bob".to_string(),
 | 
					                    user_id: UserId::new("bob_1"),
 | 
				
			||||||
                display_name: "Bôb Böbberson".to_string(),
 | 
					                    email: "bob@bobmail.bob".to_string(),
 | 
				
			||||||
                first_name: "Bôb".to_string(),
 | 
					                    display_name: "Bôb Böbberson".to_string(),
 | 
				
			||||||
                last_name: "Böbberson".to_string(),
 | 
					                    first_name: "Bôb".to_string(),
 | 
				
			||||||
                ..Default::default()
 | 
					                    last_name: "Böbberson".to_string(),
 | 
				
			||||||
 | 
					                    ..Default::default()
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                groups: None,
 | 
				
			||||||
            }])
 | 
					            }])
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        mock.expect_list_groups()
 | 
					        mock.expect_list_groups()
 | 
				
			||||||
 | 
				
			|||||||
@ -35,7 +35,7 @@ mockall::mock! {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    #[async_trait]
 | 
					    #[async_trait]
 | 
				
			||||||
    impl BackendHandler for TestTcpBackendHandler {
 | 
					    impl BackendHandler for TestTcpBackendHandler {
 | 
				
			||||||
        async fn list_users(&self, filters: Option<UserRequestFilter>) -> Result<Vec<User>>;
 | 
					    async fn list_users(&self, filters: Option<UserRequestFilter>, get_groups: bool) -> Result<Vec<UserAndGroups>>;
 | 
				
			||||||
        async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
					        async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
 | 
				
			||||||
        async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
					        async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
 | 
				
			||||||
        async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
					        async fn get_group_details(&self, group_id: GroupId) -> Result<GroupIdAndName>;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user