diff --git a/Cargo.toml b/Cargo.toml index 6d56360b..36ac8e8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -223,6 +223,11 @@ version = "0.1.3" [workspace.dependencies.itertools] version = "0.14.0" +[workspace.dependencies.ldap3] +version = "0.11" +default-features = false +features = ["sync", "tls-rustls"] + [workspace.dependencies.libc] version = "0.2" @@ -549,11 +554,6 @@ features = ["serde"] version = "2.0.1" default-features = false -[workspace.dependencies.ldap3] -version = "0.11" -default-features = false -features = ["sync", "tls-rustls"] - # # Patches # diff --git a/src/api/Cargo.toml b/src/api/Cargo.toml index fb723b46..a750391a 100644 --- a/src/api/Cargo.toml +++ b/src/api/Cargo.toml @@ -49,6 +49,9 @@ jemalloc_stats = [ "tuwunel-core/jemalloc_stats", "tuwunel-service/jemalloc_stats", ] +ldap = [ + "tuwunel-service/ldap", +] release_max_log_level = [ "tuwunel-core/release_max_log_level", "tuwunel-service/release_max_log_level", diff --git a/src/api/client/session.rs b/src/api/client/session.rs index 3c15484f..5b3e953f 100644 --- a/src/api/client/session.rs +++ b/src/api/client/session.rs @@ -190,7 +190,7 @@ pub(crate) async fn login_route( return Err!(Request(Unknown("User ID does not belong to this homeserver"))); } - if services.config.ldap.enable { + if cfg!(feature = "ldap") && services.config.ldap.enable { ldap_login(&services, &user_id, &lowercased_user_id, password).await? } else { password_login(&services, &user_id, &lowercased_user_id, password).await? diff --git a/src/main/Cargo.toml b/src/main/Cargo.toml index 7e7ac276..8b949f44 100644 --- a/src/main/Cargo.toml +++ b/src/main/Cargo.toml @@ -93,6 +93,10 @@ jemalloc_stats = [ jemalloc_conf = [ "tuwunel-core/jemalloc_conf", ] +ldap = [ + "tuwunel-api/ldap", + "tuwunel-service/ldap", +] media_thumbnail = [ "tuwunel-service/media_thumbnail", ] diff --git a/src/service/Cargo.toml b/src/service/Cargo.toml index 20eac916..52bdd3e1 100644 --- a/src/service/Cargo.toml +++ b/src/service/Cargo.toml @@ -53,6 +53,9 @@ jemalloc_stats = [ "tuwunel-core/jemalloc_stats", "tuwunel-database/jemalloc_stats", ] +ldap = [ + "dep:ldap3", +] media_thumbnail = [ "dep:image", ] @@ -88,6 +91,7 @@ image.optional = true ipaddress.workspace = true itertools.workspace = true ldap3.workspace = true +ldap3.optional = true log.workspace = true loole.workspace = true lru-cache.workspace = true diff --git a/src/service/users/mod.rs b/src/service/users/mod.rs index 2d13dad5..7410c6a8 100644 --- a/src/service/users/mod.rs +++ b/src/service/users/mod.rs @@ -1,8 +1,6 @@ use std::{collections::BTreeMap, mem, sync::Arc}; use futures::{Stream, StreamExt, TryFutureExt}; -use itertools::Itertools; -use ldap3::{LdapConnAsync, Scope, SearchEntry}; use ruma::{ DeviceId, KeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, OneTimeKeyName, OwnedDeviceId, OwnedKeyId, OwnedMxcUri, OwnedUserId, RoomId, UInt, UserId, @@ -15,8 +13,8 @@ use ruma::{ }; use serde_json::json; use tuwunel_core::{ - Err, Error, Result, Server, at, debug, debug_warn, err, error, trace, - utils::{self, ReadyExt, result::LogErr, stream::TryIgnore, string::Unquoted}, + Err, Error, Result, Server, at, debug_warn, err, trace, + utils::{self, ReadyExt, stream::TryIgnore, string::Unquoted}, }; use tuwunel_database::{Deserialized, Ignore, Interfix, Json, Map}; @@ -249,30 +247,32 @@ impl Service { // Cannot change the password of a LDAP user. There are two special cases : // - a `None` password can be used to deactivate a LDAP user // - a "*" password is used as the default password of an active LDAP user - if self - .db - .userid_origin - .get(user_id) - .await - .deserialized::()? - == "ldap" && password.is_some() + if cfg!(feature = "ldap") + && password.is_some() && password != Some("*") + && self + .db + .userid_origin + .get(user_id) + .await + .deserialized::()? + == "ldap" { - Err!(Request(InvalidParam("Cannot change password of a LDAP user"))) - } else { - password - .map(utils::hash::password) - .transpose() - .map_err(|e| { - err!(Request(InvalidParam("Password does not meet the requirements: {e}"))) - })? - .map_or_else( - || self.db.userid_password.insert(user_id, b""), - |hash| self.db.userid_password.insert(user_id, hash), - ); - - Ok(()) + return Err!(Request(InvalidParam("Cannot change password of a LDAP user"))); } + + password + .map(utils::hash::password) + .transpose() + .map_err(|e| { + err!(Request(InvalidParam("Password does not meet the requirements: {e}"))) + })? + .map_or_else( + || self.db.userid_password.insert(user_id, b""), + |hash| self.db.userid_password.insert(user_id, hash), + ); + + Ok(()) } /// Returns the displayname of a user on this homeserver. @@ -1178,7 +1178,12 @@ impl Service { } } + #[cfg(feature = "ldap")] pub async fn search_ldap(&self, user_id: &UserId) -> Result> { + use itertools::Itertools; + use ldap3::{LdapConnAsync, Scope, SearchEntry}; + use tuwunel_core::{debug, error, result::LogErr}; + let config = &self.services.server.config.ldap; let (conn, mut ldap) = LdapConnAsync::new(config.uri.as_str()) .await @@ -1239,7 +1244,16 @@ impl Service { Ok(dns) } + #[cfg(not(feature = "ldap"))] + pub async fn search_ldap(&self, _user_id: &UserId) -> Result> { + Err!(FeatureDisabled("ldap")) + } + + #[cfg(feature = "ldap")] pub async fn auth_ldap(&self, user_dn: &str, password: &str) -> Result { + use ldap3::LdapConnAsync; + use tuwunel_core::{debug, error, result::LogErr}; + let config = &self.services.server.config.ldap; let (conn, mut ldap) = LdapConnAsync::new(config.uri.as_str()) .await @@ -1267,6 +1281,11 @@ impl Service { Ok(()) } + + #[cfg(not(feature = "ldap"))] + pub async fn auth_ldap(&self, _user_dn: &str, _password: &str) -> Result { + Err!(FeatureDisabled("ldap")) + } } pub fn parse_master_key(