mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
Add attribute list handling
Also, fix various clippy warnings
This commit is contained in:
parent
cda2bcacc3
commit
31e8998ac3
@ -11,7 +11,6 @@ pub struct BindRequest {
|
|||||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||||
pub struct ListUsersRequest {
|
pub struct ListUsersRequest {
|
||||||
// filters
|
// filters
|
||||||
pub attrs: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||||
|
@ -2,17 +2,15 @@ use crate::domain::handler::{BackendHandler, ListUsersRequest, User};
|
|||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use ldap3_server::simple::*;
|
use ldap3_server::simple::*;
|
||||||
|
|
||||||
fn make_dn_pair<'a, I>(mut iter: I) -> Result<(String, String)>
|
fn make_dn_pair<I>(mut iter: I) -> Result<(String, String)>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = String>,
|
I: Iterator<Item = String>,
|
||||||
{
|
{
|
||||||
let pair = (
|
let pair = (
|
||||||
iter.next()
|
iter.next()
|
||||||
.ok_or(anyhow::Error::msg("Empty DN element"))?
|
.ok_or_else(|| anyhow::Error::msg("Empty DN element"))?,
|
||||||
.clone(),
|
|
||||||
iter.next()
|
iter.next()
|
||||||
.ok_or(anyhow::Error::msg("Missing DN value"))?
|
.ok_or_else(|| anyhow::Error::msg("Missing DN value"))?,
|
||||||
.clone(),
|
|
||||||
);
|
);
|
||||||
if let Some(e) = iter.next() {
|
if let Some(e) = iter.next() {
|
||||||
bail!(
|
bail!(
|
||||||
@ -26,48 +24,47 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_distinguished_name(dn: &str) -> Result<Vec<(String, String)>> {
|
fn parse_distinguished_name(dn: &str) -> Result<Vec<(String, String)>> {
|
||||||
dn.split(",")
|
dn.split(',')
|
||||||
.map(|s| make_dn_pair(s.split("=").map(String::from)))
|
.map(|s| make_dn_pair(s.split('=').map(String::from)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_ldap_search_result_entry(user: User, base_dn_str: &str) -> LdapSearchResultEntry {
|
fn get_attribute(user: &User, attribute: &str) -> Result<Vec<String>> {
|
||||||
LdapSearchResultEntry {
|
match attribute {
|
||||||
dn: format!("cn={},{}", user.user_id, base_dn_str),
|
"objectClass" => Ok(vec![
|
||||||
attributes: vec![
|
|
||||||
LdapPartialAttribute {
|
|
||||||
atype: "objectClass".to_string(),
|
|
||||||
vals: vec![
|
|
||||||
"inetOrgPerson".to_string(),
|
"inetOrgPerson".to_string(),
|
||||||
"posixAccount".to_string(),
|
"posixAccount".to_string(),
|
||||||
"mailAccount".to_string(),
|
"mailAccount".to_string(),
|
||||||
],
|
]),
|
||||||
},
|
"uid" => Ok(vec![user.user_id.to_string()]),
|
||||||
LdapPartialAttribute {
|
"mail" => Ok(vec![user.email.to_string()]),
|
||||||
atype: "uid".to_string(),
|
"givenName" => Ok(vec![user.first_name.to_string()]),
|
||||||
vals: vec![user.user_id],
|
"sn" => Ok(vec![user.last_name.to_string()]),
|
||||||
},
|
"cn" => Ok(vec![user.display_name.to_string()]),
|
||||||
LdapPartialAttribute {
|
_ => bail!("Unsupported attribute: {}", attribute),
|
||||||
atype: "mail".to_string(),
|
|
||||||
vals: vec![user.email],
|
|
||||||
},
|
|
||||||
LdapPartialAttribute {
|
|
||||||
atype: "givenName".to_string(),
|
|
||||||
vals: vec![user.first_name],
|
|
||||||
},
|
|
||||||
LdapPartialAttribute {
|
|
||||||
atype: "sn".to_string(),
|
|
||||||
vals: vec![user.last_name],
|
|
||||||
},
|
|
||||||
LdapPartialAttribute {
|
|
||||||
atype: "cn".to_string(),
|
|
||||||
vals: vec![user.display_name],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_subtree(subtree: &Vec<(String, String)>, base_tree: &Vec<(String, String)>) -> bool {
|
fn make_ldap_search_result_entry(
|
||||||
|
user: User,
|
||||||
|
base_dn_str: &str,
|
||||||
|
attributes: &[String],
|
||||||
|
) -> Result<LdapSearchResultEntry> {
|
||||||
|
Ok(LdapSearchResultEntry {
|
||||||
|
dn: format!("cn={},{}", user.user_id, base_dn_str),
|
||||||
|
attributes: attributes
|
||||||
|
.iter()
|
||||||
|
.map(|a| {
|
||||||
|
Ok(LdapPartialAttribute {
|
||||||
|
atype: a.to_string(),
|
||||||
|
vals: get_attribute(&user, a)?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<LdapPartialAttribute>>>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)]) -> bool {
|
||||||
if subtree.len() < base_tree.len() {
|
if subtree.len() < base_tree.len() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -92,10 +89,12 @@ impl<Backend: BackendHandler> LdapHandler<Backend> {
|
|||||||
Self {
|
Self {
|
||||||
dn: "Unauthenticated".to_string(),
|
dn: "Unauthenticated".to_string(),
|
||||||
backend_handler,
|
backend_handler,
|
||||||
base_dn: parse_distinguished_name(&ldap_base_dn).expect(&format!(
|
base_dn: parse_distinguished_name(&ldap_base_dn).unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
"Invalid value for ldap_base_dn in configuration: {}",
|
"Invalid value for ldap_base_dn in configuration: {}",
|
||||||
ldap_base_dn
|
ldap_base_dn
|
||||||
)),
|
)
|
||||||
|
}),
|
||||||
base_dn_str: ldap_base_dn,
|
base_dn_str: ldap_base_dn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,11 +125,10 @@ impl<Backend: BackendHandler> LdapHandler<Backend> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !is_subtree(&dn_parts, &self.base_dn) {
|
if !is_subtree(&dn_parts, &self.base_dn) {
|
||||||
|
// 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 {
|
let users = match self.backend_handler.list_users(ListUsersRequest {}) {
|
||||||
attrs: lsr.attrs.clone(),
|
|
||||||
}) {
|
|
||||||
Ok(users) => users,
|
Ok(users) => users,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return vec![lsr.gen_error(
|
return vec![lsr.gen_error(
|
||||||
@ -139,12 +137,15 @@ impl<Backend: BackendHandler> LdapHandler<Backend> {
|
|||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut res = users
|
|
||||||
|
users
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|u| lsr.gen_result_entry(make_ldap_search_result_entry(u, &self.base_dn_str)))
|
.map(|u| make_ldap_search_result_entry(u, &self.base_dn_str, &lsr.attrs))
|
||||||
.collect::<Vec<_>>();
|
.map(|entry| Ok(lsr.gen_result_entry(entry?)))
|
||||||
res.push(lsr.gen_success());
|
// If the processing succeeds, add a success message at the end.
|
||||||
res
|
.chain(std::iter::once(Ok(lsr.gen_success())))
|
||||||
|
.collect::<Result<Vec<_>>>()
|
||||||
|
.unwrap_or_else(|e| vec![lsr.gen_error(LdapResultCode::NoSuchAttribute, e.to_string())])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_whoami(&mut self, wr: &WhoamiRequest) -> LdapMsg {
|
pub fn do_whoami(&mut self, wr: &WhoamiRequest) -> LdapMsg {
|
||||||
@ -210,22 +211,22 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_subtree() {
|
fn test_is_subtree() {
|
||||||
let subtree1 = vec![
|
let subtree1 = &[
|
||||||
("ou".to_string(), "people".to_string()),
|
("ou".to_string(), "people".to_string()),
|
||||||
("dc".to_string(), "example".to_string()),
|
("dc".to_string(), "example".to_string()),
|
||||||
("dc".to_string(), "com".to_string()),
|
("dc".to_string(), "com".to_string()),
|
||||||
];
|
];
|
||||||
let root = vec![
|
let root = &[
|
||||||
("dc".to_string(), "example".to_string()),
|
("dc".to_string(), "example".to_string()),
|
||||||
("dc".to_string(), "com".to_string()),
|
("dc".to_string(), "com".to_string()),
|
||||||
];
|
];
|
||||||
assert!(is_subtree(&subtree1, &root));
|
assert!(is_subtree(subtree1, root));
|
||||||
assert!(!is_subtree(&vec![], &root));
|
assert!(!is_subtree(&[], root));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_distinguished_name() {
|
fn test_parse_distinguished_name() {
|
||||||
let parsed_dn = vec![
|
let parsed_dn = &[
|
||||||
("ou".to_string(), "people".to_string()),
|
("ou".to_string(), "people".to_string()),
|
||||||
("dc".to_string(), "example".to_string()),
|
("dc".to_string(), "example".to_string()),
|
||||||
("dc".to_string(), "com".to_string()),
|
("dc".to_string(), "com".to_string()),
|
||||||
@ -241,7 +242,7 @@ mod tests {
|
|||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
mock.expect_bind().return_once(|_| Ok(()));
|
mock.expect_bind().return_once(|_| Ok(()));
|
||||||
mock.expect_list_users()
|
mock.expect_list_users()
|
||||||
.with(eq(ListUsersRequest { attrs: vec![] }))
|
.with(eq(ListUsersRequest {}))
|
||||||
.times(1)
|
.times(1)
|
||||||
.return_once(|_| {
|
.return_once(|_| {
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
@ -275,7 +276,14 @@ mod tests {
|
|||||||
base: "ou=people,dc=example,dc=com".to_string(),
|
base: "ou=people,dc=example,dc=com".to_string(),
|
||||||
scope: LdapSearchScope::Base,
|
scope: LdapSearchScope::Base,
|
||||||
filter: LdapFilter::And(vec![]),
|
filter: LdapFilter::And(vec![]),
|
||||||
attrs: vec![],
|
attrs: vec![
|
||||||
|
"objectClass".to_string(),
|
||||||
|
"uid".to_string(),
|
||||||
|
"mail".to_string(),
|
||||||
|
"givenName".to_string(),
|
||||||
|
"sn".to_string(),
|
||||||
|
"cn".to_string(),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ldap_handler.do_search(&request),
|
ldap_handler.do_search(&request),
|
||||||
@ -285,7 +293,11 @@ mod tests {
|
|||||||
attributes: vec![
|
attributes: vec![
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
atype: "objectClass".to_string(),
|
atype: "objectClass".to_string(),
|
||||||
vals: vec!["inetOrgPerson".to_string(), "posixAccount".to_string(), "mailAccount".to_string()]
|
vals: vec![
|
||||||
|
"inetOrgPerson".to_string(),
|
||||||
|
"posixAccount".to_string(),
|
||||||
|
"mailAccount".to_string()
|
||||||
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
atype: "uid".to_string(),
|
atype: "uid".to_string(),
|
||||||
@ -314,7 +326,11 @@ mod tests {
|
|||||||
attributes: vec![
|
attributes: vec![
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
atype: "objectClass".to_string(),
|
atype: "objectClass".to_string(),
|
||||||
vals: vec!["inetOrgPerson".to_string(), "posixAccount".to_string(), "mailAccount".to_string()]
|
vals: vec![
|
||||||
|
"inetOrgPerson".to_string(),
|
||||||
|
"posixAccount".to_string(),
|
||||||
|
"mailAccount".to_string()
|
||||||
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
atype: "uid".to_string(),
|
atype: "uid".to_string(),
|
||||||
|
@ -20,10 +20,7 @@ async fn handle_incoming_message<Backend: BackendHandler>(
|
|||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
let server_op = match msg
|
let server_op = match msg.map_err(|_e| ()).and_then(ServerOps::try_from) {
|
||||||
.map_err(|_e| ())
|
|
||||||
.and_then(|msg| ServerOps::try_from(msg))
|
|
||||||
{
|
|
||||||
Ok(a_value) => a_value,
|
Ok(a_value) => a_value,
|
||||||
Err(an_error) => {
|
Err(an_error) => {
|
||||||
let _err = resp
|
let _err = resp
|
||||||
|
@ -23,7 +23,7 @@ where
|
|||||||
|
|
||||||
let count = Arc::new(AtomicUsize::new(0));
|
let count = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
Ok(server_builder
|
server_builder
|
||||||
.bind("http", ("0.0.0.0", config.http_port), move || {
|
.bind("http", ("0.0.0.0", config.http_port), move || {
|
||||||
let count = Arc::clone(&count);
|
let count = Arc::clone(&count);
|
||||||
let num2 = Arc::clone(&count);
|
let num2 = Arc::clone(&count);
|
||||||
@ -73,5 +73,5 @@ where
|
|||||||
"While bringing up the TCP server with port {}",
|
"While bringing up the TCP server with port {}",
|
||||||
config.http_port
|
config.http_port
|
||||||
)
|
)
|
||||||
})?)
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user