mirror of
				https://github.com/nitnelave/lldap.git
				synced 2023-04-12 14:25:13 +00:00 
			
		
		
		
	ldap: add an option to silence unknown fields in the config
This commit is contained in:
		
							parent
							
								
									a0b0b455ed
								
							
						
					
					
						commit
						1efab58d0c
					
				@ -78,6 +78,14 @@ database_url = "sqlite:///data/users.db?mode=rwc"
 | 
			
		||||
## Randomly generated on first run if it doesn't exist.
 | 
			
		||||
key_file = "/data/private_key"
 | 
			
		||||
 | 
			
		||||
## Ignored attributes.
 | 
			
		||||
## Some services will request attributes that are not present in LLDAP. When it
 | 
			
		||||
## is the case, LLDAP will warn about the attribute being unknown. If you want
 | 
			
		||||
## to ignore the attribute and the service works without, you can add it to this
 | 
			
		||||
## list to silence the warning.
 | 
			
		||||
#ignored_user_attributes = [ "sAMAccountName" ]
 | 
			
		||||
#ignored_group_attributes = [ "mail", "userPrincipalName" ]
 | 
			
		||||
 | 
			
		||||
## Options to configure SMTP parameters, to send password reset emails.
 | 
			
		||||
## To set these options from environment variables, use the following format
 | 
			
		||||
## (example with "password"): LLDAP_SMTP_OPTIONS__PASSWORD
 | 
			
		||||
 | 
			
		||||
