fix: Allow using LDAP passwords in UIAA

Fixes #1131

Co-authored-by: Jade Ellis <jade@ellis.link>
This commit is contained in:
Jade Ellis
2025-12-16 13:55:32 +00:00
committed by Ellis Git
parent 0b667ae4fd
commit cdc53b3421
+33 -10
View File
@@ -11,7 +11,7 @@ use database::{Deserialized, Json, Map};
use ruma::{ use ruma::{
CanonicalJsonValue, DeviceId, OwnedDeviceId, OwnedUserId, UserId, CanonicalJsonValue, DeviceId, OwnedDeviceId, OwnedUserId, UserId,
api::client::{ api::client::{
error::ErrorKind, error::{ErrorKind, StandardErrorBody},
uiaa::{AuthData, AuthType, Password, UiaaInfo, UserIdentifier}, uiaa::{AuthData, AuthType, Password, UiaaInfo, UserIdentifier},
}, },
}; };
@@ -104,6 +104,7 @@ pub fn create(
} }
#[implement(Service)] #[implement(Service)]
#[allow(clippy::useless_let_if_seq)]
pub async fn try_auth( pub async fn try_auth(
&self, &self,
user_id: &UserId, user_id: &UserId,
@@ -163,17 +164,39 @@ pub async fn try_auth(
let user_id = user_id_from_username; let user_id = user_id_from_username;
// Check if password is correct // Check if password is correct
let mut password_verified = false;
// First try local password hash verification
if let Ok(hash) = self.services.users.password_hash(&user_id).await { if let Ok(hash) = self.services.users.password_hash(&user_id).await {
let hash_matches = hash::verify_password(password, &hash).is_ok(); password_verified = hash::verify_password(password, &hash).is_ok();
if !hash_matches { }
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {
kind: ErrorKind::forbidden(), // If local password verification failed, try LDAP authentication
message: "Invalid username or password.".to_owned(), #[cfg(feature = "ldap")]
}); if !password_verified && self.services.config.ldap.enable {
return Ok((false, uiaainfo)); // Search for user in LDAP to get their DN
if let Ok(dns) = self.services.users.search_ldap(&user_id).await {
if let Some((user_dn, _is_admin)) = dns.first() {
// Try to authenticate with LDAP
password_verified = self
.services
.users
.auth_ldap(user_dn, password)
.await
.is_ok();
}
} }
} }
if !password_verified {
uiaainfo.auth_error = Some(StandardErrorBody {
kind: ErrorKind::forbidden(),
message: "Invalid username or password.".to_owned(),
});
return Ok((false, uiaainfo));
}
// Password was correct! Let's add it to `completed` // Password was correct! Let's add it to `completed`
uiaainfo.completed.push(AuthType::Password); uiaainfo.completed.push(AuthType::Password);
}, },
@@ -197,7 +220,7 @@ pub async fn try_auth(
}, },
| Err(e) => { | Err(e) => {
error!("ReCaptcha verification failed: {e:?}"); error!("ReCaptcha verification failed: {e:?}");
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { uiaainfo.auth_error = Some(StandardErrorBody {
kind: ErrorKind::forbidden(), kind: ErrorKind::forbidden(),
message: "ReCaptcha verification failed.".to_owned(), message: "ReCaptcha verification failed.".to_owned(),
}); });
@@ -210,7 +233,7 @@ pub async fn try_auth(
if tokens.contains(t.token.trim()) { if tokens.contains(t.token.trim()) {
uiaainfo.completed.push(AuthType::RegistrationToken); uiaainfo.completed.push(AuthType::RegistrationToken);
} else { } else {
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { uiaainfo.auth_error = Some(StandardErrorBody {
kind: ErrorKind::forbidden(), kind: ErrorKind::forbidden(),
message: "Invalid registration token.".to_owned(), message: "Invalid registration token.".to_owned(),
}); });