mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
Add support for basic ldap filters
This commit is contained in:
parent
6abe94af13
commit
bfd7730d55
@ -10,9 +10,17 @@ pub struct BindRequest {
|
|||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||||
|
pub enum RequestFilter {
|
||||||
|
And(Vec<RequestFilter>),
|
||||||
|
Or(Vec<RequestFilter>),
|
||||||
|
Not(Box<RequestFilter>),
|
||||||
|
Equality(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||||
pub struct ListUsersRequest {
|
pub struct ListUsersRequest {
|
||||||
// filters
|
pub filters: Option<RequestFilter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::domain::handler::{BackendHandler, ListUsersRequest, User};
|
use crate::domain::handler::{BackendHandler, ListUsersRequest, RequestFilter, User};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use ldap3_server::simple::*;
|
use ldap3_server::simple::*;
|
||||||
|
|
||||||
@ -77,6 +77,28 @@ fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)]) -> b
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_filter(filter: &LdapFilter) -> Result<RequestFilter> {
|
||||||
|
match filter {
|
||||||
|
LdapFilter::And(filters) => Ok(RequestFilter::And(
|
||||||
|
filters
|
||||||
|
.into_iter()
|
||||||
|
.map(convert_filter)
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
)),
|
||||||
|
LdapFilter::Or(filters) => Ok(RequestFilter::Or(
|
||||||
|
filters
|
||||||
|
.into_iter()
|
||||||
|
.map(convert_filter)
|
||||||
|
.collect::<Result<_>>()?,
|
||||||
|
)),
|
||||||
|
LdapFilter::Not(filter) => Ok(RequestFilter::Not(Box::new(convert_filter(&*filter)?))),
|
||||||
|
LdapFilter::Equality(field, value) => {
|
||||||
|
Ok(RequestFilter::Equality(field.clone(), value.clone()))
|
||||||
|
}
|
||||||
|
_ => bail!("Unsupported filter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LdapHandler<Backend: BackendHandler> {
|
pub struct LdapHandler<Backend: BackendHandler> {
|
||||||
dn: String,
|
dn: String,
|
||||||
backend_handler: Backend,
|
backend_handler: Backend,
|
||||||
@ -138,7 +160,20 @@ impl<Backend: BackendHandler> LdapHandler<Backend> {
|
|||||||
// Search path is not in our tree, just return an empty success.
|
// Search path is not in our tree, just return an empty success.
|
||||||
return vec![lsr.gen_success()];
|
return vec![lsr.gen_success()];
|
||||||
}
|
}
|
||||||
let users = match self.backend_handler.list_users(ListUsersRequest {}).await {
|
let filters = match convert_filter(&lsr.filter) {
|
||||||
|
Ok(f) => Some(f),
|
||||||
|
Err(_) => {
|
||||||
|
return vec![lsr.gen_error(
|
||||||
|
LdapResultCode::UnwillingToPerform,
|
||||||
|
"Unsupported filter".to_string(),
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let users = match self
|
||||||
|
.backend_handler
|
||||||
|
.list_users(ListUsersRequest { filters })
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(users) => users,
|
Ok(users) => users,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return vec![lsr.gen_error(
|
return vec![lsr.gen_error(
|
||||||
@ -188,6 +223,21 @@ mod tests {
|
|||||||
use mockall::predicate::eq;
|
use mockall::predicate::eq;
|
||||||
use tokio;
|
use tokio;
|
||||||
|
|
||||||
|
async fn setup_bound_handler(
|
||||||
|
mut mock: MockTestBackendHandler,
|
||||||
|
) -> LdapHandler<MockTestBackendHandler> {
|
||||||
|
mock.expect_bind().return_once(|_| Ok(()));
|
||||||
|
let mut ldap_handler =
|
||||||
|
LdapHandler::new(mock, "dc=example,dc=com".to_string(), "test".to_string());
|
||||||
|
let request = SimpleBindRequest {
|
||||||
|
msgid: 1,
|
||||||
|
dn: "test".to_string(),
|
||||||
|
pw: "pass".to_string(),
|
||||||
|
};
|
||||||
|
ldap_handler.do_bind(&request).await;
|
||||||
|
ldap_handler
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bind() {
|
async fn test_bind() {
|
||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
@ -300,11 +350,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search() {
|
async fn test_search() {
|
||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
mock.expect_bind().return_once(|_| Ok(()));
|
mock.expect_list_users().times(1).return_once(|_| {
|
||||||
mock.expect_list_users()
|
|
||||||
.with(eq(ListUsersRequest {}))
|
|
||||||
.times(1)
|
|
||||||
.return_once(|_| {
|
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
User {
|
User {
|
||||||
user_id: "bob_1".to_string(),
|
user_id: "bob_1".to_string(),
|
||||||
@ -324,14 +370,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
let mut ldap_handler =
|
let mut ldap_handler = setup_bound_handler(mock).await;
|
||||||
LdapHandler::new(mock, "dc=example,dc=com".to_string(), "test".to_string());
|
|
||||||
let request = SimpleBindRequest {
|
|
||||||
msgid: 1,
|
|
||||||
dn: "test".to_string(),
|
|
||||||
pw: "pass".to_string(),
|
|
||||||
};
|
|
||||||
assert_eq!(ldap_handler.do_bind(&request).await, request.gen_success());
|
|
||||||
let request = SearchRequest {
|
let request = SearchRequest {
|
||||||
msgid: 2,
|
msgid: 2,
|
||||||
base: "ou=people,dc=example,dc=com".to_string(),
|
base: "ou=people,dc=example,dc=com".to_string(),
|
||||||
@ -419,4 +458,54 @@ mod tests {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_search_filters() {
|
||||||
|
let mut mock = MockTestBackendHandler::new();
|
||||||
|
mock.expect_list_users()
|
||||||
|
.with(eq(ListUsersRequest {
|
||||||
|
filters: Some(RequestFilter::And(vec![RequestFilter::Or(vec![
|
||||||
|
RequestFilter::Not(Box::new(RequestFilter::Equality(
|
||||||
|
"uid".to_string(),
|
||||||
|
"bob".to_string(),
|
||||||
|
))),
|
||||||
|
])])),
|
||||||
|
}))
|
||||||
|
.times(1)
|
||||||
|
.return_once(|_| Ok(vec![]));
|
||||||
|
let mut ldap_handler = setup_bound_handler(mock).await;
|
||||||
|
let request = SearchRequest {
|
||||||
|
msgid: 2,
|
||||||
|
base: "ou=people,dc=example,dc=com".to_string(),
|
||||||
|
scope: LdapSearchScope::Base,
|
||||||
|
filter: LdapFilter::And(vec![LdapFilter::Or(vec![LdapFilter::Not(Box::new(
|
||||||
|
LdapFilter::Equality("uid".to_string(), "bob".to_string()),
|
||||||
|
))])]),
|
||||||
|
attrs: vec!["objectClass".to_string()],
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_search(&request).await,
|
||||||
|
vec![request.gen_success()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_search_unsupported_filters() {
|
||||||
|
let mut mock = MockTestBackendHandler::new();
|
||||||
|
let mut ldap_handler = setup_bound_handler(mock).await;
|
||||||
|
let request = SearchRequest {
|
||||||
|
msgid: 2,
|
||||||
|
base: "ou=people,dc=example,dc=com".to_string(),
|
||||||
|
scope: LdapSearchScope::Base,
|
||||||
|
filter: LdapFilter::Present("uid".to_string()),
|
||||||
|
attrs: vec!["objectClass".to_string()],
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_search(&request).await,
|
||||||
|
vec![request.gen_error(
|
||||||
|
LdapResultCode::UnwillingToPerform,
|
||||||
|
"Unsupported filter".to_string()
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user