diff --git a/src/db/common.rs b/src/db/common.rs index 094b0e5..94a3071 100644 --- a/src/db/common.rs +++ b/src/db/common.rs @@ -2,6 +2,7 @@ use tokio::sync::RwLock; use std::sync::Arc; +use crate::db::error::BoxableError; use crate::db::{DatabaseInterface, UserRef}; #[derive(Clone, Debug)] @@ -26,13 +27,17 @@ impl Database { /// TODO: we may want to make sure the method runs in constant time /// to avoid leaking information about existing users... /// or maybe we do not care. - pub async fn check_password(&self, user: &UserRef, password: &str) -> bool { - let Some(user) = self.get_user(user).await else { + pub async fn check_password( + &self, + user: &UserRef, + password: &str, + ) -> Result> { + let Some(user) = self.get_user(user).await? else { tracing::debug!("check_password: User not found {user}"); - return false; + return Ok(false); }; tracing::debug!("Comparing {} and {}", user.password, password); - user.password == password + Ok(user.password == password) } } diff --git a/src/db/error.rs b/src/db/error.rs index e45a796..178fe3b 100644 --- a/src/db/error.rs +++ b/src/db/error.rs @@ -1,6 +1,21 @@ +use ldap3_proto::LdapResultCode; + use std::fmt; use crate::db::UserRef; +use crate::ldap::LdapReturnError; + +pub trait BoxableError: std::error::Error + tracing::Value + Sync + Send + 'static {} + +impl LdapReturnError for T { + fn code(&self) -> LdapResultCode { + LdapResultCode::Unavailable + } + + fn message(&self) -> String { + self.to_string() + } +} #[derive(Debug)] pub struct UserAlreadyExists(pub UserRef); diff --git a/src/db/interface.rs b/src/db/interface.rs index 3bcd7a3..43bcdf7 100644 --- a/src/db/interface.rs +++ b/src/db/interface.rs @@ -1,17 +1,23 @@ -use crate::db::error::UserAlreadyExists; +use crate::db::error::{BoxableError, UserAlreadyExists}; use crate::db::{Database, User, UserRef}; impl DatabaseInterface for Database { - async fn get_user(&self, user: &UserRef) -> Option { + async fn get_user(&self, user: &UserRef) -> Result, Box> { self.inner.read().await.get_user(user).await } - async fn create_user(&mut self, user: User) -> Result<(), UserAlreadyExists> { + async fn create_user( + &mut self, + user: User, + ) -> Result, Box> { self.inner.write().await.create_user(user).await } } pub trait DatabaseInterface { - async fn get_user(&self, user: &UserRef) -> Option; - async fn create_user(&mut self, user: User) -> Result<(), UserAlreadyExists>; + async fn get_user(&self, user: &UserRef) -> Result, Box>; + async fn create_user( + &mut self, + user: User, + ) -> Result, Box>; } diff --git a/src/db/memory.rs b/src/db/memory.rs index cc88068..361f65b 100644 --- a/src/db/memory.rs +++ b/src/db/memory.rs @@ -1,6 +1,6 @@ use std::future::{Future, ready}; -use crate::db::error::UserAlreadyExists; +use crate::db::error::{BoxableError, UserAlreadyExists}; use crate::db::{Database, DatabaseInterface, Group, User, UserRef}; #[derive(Clone, Debug, Default)] @@ -16,23 +16,29 @@ impl MemoryDatabase { } impl DatabaseInterface for MemoryDatabase { - fn get_user(&self, req_user: &UserRef) -> impl Future> { + fn get_user( + &self, + req_user: &UserRef, + ) -> impl Future, Box>> { for user in &self.users { if user.username == req_user.username && user.domain == req_user.domain { - return ready(Some(user.clone())); + return ready(Ok(Some(user.clone()))); } } - ready(None) + ready(Ok(None)) } - async fn create_user(&mut self, user: User) -> Result<(), UserAlreadyExists> { + async fn create_user( + &mut self, + user: User, + ) -> Result, Box> { let user_ref = user.user_ref(); - if self.get_user(&user_ref).await.is_some() { - return Err(UserAlreadyExists(user_ref)); + if self.get_user(&user_ref).await?.is_some() { + return Ok(Err(UserAlreadyExists(user_ref))); } self.users.push(user); - Ok(()) + Ok(Ok(())) } } diff --git a/src/ldap/op/bind.rs b/src/ldap/op/bind.rs index ee5464d..69aa47a 100644 --- a/src/ldap/op/bind.rs +++ b/src/ldap/op/bind.rs @@ -1,13 +1,15 @@ use ldap3_proto::proto::{LdapBindCred, LdapBindRequest, LdapBindResponse, LdapOp, LdapResult}; use ldap3_proto::{LdapMsg, LdapResultCode}; +use crate::db::error::BoxableError; use crate::db::{Database, DatabaseInterface}; use crate::ldap::{ Dn, InvalidDnError, LdapReturnError, LdapStream, LdapStreamError, NotUserDnError, }; +#[derive(Debug)] pub enum BindError { - // TODO: DB Error + Db(Box), InvalidCredentials, InvalidDn(InvalidDnError), NotUserDn(NotUserDnError), @@ -20,6 +22,7 @@ impl BindError { stream: &mut LdapStream, msgid: i32, ) -> Result<(), LdapStreamError> { + tracing::debug!(error=?self, "Bind failed"); let resp_msg = LdapMsg { msgid, op: LdapOp::BindResponse(LdapBindResponse { @@ -48,6 +51,7 @@ impl From for BindError { impl LdapReturnError for BindError { fn code(&self) -> LdapResultCode { match self { + Self::Db(e) => e.code(), Self::InvalidCredentials => LdapResultCode::InvalidCredentials, Self::InvalidDn(e) => e.code(), Self::NotUserDn(e) => e.code(), @@ -57,6 +61,7 @@ impl LdapReturnError for BindError { fn message(&self) -> String { match self { + Self::Db(e) => e.message(), Self::InvalidCredentials => "Wrong username or password".to_string(), Self::InvalidDn(e) => e.message(), Self::NotUserDn(e) => e.message(), @@ -117,7 +122,16 @@ pub async fn op_bind( } }; - if db.check_password(&user_ref, &password).await { + let success = match db.check_password(&user_ref, &password).await { + Ok(success) => success, + Err(e) => { + tracing::error!(error = e, "Database failure"); + BindError::Db(e).error_message(stream, msgid).await?; + return Ok(None); + } + }; + + if success { bind_success(stream, msgid).await?; Ok(Some(dn)) } else { diff --git a/src/main.rs b/src/main.rs index 95eacc4..1bcde2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ async fn create_dummy_users(db: &mut Database) { mail: format!("{domain}@{domain}.localhost"), }) .await + .unwrap() .unwrap(); } }