mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
Separate ldap_handler, add tests
This commit is contained in:
parent
ff4e986a0d
commit
86b89a00cc
@ -2,13 +2,16 @@ use crate::infra::configuration::Configuration;
|
||||
use anyhow::{bail, Result};
|
||||
use sqlx::any::AnyPool;
|
||||
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
pub struct BindRequest {
|
||||
pub name: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
pub struct SearchRequest {}
|
||||
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
|
||||
pub struct SearchResponse {}
|
||||
|
||||
pub trait BackendHandler: Clone + Send {
|
||||
@ -47,3 +50,15 @@ impl BackendHandler for SqlBackendHandler {
|
||||
Ok(SearchResponse {})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mockall::mock! {
|
||||
pub TestBackendHandler{}
|
||||
impl Clone for TestBackendHandler {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
impl BackendHandler for TestBackendHandler {
|
||||
fn bind(&mut self, request: BindRequest) -> Result<()>;
|
||||
fn search(&mut self, request: SearchRequest) -> Result<SearchResponse>;
|
||||
}
|
||||
}
|
||||
|
123
src/infra/ldap_handler.rs
Normal file
123
src/infra/ldap_handler.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use crate::domain::handler::BackendHandler;
|
||||
use ldap3_server::simple::*;
|
||||
|
||||
pub struct LdapHandler<Backend: BackendHandler> {
|
||||
dn: String,
|
||||
backend_handler: Backend,
|
||||
}
|
||||
|
||||
impl<Backend: BackendHandler> LdapHandler<Backend> {
|
||||
pub fn new(backend_handler: Backend) -> Self {
|
||||
Self {
|
||||
dn: "Unauthenticated".to_string(),
|
||||
backend_handler,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_bind(&mut self, sbr: &SimpleBindRequest) -> LdapMsg {
|
||||
match self
|
||||
.backend_handler
|
||||
.bind(crate::domain::handler::BindRequest {
|
||||
name: sbr.dn.clone(),
|
||||
password: sbr.pw.clone(),
|
||||
}) {
|
||||
Ok(()) => {
|
||||
self.dn = sbr.dn.clone();
|
||||
sbr.gen_success()
|
||||
}
|
||||
Err(_) => sbr.gen_invalid_cred(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_search(&mut self, lsr: &SearchRequest) -> Vec<LdapMsg> {
|
||||
vec![
|
||||
lsr.gen_result_entry(LdapSearchResultEntry {
|
||||
dn: "cn=hello,dc=example,dc=com".to_string(),
|
||||
attributes: vec![
|
||||
LdapPartialAttribute {
|
||||
atype: "objectClass".to_string(),
|
||||
vals: vec!["cursed".to_string()],
|
||||
},
|
||||
LdapPartialAttribute {
|
||||
atype: "cn".to_string(),
|
||||
vals: vec!["hello".to_string()],
|
||||
},
|
||||
],
|
||||
}),
|
||||
lsr.gen_result_entry(LdapSearchResultEntry {
|
||||
dn: "cn=world,dc=example,dc=com".to_string(),
|
||||
attributes: vec![
|
||||
LdapPartialAttribute {
|
||||
atype: "objectClass".to_string(),
|
||||
vals: vec!["cursed".to_string()],
|
||||
},
|
||||
LdapPartialAttribute {
|
||||
atype: "cn".to_string(),
|
||||
vals: vec!["world".to_string()],
|
||||
},
|
||||
],
|
||||
}),
|
||||
lsr.gen_success(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn do_whoami(&mut self, wr: &WhoamiRequest) -> LdapMsg {
|
||||
if self.dn == "Unauthenticated" {
|
||||
wr.gen_operror("Unauthenticated")
|
||||
} else {
|
||||
wr.gen_success(format!("dn: {}", self.dn).as_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_ldap_message(&mut self, server_op: ServerOps) -> Option<Vec<LdapMsg>> {
|
||||
let result = match server_op {
|
||||
ServerOps::SimpleBind(sbr) => vec![self.do_bind(&sbr)],
|
||||
ServerOps::Search(sr) => self.do_search(&sr),
|
||||
ServerOps::Unbind(_) => {
|
||||
// No need to notify on unbind (per rfc4511)
|
||||
return None;
|
||||
}
|
||||
ServerOps::Whoami(wr) => vec![self.do_whoami(&wr)],
|
||||
};
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::domain::handler::MockTestBackendHandler;
|
||||
use mockall::{mock, predicate::*};
|
||||
|
||||
#[test]
|
||||
fn test_bind() {
|
||||
let mut mock = MockTestBackendHandler::new();
|
||||
mock.expect_bind()
|
||||
.with(eq(crate::domain::handler::BindRequest {
|
||||
name: "test".to_string(),
|
||||
password: "pass".to_string(),
|
||||
}))
|
||||
.times(1)
|
||||
.return_once(|_| Ok(()));
|
||||
let mut ldap_handler = LdapHandler::new(mock);
|
||||
|
||||
let request = WhoamiRequest { msgid: 1 };
|
||||
assert_eq!(
|
||||
ldap_handler.do_whoami(&request),
|
||||
request.gen_operror("Unauthenticated")
|
||||
);
|
||||
|
||||
let request = SimpleBindRequest {
|
||||
msgid: 2,
|
||||
dn: "test".to_string(),
|
||||
pw: "pass".to_string(),
|
||||
};
|
||||
assert_eq!(ldap_handler.do_bind(&request), request.gen_success());
|
||||
|
||||
let request = WhoamiRequest { msgid: 3 };
|
||||
assert_eq!(
|
||||
ldap_handler.do_whoami(&request),
|
||||
request.gen_success("dn: test")
|
||||
);
|
||||
}
|
||||
}
|
@ -1,89 +1,18 @@
|
||||
use crate::domain::handler::BackendHandler;
|
||||
use crate::infra::configuration::Configuration;
|
||||
use crate::infra::ldap_handler::LdapHandler;
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_server::ServerBuilder;
|
||||
use actix_service::{fn_service, pipeline_factory};
|
||||
use anyhow::bail;
|
||||
use anyhow::Result;
|
||||
use futures_util::future::ok;
|
||||
use ldap3_server::simple::*;
|
||||
use ldap3_server::LdapCodec;
|
||||
use log::*;
|
||||
use tokio::net::tcp::WriteHalf;
|
||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||
|
||||
use ldap3_server::simple::*;
|
||||
use ldap3_server::LdapCodec;
|
||||
|
||||
pub struct LdapHandler<Backend: BackendHandler> {
|
||||
dn: String,
|
||||
backend_handler: Backend,
|
||||
}
|
||||
|
||||
impl<Backend: BackendHandler> LdapHandler<Backend> {
|
||||
pub fn do_bind(&mut self, sbr: &SimpleBindRequest) -> LdapMsg {
|
||||
match self
|
||||
.backend_handler
|
||||
.bind(crate::domain::handler::BindRequest {
|
||||
name: sbr.dn.clone(),
|
||||
password: sbr.pw.clone(),
|
||||
}) {
|
||||
Ok(()) => {
|
||||
self.dn = sbr.dn.clone();
|
||||
sbr.gen_success()
|
||||
}
|
||||
Err(_) => sbr.gen_invalid_cred(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_search(&mut self, lsr: &SearchRequest) -> Vec<LdapMsg> {
|
||||
vec![
|
||||
lsr.gen_result_entry(LdapSearchResultEntry {
|
||||
dn: "cn=hello,dc=example,dc=com".to_string(),
|
||||
attributes: vec![
|
||||
LdapPartialAttribute {
|
||||
atype: "objectClass".to_string(),
|
||||
vals: vec!["cursed".to_string()],
|
||||
},
|
||||
LdapPartialAttribute {
|
||||
atype: "cn".to_string(),
|
||||
vals: vec!["hello".to_string()],
|
||||
},
|
||||
],
|
||||
}),
|
||||
lsr.gen_result_entry(LdapSearchResultEntry {
|
||||
dn: "cn=world,dc=example,dc=com".to_string(),
|
||||
attributes: vec![
|
||||
LdapPartialAttribute {
|
||||
atype: "objectClass".to_string(),
|
||||
vals: vec!["cursed".to_string()],
|
||||
},
|
||||
LdapPartialAttribute {
|
||||
atype: "cn".to_string(),
|
||||
vals: vec!["world".to_string()],
|
||||
},
|
||||
],
|
||||
}),
|
||||
lsr.gen_success(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn do_whoami(&mut self, wr: &WhoamiRequest) -> LdapMsg {
|
||||
wr.gen_success(format!("dn: {}", self.dn).as_str())
|
||||
}
|
||||
|
||||
pub fn handle_ldap_message(&mut self, server_op: ServerOps) -> Option<Vec<LdapMsg>> {
|
||||
let result = match server_op {
|
||||
ServerOps::SimpleBind(sbr) => vec![self.do_bind(&sbr)],
|
||||
ServerOps::Search(sr) => self.do_search(&sr),
|
||||
ServerOps::Unbind(_) => {
|
||||
// No need to notify on unbind (per rfc4511)
|
||||
return None;
|
||||
}
|
||||
ServerOps::Whoami(wr) => vec![self.do_whoami(&wr)],
|
||||
};
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_incoming_message<Backend: BackendHandler>(
|
||||
msg: Result<LdapMsg, std::io::Error>,
|
||||
resp: &mut FramedWrite<WriteHalf<'_>, LdapCodec>,
|
||||
@ -146,10 +75,7 @@ where
|
||||
let mut requests = FramedRead::new(r, LdapCodec);
|
||||
let mut resp = FramedWrite::new(w, LdapCodec);
|
||||
|
||||
let mut session = LdapHandler {
|
||||
dn: "Unauthenticated".to_string(),
|
||||
backend_handler,
|
||||
};
|
||||
let mut session = LdapHandler::new(backend_handler);
|
||||
|
||||
while let Some(msg) = requests.next().await {
|
||||
if !handle_incoming_message(msg, &mut resp, &mut session).await? {
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub mod cli;
|
||||
pub mod configuration;
|
||||
pub mod ldap_handler;
|
||||
pub mod ldap_server;
|
||||
pub mod logging;
|
||||
pub mod tcp_server;
|
||||
|
Loading…
Reference in New Issue
Block a user