server: Add support for querying the OUs
This commit is contained in:
committed by
nitnelave
parent
47b308f9b7
commit
7671b61a6b
@@ -40,11 +40,23 @@ enum SearchScope {
|
||||
Groups,
|
||||
User(LdapFilter),
|
||||
Group(LdapFilter),
|
||||
UserOuOnly,
|
||||
GroupOuOnly,
|
||||
Unknown,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
fn get_search_scope(base_dn: &[(String, String)], dn_parts: &[(String, String)]) -> SearchScope {
|
||||
enum InternalSearchResults {
|
||||
UsersAndGroups(Vec<UserAndGroups>, Vec<Group>),
|
||||
Raw(Vec<LdapOp>),
|
||||
Empty,
|
||||
}
|
||||
|
||||
fn get_search_scope(
|
||||
base_dn: &[(String, String)],
|
||||
dn_parts: &[(String, String)],
|
||||
ldap_scope: &LdapSearchScope,
|
||||
) -> SearchScope {
|
||||
let base_dn_len = base_dn.len();
|
||||
if !is_subtree(dn_parts, base_dn) {
|
||||
SearchScope::Invalid
|
||||
@@ -53,11 +65,19 @@ fn get_search_scope(base_dn: &[(String, String)], dn_parts: &[(String, String)])
|
||||
} else if dn_parts.len() == base_dn_len + 1
|
||||
&& dn_parts[0] == ("ou".to_string(), "people".to_string())
|
||||
{
|
||||
SearchScope::Users
|
||||
if matches!(ldap_scope, LdapSearchScope::Base) {
|
||||
SearchScope::UserOuOnly
|
||||
} else {
|
||||
SearchScope::Users
|
||||
}
|
||||
} else if dn_parts.len() == base_dn_len + 1
|
||||
&& dn_parts[0] == ("ou".to_string(), "groups".to_string())
|
||||
{
|
||||
SearchScope::Groups
|
||||
if matches!(ldap_scope, LdapSearchScope::Base) {
|
||||
SearchScope::GroupOuOnly
|
||||
} else {
|
||||
SearchScope::Groups
|
||||
}
|
||||
} else if dn_parts.len() == base_dn_len + 2
|
||||
&& dn_parts[1] == ("ou".to_string(), "people".to_string())
|
||||
{
|
||||
@@ -84,7 +104,7 @@ fn make_search_request<S: Into<String>>(
|
||||
) -> LdapSearchRequest {
|
||||
LdapSearchRequest {
|
||||
base: base.to_string(),
|
||||
scope: LdapSearchScope::Base,
|
||||
scope: LdapSearchScope::Subtree,
|
||||
aliases: LdapDerefAliases::Never,
|
||||
sizelimit: 0,
|
||||
timelimit: 0,
|
||||
@@ -503,9 +523,9 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
&self,
|
||||
backend_handler: &impl UserAndGroupListerBackendHandler,
|
||||
request: &LdapSearchRequest,
|
||||
) -> LdapResult<(Option<Vec<UserAndGroups>>, Option<Vec<Group>>)> {
|
||||
) -> LdapResult<InternalSearchResults> {
|
||||
let dn_parts = parse_distinguished_name(&request.base.to_ascii_lowercase())?;
|
||||
let scope = get_search_scope(&self.ldap_info.base_dn, &dn_parts);
|
||||
let scope = get_search_scope(&self.ldap_info.base_dn, &dn_parts, &request.scope);
|
||||
debug!(?request.base, ?scope);
|
||||
// Disambiguate the lifetimes.
|
||||
fn cast<'a, T, R>(x: T) -> T
|
||||
@@ -533,26 +553,41 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
get_groups_list(&self.ldap_info, filter, &request.base, backend_handler).await
|
||||
});
|
||||
Ok(match scope {
|
||||
SearchScope::Global => (
|
||||
Some(get_user_list(&request.filter).await?),
|
||||
Some(get_group_list(&request.filter).await?),
|
||||
SearchScope::Global => InternalSearchResults::UsersAndGroups(
|
||||
get_user_list(&request.filter).await?,
|
||||
get_group_list(&request.filter).await?,
|
||||
),
|
||||
SearchScope::Users => InternalSearchResults::UsersAndGroups(
|
||||
get_user_list(&request.filter).await?,
|
||||
Vec::new(),
|
||||
),
|
||||
SearchScope::Groups => InternalSearchResults::UsersAndGroups(
|
||||
Vec::new(),
|
||||
get_group_list(&request.filter).await?,
|
||||
),
|
||||
SearchScope::Users => (Some(get_user_list(&request.filter).await?), None),
|
||||
SearchScope::Groups => (None, Some(get_group_list(&request.filter).await?)),
|
||||
SearchScope::User(filter) => {
|
||||
let filter = LdapFilter::And(vec![request.filter.clone(), filter]);
|
||||
(Some(get_user_list(&filter).await?), None)
|
||||
InternalSearchResults::UsersAndGroups(get_user_list(&filter).await?, Vec::new())
|
||||
}
|
||||
SearchScope::Group(filter) => {
|
||||
let filter = LdapFilter::And(vec![request.filter.clone(), filter]);
|
||||
(None, Some(get_group_list(&filter).await?))
|
||||
InternalSearchResults::UsersAndGroups(Vec::new(), get_group_list(&filter).await?)
|
||||
}
|
||||
SearchScope::UserOuOnly | SearchScope::GroupOuOnly => {
|
||||
InternalSearchResults::Raw(vec![LdapOp::SearchResultEntry(LdapSearchResultEntry {
|
||||
dn: request.base.clone(),
|
||||
attributes: vec![LdapPartialAttribute {
|
||||
atype: "objectClass".to_owned(),
|
||||
vals: vec![b"top".to_vec(), b"organizationalUnit".to_vec()],
|
||||
}],
|
||||
})])
|
||||
}
|
||||
SearchScope::Unknown => {
|
||||
warn!(
|
||||
r#"The requested search tree "{}" matches neither the user subtree "ou=people,{}" nor the group subtree "ou=groups,{}""#,
|
||||
&request.base, &self.ldap_info.base_dn_str, &self.ldap_info.base_dn_str
|
||||
);
|
||||
(None, None)
|
||||
InternalSearchResults::Empty
|
||||
}
|
||||
SearchScope::Invalid => {
|
||||
// Search path is not in our tree, just return an empty success.
|
||||
@@ -560,7 +595,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
"The specified search tree {:?} is not under the common subtree {:?}",
|
||||
&dn_parts, &self.ldap_info.base_dn
|
||||
);
|
||||
(None, None)
|
||||
InternalSearchResults::Empty
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -574,31 +609,27 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||
let backend_handler = self
|
||||
.backend_handler
|
||||
.get_user_restricted_lister_handler(user_info);
|
||||
let (users, groups) = self.do_search_internal(&backend_handler, request).await?;
|
||||
let search_results = self.do_search_internal(&backend_handler, request).await?;
|
||||
|
||||
let schema = backend_handler.get_schema().await.map_err(|e| LdapError {
|
||||
code: LdapResultCode::OperationsError,
|
||||
message: format!("Unable to get schema: {:#}", e),
|
||||
})?;
|
||||
let mut results = Vec::new();
|
||||
if let Some(users) = users {
|
||||
results.extend(convert_users_to_ldap_op(
|
||||
users,
|
||||
&request.attrs,
|
||||
&self.ldap_info,
|
||||
&schema,
|
||||
));
|
||||
}
|
||||
if let Some(groups) = groups {
|
||||
results.extend(convert_groups_to_ldap_op(
|
||||
groups,
|
||||
&request.attrs,
|
||||
&self.ldap_info,
|
||||
&backend_handler.user_filter,
|
||||
));
|
||||
}
|
||||
if results.is_empty() || matches!(results[results.len() - 1], LdapOp::SearchResultEntry(_))
|
||||
{
|
||||
let mut results = match search_results {
|
||||
InternalSearchResults::UsersAndGroups(users, groups) => {
|
||||
convert_users_to_ldap_op(users, &request.attrs, &self.ldap_info, &schema)
|
||||
.chain(convert_groups_to_ldap_op(
|
||||
groups,
|
||||
&request.attrs,
|
||||
&self.ldap_info,
|
||||
&backend_handler.user_filter,
|
||||
))
|
||||
.collect()
|
||||
}
|
||||
InternalSearchResults::Raw(raw_results) => raw_results,
|
||||
InternalSearchResults::Empty => Vec::new(),
|
||||
};
|
||||
if !matches!(results.last(), Some(LdapOp::SearchResultDone(_))) {
|
||||
results.push(make_search_success());
|
||||
}
|
||||
Ok(results)
|
||||
@@ -2652,4 +2683,32 @@ mod tests {
|
||||
})])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_user_ou_search() {
|
||||
let mut ldap_handler = setup_bound_readonly_handler(MockTestBackendHandler::new()).await;
|
||||
let request = LdapSearchRequest {
|
||||
base: "ou=people,dc=example,dc=com".to_owned(),
|
||||
scope: LdapSearchScope::Base,
|
||||
aliases: LdapDerefAliases::Never,
|
||||
sizelimit: 0,
|
||||
timelimit: 0,
|
||||
typesonly: false,
|
||||
filter: LdapFilter::And(vec![]),
|
||||
attrs: Vec::new(),
|
||||
};
|
||||
assert_eq!(
|
||||
ldap_handler.do_search_or_dse(&request).await,
|
||||
Ok(vec![
|
||||
LdapOp::SearchResultEntry(LdapSearchResultEntry {
|
||||
dn: "ou=people,dc=example,dc=com".to_owned(),
|
||||
attributes: vec![LdapPartialAttribute {
|
||||
atype: "objectClass".to_owned(),
|
||||
vals: vec![b"top".to_vec(), b"organizationalUnit".to_vec()]
|
||||
}]
|
||||
}),
|
||||
make_search_success()
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user