Add feature-gate for ldap.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk
2025-04-28 06:54:29 +00:00
parent 6160f90b8c
commit 7665d47e59
6 changed files with 61 additions and 31 deletions

View File

@@ -223,6 +223,11 @@ version = "0.1.3"
[workspace.dependencies.itertools] [workspace.dependencies.itertools]
version = "0.14.0" version = "0.14.0"
[workspace.dependencies.ldap3]
version = "0.11"
default-features = false
features = ["sync", "tls-rustls"]
[workspace.dependencies.libc] [workspace.dependencies.libc]
version = "0.2" version = "0.2"
@@ -549,11 +554,6 @@ features = ["serde"]
version = "2.0.1" version = "2.0.1"
default-features = false default-features = false
[workspace.dependencies.ldap3]
version = "0.11"
default-features = false
features = ["sync", "tls-rustls"]
# #
# Patches # Patches
# #

View File

@@ -49,6 +49,9 @@ jemalloc_stats = [
"tuwunel-core/jemalloc_stats", "tuwunel-core/jemalloc_stats",
"tuwunel-service/jemalloc_stats", "tuwunel-service/jemalloc_stats",
] ]
ldap = [
"tuwunel-service/ldap",
]
release_max_log_level = [ release_max_log_level = [
"tuwunel-core/release_max_log_level", "tuwunel-core/release_max_log_level",
"tuwunel-service/release_max_log_level", "tuwunel-service/release_max_log_level",

View File

@@ -190,7 +190,7 @@ pub(crate) async fn login_route(
return Err!(Request(Unknown("User ID does not belong to this homeserver"))); 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? ldap_login(&services, &user_id, &lowercased_user_id, password).await?
} else { } else {
password_login(&services, &user_id, &lowercased_user_id, password).await? password_login(&services, &user_id, &lowercased_user_id, password).await?

View File

@@ -93,6 +93,10 @@ jemalloc_stats = [
jemalloc_conf = [ jemalloc_conf = [
"tuwunel-core/jemalloc_conf", "tuwunel-core/jemalloc_conf",
] ]
ldap = [
"tuwunel-api/ldap",
"tuwunel-service/ldap",
]
media_thumbnail = [ media_thumbnail = [
"tuwunel-service/media_thumbnail", "tuwunel-service/media_thumbnail",
] ]

View File

@@ -53,6 +53,9 @@ jemalloc_stats = [
"tuwunel-core/jemalloc_stats", "tuwunel-core/jemalloc_stats",
"tuwunel-database/jemalloc_stats", "tuwunel-database/jemalloc_stats",
] ]
ldap = [
"dep:ldap3",
]
media_thumbnail = [ media_thumbnail = [
"dep:image", "dep:image",
] ]
@@ -88,6 +91,7 @@ image.optional = true
ipaddress.workspace = true ipaddress.workspace = true
itertools.workspace = true itertools.workspace = true
ldap3.workspace = true ldap3.workspace = true
ldap3.optional = true
log.workspace = true log.workspace = true
loole.workspace = true loole.workspace = true
lru-cache.workspace = true lru-cache.workspace = true

View File

@@ -1,8 +1,6 @@
use std::{collections::BTreeMap, mem, sync::Arc}; use std::{collections::BTreeMap, mem, sync::Arc};
use futures::{Stream, StreamExt, TryFutureExt}; use futures::{Stream, StreamExt, TryFutureExt};
use itertools::Itertools;
use ldap3::{LdapConnAsync, Scope, SearchEntry};
use ruma::{ use ruma::{
DeviceId, KeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, DeviceId, KeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId,
OneTimeKeyName, OwnedDeviceId, OwnedKeyId, OwnedMxcUri, OwnedUserId, RoomId, UInt, UserId, OneTimeKeyName, OwnedDeviceId, OwnedKeyId, OwnedMxcUri, OwnedUserId, RoomId, UInt, UserId,
@@ -15,8 +13,8 @@ use ruma::{
}; };
use serde_json::json; use serde_json::json;
use tuwunel_core::{ use tuwunel_core::{
Err, Error, Result, Server, at, debug, debug_warn, err, error, trace, Err, Error, Result, Server, at, debug_warn, err, trace,
utils::{self, ReadyExt, result::LogErr, stream::TryIgnore, string::Unquoted}, utils::{self, ReadyExt, stream::TryIgnore, string::Unquoted},
}; };
use tuwunel_database::{Deserialized, Ignore, Interfix, Json, Map}; 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 : // 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 `None` password can be used to deactivate a LDAP user
// - a "*" password is used as the default password of an active LDAP user // - a "*" password is used as the default password of an active LDAP user
if self if cfg!(feature = "ldap")
.db && password.is_some()
.userid_origin
.get(user_id)
.await
.deserialized::<String>()?
== "ldap" && password.is_some()
&& password != Some("*") && password != Some("*")
&& self
.db
.userid_origin
.get(user_id)
.await
.deserialized::<String>()?
== "ldap"
{ {
Err!(Request(InvalidParam("Cannot change password of a LDAP user"))) return 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(())
} }
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. /// 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<Vec<String>> { pub async fn search_ldap(&self, user_id: &UserId) -> Result<Vec<String>> {
use itertools::Itertools;
use ldap3::{LdapConnAsync, Scope, SearchEntry};
use tuwunel_core::{debug, error, result::LogErr};
let config = &self.services.server.config.ldap; let config = &self.services.server.config.ldap;
let (conn, mut ldap) = LdapConnAsync::new(config.uri.as_str()) let (conn, mut ldap) = LdapConnAsync::new(config.uri.as_str())
.await .await
@@ -1239,7 +1244,16 @@ impl Service {
Ok(dns) Ok(dns)
} }
#[cfg(not(feature = "ldap"))]
pub async fn search_ldap(&self, _user_id: &UserId) -> Result<Vec<String>> {
Err!(FeatureDisabled("ldap"))
}
#[cfg(feature = "ldap")]
pub async fn auth_ldap(&self, user_dn: &str, password: &str) -> Result { 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 config = &self.services.server.config.ldap;
let (conn, mut ldap) = LdapConnAsync::new(config.uri.as_str()) let (conn, mut ldap) = LdapConnAsync::new(config.uri.as_str())
.await .await
@@ -1267,6 +1281,11 @@ impl Service {
Ok(()) 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( pub fn parse_master_key(