server: return custom attributes when asked for all attributes
This commit is contained in:
committed by
nitnelave
parent
df188ee83f
commit
a190fe7ddf
@@ -7,31 +7,28 @@ use tracing::{debug, instrument, warn};
|
||||
use crate::domain::{
|
||||
deserialize::deserialize_attribute_value,
|
||||
handler::{GroupListerBackendHandler, GroupRequestFilter},
|
||||
ldap::error::LdapError,
|
||||
ldap::{
|
||||
error::{LdapError, LdapResult},
|
||||
utils::{
|
||||
expand_attribute_wildcards, get_custom_attribute,
|
||||
get_group_id_from_distinguished_name_or_plain_name,
|
||||
get_user_id_from_distinguished_name_or_plain_name, map_group_field, ExpandedAttributes,
|
||||
GroupFieldType, LdapInfo,
|
||||
},
|
||||
},
|
||||
schema::{PublicSchema, SchemaGroupAttributeExtractor},
|
||||
types::{AttributeName, AttributeType, Group, LdapObjectClass, UserId, Uuid},
|
||||
};
|
||||
|
||||
use super::{
|
||||
error::LdapResult,
|
||||
utils::{
|
||||
expand_attribute_wildcards, get_custom_attribute,
|
||||
get_group_id_from_distinguished_name_or_plain_name,
|
||||
get_user_id_from_distinguished_name_or_plain_name, map_group_field, GroupFieldType,
|
||||
LdapInfo,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn get_group_attribute(
|
||||
group: &Group,
|
||||
base_dn_str: &str,
|
||||
attribute: &str,
|
||||
attribute: &AttributeName,
|
||||
user_filter: &Option<UserId>,
|
||||
ignored_group_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let attribute = AttributeName::from(attribute);
|
||||
let attribute_values = match map_group_field(&attribute, schema) {
|
||||
let attribute_values = match map_group_field(attribute, schema) {
|
||||
GroupFieldType::ObjectClass => {
|
||||
let mut classes = vec![b"groupOfUniqueNames".to_vec()];
|
||||
classes.extend(
|
||||
@@ -74,12 +71,12 @@ pub fn get_group_attribute(
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
if ignored_group_attributes.contains(&attribute) {
|
||||
if ignored_group_attributes.contains(attribute) {
|
||||
return None;
|
||||
}
|
||||
get_custom_attribute::<SchemaGroupAttributeExtractor>(
|
||||
&group.attributes,
|
||||
&attribute,
|
||||
attribute,
|
||||
schema,
|
||||
).or_else(||{warn!(
|
||||
r#"Ignoring unrecognized group attribute: {}\n\
|
||||
@@ -105,33 +102,42 @@ const ALL_GROUP_ATTRIBUTE_KEYS: &[&str] = &[
|
||||
"entryuuid",
|
||||
];
|
||||
|
||||
fn expand_group_attribute_wildcards(attributes: &[String]) -> Vec<&str> {
|
||||
fn expand_group_attribute_wildcards(attributes: &[String]) -> ExpandedAttributes {
|
||||
expand_attribute_wildcards(attributes, ALL_GROUP_ATTRIBUTE_KEYS)
|
||||
}
|
||||
|
||||
fn make_ldap_search_group_result_entry(
|
||||
group: Group,
|
||||
base_dn_str: &str,
|
||||
expanded_attributes: &[&str],
|
||||
mut expanded_attributes: ExpandedAttributes,
|
||||
user_filter: &Option<UserId>,
|
||||
ignored_group_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> LdapSearchResultEntry {
|
||||
if expanded_attributes.include_custom_attributes {
|
||||
expanded_attributes.attribute_keys.extend(
|
||||
group
|
||||
.attributes
|
||||
.iter()
|
||||
.map(|a| (a.name.clone(), a.name.to_string())),
|
||||
);
|
||||
}
|
||||
LdapSearchResultEntry {
|
||||
dn: format!("cn={},ou=groups,{}", group.display_name, base_dn_str),
|
||||
attributes: expanded_attributes
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
.attribute_keys
|
||||
.into_iter()
|
||||
.filter_map(|(attribute, name)| {
|
||||
let values = get_group_attribute(
|
||||
&group,
|
||||
base_dn_str,
|
||||
a,
|
||||
&attribute,
|
||||
user_filter,
|
||||
ignored_group_attributes,
|
||||
schema,
|
||||
)?;
|
||||
Some(LdapPartialAttribute {
|
||||
atype: a.to_string(),
|
||||
atype: name,
|
||||
vals: values,
|
||||
})
|
||||
})
|
||||
@@ -295,7 +301,7 @@ pub fn convert_groups_to_ldap_op<'a>(
|
||||
LdapOp::SearchResultEntry(make_ldap_search_group_result_entry(
|
||||
g,
|
||||
&ldap_info.base_dn_str,
|
||||
expanded_attributes.as_ref().unwrap(),
|
||||
expanded_attributes.clone().unwrap(),
|
||||
user_filter,
|
||||
&ldap_info.ignored_group_attributes,
|
||||
schema,
|
||||
|
||||
@@ -12,8 +12,8 @@ use crate::domain::{
|
||||
utils::{
|
||||
expand_attribute_wildcards, get_custom_attribute,
|
||||
get_group_id_from_distinguished_name_or_plain_name,
|
||||
get_user_id_from_distinguished_name_or_plain_name, map_user_field, LdapInfo,
|
||||
UserFieldType,
|
||||
get_user_id_from_distinguished_name_or_plain_name, map_user_field, ExpandedAttributes,
|
||||
LdapInfo, UserFieldType,
|
||||
},
|
||||
},
|
||||
schema::{PublicSchema, SchemaUserAttributeExtractor},
|
||||
@@ -25,14 +25,13 @@ use crate::domain::{
|
||||
|
||||
pub fn get_user_attribute(
|
||||
user: &User,
|
||||
attribute: &str,
|
||||
attribute: &AttributeName,
|
||||
base_dn_str: &str,
|
||||
groups: Option<&[GroupDetails]>,
|
||||
ignored_user_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let attribute = AttributeName::from(attribute);
|
||||
let attribute_values = match map_user_field(&attribute, schema) {
|
||||
let attribute_values = match map_user_field(attribute, schema) {
|
||||
UserFieldType::ObjectClass => {
|
||||
let mut classes = vec![
|
||||
b"inetOrgPerson".to_vec(),
|
||||
@@ -93,12 +92,12 @@ pub fn get_user_attribute(
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
if ignored_user_attributes.contains(&attribute) {
|
||||
if ignored_user_attributes.contains(attribute) {
|
||||
return None;
|
||||
}
|
||||
get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
&attribute,
|
||||
attribute,
|
||||
schema,
|
||||
)
|
||||
.or_else(|| {
|
||||
@@ -134,27 +133,34 @@ const ALL_USER_ATTRIBUTE_KEYS: &[&str] = &[
|
||||
fn make_ldap_search_user_result_entry(
|
||||
user: User,
|
||||
base_dn_str: &str,
|
||||
expanded_attributes: &[&str],
|
||||
mut expanded_attributes: ExpandedAttributes,
|
||||
groups: Option<&[GroupDetails]>,
|
||||
ignored_user_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> LdapSearchResultEntry {
|
||||
let dn = format!("uid={},ou=people,{}", user.user_id.as_str(), base_dn_str);
|
||||
if expanded_attributes.include_custom_attributes {
|
||||
expanded_attributes.attribute_keys.extend(
|
||||
user.attributes
|
||||
.iter()
|
||||
.map(|a| (a.name.clone(), a.name.to_string())),
|
||||
);
|
||||
}
|
||||
LdapSearchResultEntry {
|
||||
dn,
|
||||
dn: format!("uid={},ou=people,{}", user.user_id.as_str(), base_dn_str),
|
||||
attributes: expanded_attributes
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
.attribute_keys
|
||||
.into_iter()
|
||||
.filter_map(|(attribute, name)| {
|
||||
let values = get_user_attribute(
|
||||
&user,
|
||||
a,
|
||||
&attribute,
|
||||
base_dn_str,
|
||||
groups,
|
||||
ignored_user_attributes,
|
||||
schema,
|
||||
)?;
|
||||
Some(LdapPartialAttribute {
|
||||
atype: a.to_string(),
|
||||
atype: name,
|
||||
vals: values,
|
||||
})
|
||||
})
|
||||
@@ -295,7 +301,7 @@ fn convert_user_filter(
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_user_attribute_wildcards(attributes: &[String]) -> Vec<&str> {
|
||||
fn expand_user_attribute_wildcards(attributes: &[String]) -> ExpandedAttributes {
|
||||
expand_attribute_wildcards(attributes, ALL_USER_ATTRIBUTE_KEYS)
|
||||
}
|
||||
|
||||
@@ -334,7 +340,7 @@ pub fn convert_users_to_ldap_op<'a>(
|
||||
LdapOp::SearchResultEntry(make_ldap_search_user_result_entry(
|
||||
u.user,
|
||||
&ldap_info.base_dn_str,
|
||||
expanded_attributes.as_ref().unwrap(),
|
||||
expanded_attributes.clone().unwrap(),
|
||||
u.groups.as_deref(),
|
||||
&ldap_info.ignored_user_attributes,
|
||||
schema,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use chrono::{NaiveDateTime, TimeZone};
|
||||
use itertools::Itertools;
|
||||
use ldap3_proto::{proto::LdapSubstringFilter, LdapResultCode};
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
@@ -137,30 +138,39 @@ pub fn get_group_id_from_distinguished_name_or_plain_name(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExpandedAttributes {
|
||||
// Lowercase name to original name.
|
||||
pub attribute_keys: BTreeMap<AttributeName, String>,
|
||||
pub include_custom_attributes: bool,
|
||||
}
|
||||
|
||||
#[instrument(skip(all_attribute_keys), level = "debug")]
|
||||
pub fn expand_attribute_wildcards<'a>(
|
||||
ldap_attributes: &'a [String],
|
||||
all_attribute_keys: &'a [&'static str],
|
||||
) -> Vec<&'a str> {
|
||||
let extra_attributes =
|
||||
pub fn expand_attribute_wildcards(
|
||||
ldap_attributes: &[String],
|
||||
all_attribute_keys: &[&'static str],
|
||||
) -> ExpandedAttributes {
|
||||
let mut include_custom_attributes = false;
|
||||
let mut attributes_out: BTreeMap<_, _> = ldap_attributes
|
||||
.iter()
|
||||
.filter(|&s| s != "*" && s != "+" && s != "1.1")
|
||||
.map(|s| (AttributeName::from(s), s.to_string()))
|
||||
.collect();
|
||||
attributes_out.extend(
|
||||
if ldap_attributes.iter().any(|x| x == "*") || ldap_attributes.is_empty() {
|
||||
include_custom_attributes = true;
|
||||
all_attribute_keys
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
.iter()
|
||||
.copied();
|
||||
let attributes_out = ldap_attributes
|
||||
.iter()
|
||||
.map(|s| s.as_str())
|
||||
.filter(|&s| s != "*" && s != "+" && s != "1.1");
|
||||
|
||||
// Deduplicate, preserving order
|
||||
let resolved_attributes = itertools::chain(attributes_out, extra_attributes)
|
||||
.unique_by(|a| a.to_ascii_lowercase())
|
||||
.collect_vec();
|
||||
debug!(?resolved_attributes);
|
||||
resolved_attributes
|
||||
.map(|&s| (AttributeName::from(s), s.to_string())),
|
||||
);
|
||||
debug!(?attributes_out);
|
||||
ExpandedAttributes {
|
||||
attribute_keys: attributes_out,
|
||||
include_custom_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_subtree(subtree: &[(String, String)], base_tree: &[(String, String)]) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user