@ -75,6 +75,10 @@ pub struct Configuration {
 | 
			
		||||
    pub ldap_user_pass: SecUtf8,
 | 
			
		||||
    #[builder(default = r#"String::from("sqlite://users.db?mode=rwc")"#)]
 | 
			
		||||
    pub database_url: String,
 | 
			
		||||
    #[builder(default)]
 | 
			
		||||
    pub ignored_user_attributes: Vec<String>,
 | 
			
		||||
    #[builder(default)]
 | 
			
		||||
    pub ignored_group_attributes: Vec<String>,
 | 
			
		||||
    #[builder(default = "false")]
 | 
			
		||||
    pub verbose: bool,
 | 
			
		||||
    #[builder(default = r#"String::from("server_key")"#)]
 | 
			
		||||
 | 
			
		||||
@ -102,8 +102,14 @@ fn get_user_id_from_distinguished_name(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_user_attribute(user: &User, attribute: &str, dn: &str) -> Result<Option<Vec<String>>> {
 | 
			
		||||
    Ok(Some(match attribute.to_ascii_lowercase().as_str() {
 | 
			
		||||
fn get_user_attribute(
 | 
			
		||||
    user: &User,
 | 
			
		||||
    attribute: &str,
 | 
			
		||||
    dn: &str,
 | 
			
		||||
    ignored_user_attributes: &[String],
 | 
			
		||||
) -> Result<Option<Vec<String>>> {
 | 
			
		||||
    let attribute = attribute.to_ascii_lowercase();
 | 
			
		||||
    Ok(Some(match attribute.as_str() {
 | 
			
		||||
        "objectclass" => vec![
 | 
			
		||||
            "inetOrgPerson".to_string(),
 | 
			
		||||
            "posixAccount".to_string(),
 | 
			
		||||
@ -118,7 +124,7 @@ fn get_user_attribute(user: &User, attribute: &str, dn: &str) -> Result<Option<V
 | 
			
		||||
        "cn" | "displayname" => vec![user.display_name.clone()],
 | 
			
		||||
        "createtimestamp" | "modifytimestamp" => vec![user.creation_date.to_rfc3339()],
 | 
			
		||||
        "1.1" => return Ok(None),
 | 
			
		||||
        // We ignore the operational attribute wildcard
 | 
			
		||||
        // We ignore the operational attribute wildcard.
 | 
			
		||||
        "+" => return Ok(None),
 | 
			
		||||
        "*" => {
 | 
			
		||||
            bail!(
 | 
			
		||||
@ -127,7 +133,13 @@ fn get_user_attribute(user: &User, attribute: &str, dn: &str) -> Result<Option<V
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        _ => {
 | 
			
		||||
            warn!("Ignoring unrecognized group attribute: {}", attribute);
 | 
			
		||||
            if !ignored_user_attributes.contains(&attribute) {
 | 
			
		||||
                warn!(
 | 
			
		||||
                    r#"Ignoring unrecognized group attribute: {}\n\
 | 
			
		||||
                      To disable this warning, add it to "ignored_user_attributes" in the config."#,
 | 
			
		||||
                    attribute
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        }
 | 
			
		||||
    }))
 | 
			
		||||
@ -164,6 +176,7 @@ fn make_ldap_search_user_result_entry(
 | 
			
		||||
    user: User,
 | 
			
		||||
    base_dn_str: &str,
 | 
			
		||||
    attributes: &[String],
 | 
			
		||||
    ignored_user_attributes: &[String],
 | 
			
		||||
) -> Result<LdapSearchResultEntry> {
 | 
			
		||||
    let dn = format!("uid={},ou=people,{}", user.user_id.as_str(), base_dn_str);
 | 
			
		||||
 | 
			
		||||
@ -173,7 +186,7 @@ fn make_ldap_search_user_result_entry(
 | 
			
		||||
        attributes: expanded_attributes
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter_map(|a| {
 | 
			
		||||
                let values = match get_user_attribute(&user, a, &dn) {
 | 
			
		||||
                let values = match get_user_attribute(&user, a, &dn, ignored_user_attributes) {
 | 
			
		||||
                    Err(e) => return Some(Err(e)),
 | 
			
		||||
                    Ok(v) => v,
 | 
			
		||||
                }?;
 | 
			
		||||
@ -191,8 +204,10 @@ fn get_group_attribute(
 | 
			
		||||
    base_dn_str: &str,
 | 
			
		||||
    attribute: &str,
 | 
			
		||||
    user_filter: &Option<&UserId>,
 | 
			
		||||
    ignored_group_attributes: &[String],
 | 
			
		||||
) -> Result<Option<Vec<String>>> {
 | 
			
		||||
    Ok(Some(match attribute.to_ascii_lowercase().as_str() {
 | 
			
		||||
    let attribute = attribute.to_ascii_lowercase();
 | 
			
		||||
    Ok(Some(match attribute.as_str() {
 | 
			
		||||
        "objectclass" => vec!["groupOfUniqueNames".to_string()],
 | 
			
		||||
        "dn" | "distinguishedname" => vec![format!(
 | 
			
		||||
            "cn={},ou=groups,{}",
 | 
			
		||||
@ -215,7 +230,13 @@ fn get_group_attribute(
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        _ => {
 | 
			
		||||
            warn!("Ignoring unrecognized group attribute: {}", attribute);
 | 
			
		||||
            if !ignored_group_attributes.contains(&attribute) {
 | 
			
		||||
                warn!(
 | 
			
		||||
                    r#"Ignoring unrecognized group attribute: {}\n\
 | 
			
		||||
                      To disable this warning, add it to "ignored_group_attributes" in the config."#,
 | 
			
		||||
                    attribute
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        }
 | 
			
		||||
    }))
 | 
			
		||||
@ -229,6 +250,7 @@ fn make_ldap_search_group_result_entry(
 | 
			
		||||
    base_dn_str: &str,
 | 
			
		||||
    attributes: &[String],
 | 
			
		||||
    user_filter: &Option<&UserId>,
 | 
			
		||||
    ignored_group_attributes: &[String],
 | 
			
		||||
) -> Result<LdapSearchResultEntry> {
 | 
			
		||||
    let expanded_attributes = expand_attribute_wildcards(attributes, ALL_GROUP_ATTRIBUTE_KEYS);
 | 
			
		||||
 | 
			
		||||
@ -237,7 +259,13 @@ fn make_ldap_search_group_result_entry(
 | 
			
		||||
        attributes: expanded_attributes
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter_map(|a| {
 | 
			
		||||
                let values = match get_group_attribute(&group, base_dn_str, a, user_filter) {
 | 
			
		||||
                let values = match get_group_attribute(
 | 
			
		||||
                    &group,
 | 
			
		||||
                    base_dn_str,
 | 
			
		||||
                    a,
 | 
			
		||||
                    user_filter,
 | 
			
		||||
                    ignored_group_attributes,
 | 
			
		||||
                ) {
 | 
			
		||||
                    Err(e) => return Some(Err(e)),
 | 
			
		||||
                    Ok(v) => v,
 | 
			
		||||
                }?;
 | 
			
		||||
@ -361,10 +389,17 @@ pub struct LdapHandler<Backend: BackendHandler + LoginHandler + OpaqueHandler> {
 | 
			
		||||
    backend_handler: Backend,
 | 
			
		||||
    pub base_dn: Vec<(String, String)>,
 | 
			
		||||
    base_dn_str: String,
 | 
			
		||||
    ignored_user_attributes: Vec<String>,
 | 
			
		||||
    ignored_group_attributes: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend> {
 | 
			
		||||
    pub fn new(backend_handler: Backend, mut ldap_base_dn: String) -> Self {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        backend_handler: Backend,
 | 
			
		||||
        mut ldap_base_dn: String,
 | 
			
		||||
        ignored_user_attributes: Vec<String>,
 | 
			
		||||
        ignored_group_attributes: Vec<String>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        ldap_base_dn.make_ascii_lowercase();
 | 
			
		||||
        Self {
 | 
			
		||||
            user_info: None,
 | 
			
		||||
@ -376,6 +411,8 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
 | 
			
		||||
                )
 | 
			
		||||
            }),
 | 
			
		||||
            base_dn_str: ldap_base_dn,
 | 
			
		||||
            ignored_user_attributes,
 | 
			
		||||
            ignored_group_attributes,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -600,7 +637,14 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
 | 
			
		||||
 | 
			
		||||
        users
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|u| make_ldap_search_user_result_entry(u, &self.base_dn_str, &request.attrs))
 | 
			
		||||
            .map(|u| {
 | 
			
		||||
                make_ldap_search_user_result_entry(
 | 
			
		||||
                    u,
 | 
			
		||||
                    &self.base_dn_str,
 | 
			
		||||
                    &request.attrs,
 | 
			
		||||
                    &self.ignored_user_attributes,
 | 
			
		||||
                )
 | 
			
		||||
            })
 | 
			
		||||
            .map(|entry| Ok(LdapOp::SearchResultEntry(entry?)))
 | 
			
		||||
            .collect::<Result<Vec<_>>>()
 | 
			
		||||
            .unwrap_or_else(|e| {
 | 
			
		||||
@ -650,6 +694,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
 | 
			
		||||
                    &self.base_dn_str,
 | 
			
		||||
                    &request.attrs,
 | 
			
		||||
                    user_filter,
 | 
			
		||||
                    &self.ignored_group_attributes,
 | 
			
		||||
                )
 | 
			
		||||
            })
 | 
			
		||||
            .map(|entry| Ok(LdapOp::SearchResultEntry(entry?)))
 | 
			
		||||
@ -718,10 +763,13 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
 | 
			
		||||
                    {
 | 
			
		||||
                        Ok(GroupRequestFilter::DisplayName(value.clone()))
 | 
			
		||||
                    } else {
 | 
			
		||||
                        warn!(
 | 
			
		||||
                            r#"Ignoring unknown group attribute "{:?}" in filter"#,
 | 
			
		||||
                            field
 | 
			
		||||
                        );
 | 
			
		||||
                        if !self.ignored_group_attributes.contains(field) {
 | 
			
		||||
                            warn!(
 | 
			
		||||
                                r#"Ignoring unknown group attribute "{:?}" in filter.\n\
 | 
			
		||||
                                To disable this warning, add it to "ignored_group_attributes" in the config."#,
 | 
			
		||||
                                field
 | 
			
		||||
                            );
 | 
			
		||||
                        }
 | 
			
		||||
                        Ok(GroupRequestFilter::Not(Box::new(GroupRequestFilter::And(
 | 
			
		||||
                            vec![],
 | 
			
		||||
                        ))))
 | 
			
		||||
@ -804,7 +852,13 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        Err(_) => {
 | 
			
		||||
                            warn!(r#"Ignoring unknown user attribute "{}" in filter"#, field);
 | 
			
		||||
                            if !self.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"#,
 | 
			
		||||
                                    field
 | 
			
		||||
                                );
 | 
			
		||||
                            }
 | 
			
		||||
                            Ok(UserRequestFilter::Not(Box::new(UserRequestFilter::And(
 | 
			
		||||
                                vec![],
 | 
			
		||||
                            ))))
 | 
			
		||||
@ -921,7 +975,8 @@ mod tests {
 | 
			
		||||
                set.insert(GroupIdAndName(GroupId(42), "lldap_admin".to_string()));
 | 
			
		||||
                Ok(set)
 | 
			
		||||
            });
 | 
			
		||||
        let mut ldap_handler = LdapHandler::new(mock, "dc=Example,dc=com".to_string());
 | 
			
		||||
        let mut ldap_handler =
 | 
			
		||||
            LdapHandler::new(mock, "dc=Example,dc=com".to_string(), vec![], vec![]);
 | 
			
		||||
        let request = LdapBindRequest {
 | 
			
		||||
            dn: "uid=test,ou=people,dc=example,dc=coM".to_string(),
 | 
			
		||||
            cred: LdapBindCred::Simple("pass".to_string()),
 | 
			
		||||
@ -946,7 +1001,8 @@ mod tests {
 | 
			
		||||
        mock.expect_get_user_groups()
 | 
			
		||||
            .with(eq(UserId::new("bob")))
 | 
			
		||||
            .return_once(|_| Ok(HashSet::new()));
 | 
			
		||||
        let mut ldap_handler = LdapHandler::new(mock, "dc=eXample,dc=com".to_string());
 | 
			
		||||
        let mut ldap_handler =
 | 
			
		||||
            LdapHandler::new(mock, "dc=eXample,dc=com".to_string(), vec![], vec![]);
 | 
			
		||||
 | 
			
		||||
        let request = LdapOp::BindRequest(LdapBindRequest {
 | 
			
		||||
            dn: "uid=bob,ou=people,dc=example,dc=com".to_string(),
 | 
			
		||||
@ -983,7 +1039,8 @@ mod tests {
 | 
			
		||||
                set.insert(GroupIdAndName(GroupId(42), "lldap_admin".to_string()));
 | 
			
		||||
                Ok(set)
 | 
			
		||||
            });
 | 
			
		||||
        let mut ldap_handler = LdapHandler::new(mock, "dc=example,dc=com".to_string());
 | 
			
		||||
        let mut ldap_handler =
 | 
			
		||||
            LdapHandler::new(mock, "dc=example,dc=com".to_string(), vec![], vec![]);
 | 
			
		||||
 | 
			
		||||
        let request = LdapBindRequest {
 | 
			
		||||
            dn: "uid=test,ou=people,dc=example,dc=com".to_string(),
 | 
			
		||||
@ -1020,7 +1077,8 @@ mod tests {
 | 
			
		||||
        mock.expect_get_user_groups()
 | 
			
		||||
            .with(eq(UserId::new("test")))
 | 
			
		||||
            .return_once(|_| Ok(HashSet::new()));
 | 
			
		||||
        let mut ldap_handler = LdapHandler::new(mock, "dc=example,dc=com".to_string());
 | 
			
		||||
        let mut ldap_handler =
 | 
			
		||||
            LdapHandler::new(mock, "dc=example,dc=com".to_string(), vec![], vec![]);
 | 
			
		||||
 | 
			
		||||
        let request = LdapBindRequest {
 | 
			
		||||
            dn: "uid=test,ou=people,dc=example,dc=com".to_string(),
 | 
			
		||||
@ -1048,7 +1106,8 @@ mod tests {
 | 
			
		||||
    #[tokio::test]
 | 
			
		||||
    async fn test_bind_invalid_dn() {
 | 
			
		||||
        let mock = MockTestBackendHandler::new();
 | 
			
		||||
        let mut ldap_handler = LdapHandler::new(mock, "dc=example,dc=com".to_string());
 | 
			
		||||
        let mut ldap_handler =
 | 
			
		||||
            LdapHandler::new(mock, "dc=example,dc=com".to_string(), vec![], vec![]);
 | 
			
		||||
 | 
			
		||||
        let request = LdapBindRequest {
 | 
			
		||||
            dn: "cn=bob,dc=example,dc=com".to_string(),
 | 
			
		||||
@ -1348,9 +1407,7 @@ mod tests {
 | 
			
		||||
                GroupRequestFilter::Not(Box::new(GroupRequestFilter::Not(Box::new(
 | 
			
		||||
                    GroupRequestFilter::And(vec![]),
 | 
			
		||||
                )))),
 | 
			
		||||
                GroupRequestFilter::Not(Box::new(
 | 
			
		||||
                    GroupRequestFilter::And(vec![]),
 | 
			
		||||
                )),
 | 
			
		||||
                GroupRequestFilter::Not(Box::new(GroupRequestFilter::And(vec![]))),
 | 
			
		||||
            ]))))
 | 
			
		||||
            .times(1)
 | 
			
		||||
            .return_once(|_| {
 | 
			
		||||
 | 
			
		||||
@ -70,6 +70,8 @@ async fn handle_ldap_stream<Stream, Backend>(
 | 
			
		||||
    stream: Stream,
 | 
			
		||||
    backend_handler: Backend,
 | 
			
		||||
    ldap_base_dn: String,
 | 
			
		||||
    ignored_user_attributes: Vec<String>,
 | 
			
		||||
    ignored_group_attributes: Vec<String>,
 | 
			
		||||
) -> Result<Stream>
 | 
			
		||||
where
 | 
			
		||||
    Backend: BackendHandler + LoginHandler + OpaqueHandler + 'static,
 | 
			
		||||
@ -81,7 +83,12 @@ where
 | 
			
		||||
    let mut requests = FramedRead::new(r, LdapCodec);
 | 
			
		||||
    let mut resp = FramedWrite::new(w, LdapCodec);
 | 
			
		||||
 | 
			
		||||
    let mut session = LdapHandler::new(backend_handler, ldap_base_dn);
 | 
			
		||||
    let mut session = LdapHandler::new(
 | 
			
		||||
        backend_handler,
 | 
			
		||||
        ldap_base_dn,
 | 
			
		||||
        ignored_user_attributes,
 | 
			
		||||
        ignored_group_attributes,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    while let Some(msg) = requests.next().await {
 | 
			
		||||
        if !handle_incoming_message(msg, &mut resp, &mut session)
 | 
			
		||||
@ -110,7 +117,12 @@ pub fn build_ldap_server<Backend>(
 | 
			
		||||
where
 | 
			
		||||
    Backend: BackendHandler + LoginHandler + OpaqueHandler + 'static,
 | 
			
		||||
{
 | 
			
		||||
    let context = (backend_handler, config.ldap_base_dn.clone());
 | 
			
		||||
    let context = (
 | 
			
		||||
        backend_handler,
 | 
			
		||||
        config.ldap_base_dn.clone(),
 | 
			
		||||
        config.ignored_user_attributes.clone(),
 | 
			
		||||
        config.ignored_group_attributes.clone(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let context_for_tls = context.clone();
 | 
			
		||||
 | 
			
		||||
@ -119,8 +131,15 @@ where
 | 
			
		||||
        fn_service(move |stream: TcpStream| {
 | 
			
		||||
            let context = context.clone();
 | 
			
		||||
            async move {
 | 
			
		||||
                let (handler, base_dn) = context;
 | 
			
		||||
                handle_ldap_stream(stream, handler, base_dn).await
 | 
			
		||||
                let (handler, base_dn, ignored_user_attributes, ignored_group_attributes) = context;
 | 
			
		||||
                handle_ldap_stream(
 | 
			
		||||
                    stream,
 | 
			
		||||
                    handler,
 | 
			
		||||
                    base_dn,
 | 
			
		||||
                    ignored_user_attributes,
 | 
			
		||||
                    ignored_group_attributes,
 | 
			
		||||
                )
 | 
			
		||||
                .await
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .map_err(|err: anyhow::Error| error!("[LDAP] Service Error: {:#}", err))
 | 
			
		||||
@ -139,9 +158,19 @@ where
 | 
			
		||||
            fn_service(move |stream: TcpStream| {
 | 
			
		||||
                let tls_context = tls_context.clone();
 | 
			
		||||
                async move {
 | 
			
		||||
                    let ((handler, base_dn), tls_acceptor) = tls_context;
 | 
			
		||||
                    let (
 | 
			
		||||
                        (handler, base_dn, ignored_user_attributes, ignored_group_attributes),
 | 
			
		||||
                        tls_acceptor,
 | 
			
		||||
                    ) = tls_context;
 | 
			
		||||
                    let tls_stream = tls_acceptor.accept(stream).await?;
 | 
			
		||||
                    handle_ldap_stream(tls_stream, handler, base_dn).await
 | 
			
		||||
                    handle_ldap_stream(
 | 
			
		||||
                        tls_stream,
 | 
			
		||||
                        handler,
 | 
			
		||||
                        base_dn,
 | 
			
		||||
                        ignored_user_attributes,
 | 
			
		||||
                        ignored_group_attributes,
 | 
			
		||||
                    )
 | 
			
		||||
                    .await
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .map_err(|err: anyhow::Error| error!("[LDAPS] Service Error: {:#}", err))
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user