server: make attributes names, group names and emails case insensitive
In addition, group names and emails keep their casing
This commit is contained in:
committed by
nitnelave
parent
71d37b9e5e
commit
272c84c574
@@ -7,7 +7,7 @@ use crate::domain::{
|
||||
handler::{GroupListerBackendHandler, GroupRequestFilter},
|
||||
ldap::error::LdapError,
|
||||
schema::{PublicSchema, SchemaGroupAttributeExtractor},
|
||||
types::{Group, UserId, Uuid},
|
||||
types::{AttributeName, Group, UserId, Uuid},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -23,15 +23,15 @@ pub fn get_group_attribute(
|
||||
base_dn_str: &str,
|
||||
attribute: &str,
|
||||
user_filter: &Option<UserId>,
|
||||
ignored_group_attributes: &[String],
|
||||
ignored_group_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let attribute = attribute.to_ascii_lowercase();
|
||||
let attribute = AttributeName::from(attribute);
|
||||
let attribute_values = match attribute.as_str() {
|
||||
"objectclass" => vec![b"groupOfUniqueNames".to_vec()],
|
||||
// Always returned as part of the base response.
|
||||
"dn" | "distinguishedname" => return None,
|
||||
"cn" | "uid" | "id" => vec![group.display_name.clone().into_bytes()],
|
||||
"cn" | "uid" | "id" => vec![group.display_name.to_string().into_bytes()],
|
||||
"entryuuid" | "uuid" => vec![group.uuid.to_string().into_bytes()],
|
||||
"member" | "uniquemember" => group
|
||||
.users
|
||||
@@ -48,11 +48,11 @@ pub fn get_group_attribute(
|
||||
attribute
|
||||
)
|
||||
}
|
||||
attr => {
|
||||
_ => {
|
||||
if !ignored_group_attributes.contains(&attribute) {
|
||||
match get_custom_attribute::<SchemaGroupAttributeExtractor>(
|
||||
&group.attributes,
|
||||
attr,
|
||||
&attribute,
|
||||
schema,
|
||||
) {
|
||||
Some(v) => return Some(v),
|
||||
@@ -91,7 +91,7 @@ fn make_ldap_search_group_result_entry(
|
||||
base_dn_str: &str,
|
||||
attributes: &[String],
|
||||
user_filter: &Option<UserId>,
|
||||
ignored_group_attributes: &[String],
|
||||
ignored_group_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> LdapSearchResultEntry {
|
||||
let expanded_attributes = expand_group_attribute_wildcards(attributes);
|
||||
@@ -125,12 +125,12 @@ fn convert_group_filter(
|
||||
let rec = |f| convert_group_filter(ldap_info, f);
|
||||
match filter {
|
||||
LdapFilter::Equality(field, value) => {
|
||||
let field = &field.to_ascii_lowercase();
|
||||
let value = &value.to_ascii_lowercase();
|
||||
let field = AttributeName::from(field.as_str());
|
||||
let value = value.to_ascii_lowercase();
|
||||
match field.as_str() {
|
||||
"member" | "uniquemember" => {
|
||||
let user_name = get_user_id_from_distinguished_name(
|
||||
value,
|
||||
&value,
|
||||
&ldap_info.base_dn,
|
||||
&ldap_info.base_dn_str,
|
||||
)?;
|
||||
@@ -150,8 +150,8 @@ fn convert_group_filter(
|
||||
warn!("Invalid dn filter on group: {}", value);
|
||||
GroupRequestFilter::from(false)
|
||||
})),
|
||||
_ => match map_group_field(field) {
|
||||
Some("display_name") => Ok(GroupRequestFilter::DisplayName(value.to_string())),
|
||||
_ => match map_group_field(&field) {
|
||||
Some("display_name") => Ok(GroupRequestFilter::DisplayName(value.into())),
|
||||
Some("uuid") => Ok(GroupRequestFilter::Uuid(
|
||||
Uuid::try_from(value.as_str()).map_err(|e| LdapError {
|
||||
code: LdapResultCode::InappropriateMatching,
|
||||
@@ -159,9 +159,9 @@ fn convert_group_filter(
|
||||
})?,
|
||||
)),
|
||||
_ => {
|
||||
if !ldap_info.ignored_group_attributes.contains(field) {
|
||||
if !ldap_info.ignored_group_attributes.contains(&field) {
|
||||
warn!(
|
||||
r#"Ignoring unknown group attribute "{:?}" in filter.\n\
|
||||
r#"Ignoring unknown group attribute "{}" in filter.\n\
|
||||
To disable this warning, add it to "ignored_group_attributes" in the config."#,
|
||||
field
|
||||
);
|
||||
@@ -179,24 +179,24 @@ fn convert_group_filter(
|
||||
)),
|
||||
LdapFilter::Not(filter) => Ok(GroupRequestFilter::Not(Box::new(rec(filter)?))),
|
||||
LdapFilter::Present(field) => {
|
||||
let field = &field.to_ascii_lowercase();
|
||||
let field = AttributeName::from(field.as_str());
|
||||
Ok(GroupRequestFilter::from(
|
||||
field == "objectclass"
|
||||
|| field == "dn"
|
||||
|| field == "distinguishedname"
|
||||
|| map_group_field(field).is_some(),
|
||||
field.as_str() == "objectclass"
|
||||
|| field.as_str() == "dn"
|
||||
|| field.as_str() == "distinguishedname"
|
||||
|| map_group_field(&field).is_some(),
|
||||
))
|
||||
}
|
||||
LdapFilter::Substring(field, substring_filter) => {
|
||||
let field = &field.to_ascii_lowercase();
|
||||
match map_group_field(field.as_str()) {
|
||||
let field = AttributeName::from(field.as_str());
|
||||
match map_group_field(&field) {
|
||||
Some("display_name") => Ok(GroupRequestFilter::DisplayNameSubString(
|
||||
substring_filter.clone().into(),
|
||||
)),
|
||||
_ => Err(LdapError {
|
||||
code: LdapResultCode::UnwillingToPerform,
|
||||
message: format!(
|
||||
"Unsupported group attribute for substring filter: {:?}",
|
||||
"Unsupported group attribute for substring filter: \"{}\"",
|
||||
field
|
||||
),
|
||||
}),
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::domain::{
|
||||
},
|
||||
},
|
||||
schema::{PublicSchema, SchemaUserAttributeExtractor},
|
||||
types::{GroupDetails, User, UserAndGroups, UserColumn, UserId},
|
||||
types::{AttributeName, GroupDetails, User, UserAndGroups, UserColumn, UserId},
|
||||
};
|
||||
|
||||
pub fn get_user_attribute(
|
||||
@@ -22,10 +22,10 @@ pub fn get_user_attribute(
|
||||
attribute: &str,
|
||||
base_dn_str: &str,
|
||||
groups: Option<&[GroupDetails]>,
|
||||
ignored_user_attributes: &[String],
|
||||
ignored_user_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let attribute = attribute.to_ascii_lowercase();
|
||||
let attribute = AttributeName::from(attribute);
|
||||
let attribute_values = match attribute.as_str() {
|
||||
"objectclass" => vec![
|
||||
b"inetOrgPerson".to_vec(),
|
||||
@@ -37,20 +37,22 @@ pub fn get_user_attribute(
|
||||
"dn" | "distinguishedname" => return None,
|
||||
"uid" | "user_id" | "id" => vec![user.user_id.to_string().into_bytes()],
|
||||
"entryuuid" | "uuid" => vec![user.uuid.to_string().into_bytes()],
|
||||
"mail" | "email" => vec![user.email.clone().into_bytes()],
|
||||
"givenname" | "first_name" | "firstname" => get_custom_attribute::<
|
||||
SchemaUserAttributeExtractor,
|
||||
>(
|
||||
&user.attributes, "first_name", schema
|
||||
)?,
|
||||
"mail" | "email" => vec![user.email.to_string().into_bytes()],
|
||||
"givenname" | "first_name" | "firstname" => {
|
||||
get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
&"first_name".into(),
|
||||
schema,
|
||||
)?
|
||||
}
|
||||
"sn" | "last_name" | "lastname" => get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
"last_name",
|
||||
&"last_name".into(),
|
||||
schema,
|
||||
)?,
|
||||
"jpegphoto" | "avatar" => get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
"avatar",
|
||||
&"avatar".into(),
|
||||
schema,
|
||||
)?,
|
||||
"memberof" => groups
|
||||
@@ -80,7 +82,7 @@ pub fn get_user_attribute(
|
||||
if !ignored_user_attributes.contains(&attribute) {
|
||||
match get_custom_attribute::<SchemaUserAttributeExtractor>(
|
||||
&user.attributes,
|
||||
attr,
|
||||
&attribute,
|
||||
schema,
|
||||
) {
|
||||
Some(v) => return Some(v),
|
||||
@@ -118,7 +120,7 @@ fn make_ldap_search_user_result_entry(
|
||||
base_dn_str: &str,
|
||||
attributes: &[String],
|
||||
groups: Option<&[GroupDetails]>,
|
||||
ignored_user_attributes: &[String],
|
||||
ignored_user_attributes: &[AttributeName],
|
||||
schema: &PublicSchema,
|
||||
) -> LdapSearchResultEntry {
|
||||
let expanded_attributes = expand_user_attribute_wildcards(attributes);
|
||||
@@ -156,7 +158,7 @@ fn convert_user_filter(ldap_info: &LdapInfo, filter: &LdapFilter) -> LdapResult<
|
||||
)),
|
||||
LdapFilter::Not(filter) => Ok(UserRequestFilter::Not(Box::new(rec(filter)?))),
|
||||
LdapFilter::Equality(field, value) => {
|
||||
let field = &field.to_ascii_lowercase();
|
||||
let field = AttributeName::from(field.as_str());
|
||||
match field.as_str() {
|
||||
"memberof" => Ok(UserRequestFilter::MemberOf(
|
||||
get_group_id_from_distinguished_name(
|
||||
@@ -179,7 +181,7 @@ fn convert_user_filter(ldap_info: &LdapInfo, filter: &LdapFilter) -> LdapResult<
|
||||
warn!("Invalid dn filter on user: {}", value);
|
||||
UserRequestFilter::from(false)
|
||||
})),
|
||||
_ => match map_user_field(field) {
|
||||
_ => match map_user_field(&field) {
|
||||
UserFieldType::PrimaryField(UserColumn::UserId) => {
|
||||
Ok(UserRequestFilter::UserId(UserId::new(value)))
|
||||
}
|
||||
@@ -187,11 +189,11 @@ fn convert_user_filter(ldap_info: &LdapInfo, filter: &LdapFilter) -> LdapResult<
|
||||
Ok(UserRequestFilter::Equality(field, value.clone()))
|
||||
}
|
||||
UserFieldType::Attribute(field) => Ok(UserRequestFilter::AttributeEquality(
|
||||
field.to_owned(),
|
||||
AttributeName::from(field),
|
||||
value.clone(),
|
||||
)),
|
||||
UserFieldType::NoMatch => {
|
||||
if !ldap_info.ignored_user_attributes.contains(field) {
|
||||
if !ldap_info.ignored_user_attributes.contains(&field) {
|
||||
warn!(
|
||||
r#"Ignoring unknown user attribute "{}" in filter.\n\
|
||||
To disable this warning, add it to "ignored_user_attributes" in the config"#,
|
||||
@@ -204,18 +206,18 @@ fn convert_user_filter(ldap_info: &LdapInfo, filter: &LdapFilter) -> LdapResult<
|
||||
}
|
||||
}
|
||||
LdapFilter::Present(field) => {
|
||||
let field = &field.to_ascii_lowercase();
|
||||
let field = AttributeName::from(field.as_str());
|
||||
// Check that it's a field we support.
|
||||
Ok(UserRequestFilter::from(
|
||||
field == "objectclass"
|
||||
|| field == "dn"
|
||||
|| field == "distinguishedname"
|
||||
|| !matches!(map_user_field(field), UserFieldType::NoMatch),
|
||||
field.as_str() == "objectclass"
|
||||
|| field.as_str() == "dn"
|
||||
|| field.as_str() == "distinguishedname"
|
||||
|| !matches!(map_user_field(&field), UserFieldType::NoMatch),
|
||||
))
|
||||
}
|
||||
LdapFilter::Substring(field, substring_filter) => {
|
||||
let field = &field.to_ascii_lowercase();
|
||||
match map_user_field(field.as_str()) {
|
||||
let field = AttributeName::from(field.as_str());
|
||||
match map_user_field(&field) {
|
||||
UserFieldType::PrimaryField(UserColumn::UserId) => Ok(
|
||||
UserRequestFilter::UserIdSubString(substring_filter.clone().into()),
|
||||
),
|
||||
|
||||
@@ -7,7 +7,9 @@ use crate::domain::{
|
||||
handler::SubStringFilter,
|
||||
ldap::error::{LdapError, LdapResult},
|
||||
schema::{PublicSchema, SchemaAttributeExtractor},
|
||||
types::{AttributeType, AttributeValue, JpegPhoto, UserColumn, UserId},
|
||||
types::{
|
||||
AttributeName, AttributeType, AttributeValue, GroupName, JpegPhoto, UserColumn, UserId,
|
||||
},
|
||||
};
|
||||
|
||||
impl From<LdapSubstringFilter> for SubStringFilter {
|
||||
@@ -103,8 +105,8 @@ pub fn get_group_id_from_distinguished_name(
|
||||
dn: &str,
|
||||
base_tree: &[(String, String)],
|
||||
base_dn_str: &str,
|
||||
) -> LdapResult<String> {
|
||||
get_id_from_distinguished_name(dn, base_tree, base_dn_str, true)
|
||||
) -> LdapResult<GroupName> {
|
||||
get_id_from_distinguished_name(dn, base_tree, base_dn_str, true).map(GroupName::from)
|
||||
}
|
||||
|
||||
#[instrument(skip(all_attribute_keys), level = "debug")]
|
||||
@@ -160,9 +162,8 @@ pub enum UserFieldType {
|
||||
Attribute(&'static str),
|
||||
}
|
||||
|
||||
pub fn map_user_field(field: &str) -> UserFieldType {
|
||||
assert!(field == field.to_ascii_lowercase());
|
||||
match field {
|
||||
pub fn map_user_field(field: &AttributeName) -> UserFieldType {
|
||||
match field.as_str() {
|
||||
"uid" | "user_id" | "id" => UserFieldType::PrimaryField(UserColumn::UserId),
|
||||
"mail" | "email" => UserFieldType::PrimaryField(UserColumn::Email),
|
||||
"cn" | "displayname" | "display_name" => {
|
||||
@@ -179,9 +180,8 @@ pub fn map_user_field(field: &str) -> UserFieldType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_group_field(field: &str) -> Option<&'static str> {
|
||||
assert!(field == field.to_ascii_lowercase());
|
||||
Some(match field {
|
||||
pub fn map_group_field(field: &AttributeName) -> Option<&'static str> {
|
||||
Some(match field.as_str() {
|
||||
"cn" | "displayname" | "uid" | "display_name" => "display_name",
|
||||
"creationdate" | "createtimestamp" | "modifytimestamp" | "creation_date" => "creation_date",
|
||||
"entryuuid" | "uuid" => "uuid",
|
||||
@@ -192,13 +192,13 @@ pub fn map_group_field(field: &str) -> Option<&'static str> {
|
||||
pub struct LdapInfo {
|
||||
pub base_dn: Vec<(String, String)>,
|
||||
pub base_dn_str: String,
|
||||
pub ignored_user_attributes: Vec<String>,
|
||||
pub ignored_group_attributes: Vec<String>,
|
||||
pub ignored_user_attributes: Vec<AttributeName>,
|
||||
pub ignored_group_attributes: Vec<AttributeName>,
|
||||
}
|
||||
|
||||
pub fn get_custom_attribute<Extractor: SchemaAttributeExtractor>(
|
||||
attributes: &[AttributeValue],
|
||||
attribute_name: &str,
|
||||
attribute_name: &AttributeName,
|
||||
schema: &PublicSchema,
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let convert_date = |date| {
|
||||
@@ -212,7 +212,7 @@ pub fn get_custom_attribute<Extractor: SchemaAttributeExtractor>(
|
||||
.and_then(|attribute_type| {
|
||||
attributes
|
||||
.iter()
|
||||
.find(|a| a.name == attribute_name)
|
||||
.find(|a| &a.name == attribute_name)
|
||||
.map(|attribute| match attribute_type {
|
||||
(AttributeType::String, false) => {
|
||||
vec![attribute.value.unwrap::<String>().into_bytes()]
|
||||
|
||||
Reference in New Issue
Block a user