lldap/src/infra/ldap_server.rs

172 lines
5.5 KiB
Rust
Raw Normal View History

use crate::domain::handler::BackendHandler;
2021-03-06 22:39:34 +00:00
use crate::infra::configuration::Configuration;
use actix_rt::net::TcpStream;
use actix_server::ServerBuilder;
use actix_service::{fn_service, pipeline_factory};
2021-03-07 15:13:50 +00:00
use anyhow::bail;
2021-03-06 22:39:34 +00:00
use anyhow::Result;
use futures_util::future::ok;
2021-03-06 22:39:34 +00:00
use log::*;
2021-03-07 15:13:50 +00:00
use tokio::net::tcp::WriteHalf;
use tokio_util::codec::{FramedRead, FramedWrite};
2021-03-06 22:39:34 +00:00
use ldap3_server::simple::*;
use ldap3_server::LdapCodec;
pub struct LdapHandler<Backend: BackendHandler> {
2021-03-06 22:39:34 +00:00
dn: String,
backend_handler: Backend,
2021-03-06 22:39:34 +00:00
}
impl<Backend: BackendHandler> LdapHandler<Backend> {
2021-03-06 22:39:34 +00:00
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(),
2021-03-06 22:39:34 +00:00
}
}
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())
}
2021-03-10 11:06:32 +00:00
pub fn handle_ldap_message(&mut self, server_op: ServerOps) -> Option<Vec<LdapMsg>> {
2021-03-07 15:13:50 +00:00
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)
}
2021-03-06 22:39:34 +00:00
}
async fn handle_incoming_message<Backend: BackendHandler>(
2021-03-07 15:13:50 +00:00
msg: Result<LdapMsg, std::io::Error>,
resp: &mut FramedWrite<WriteHalf<'_>, LdapCodec>,
session: &mut LdapHandler<Backend>,
2021-03-10 11:06:32 +00:00
) -> Result<bool> {
2021-03-07 15:13:50 +00:00
use futures_util::SinkExt;
use std::convert::TryFrom;
let server_op = match msg
.map_err(|_e| ())
.and_then(|msg| ServerOps::try_from(msg))
{
Ok(a_value) => a_value,
Err(an_error) => {
let _err = resp
.send(DisconnectionNotice::gen(
LdapResultCode::Other,
"Internal Server Error",
))
.await;
let _err = resp.flush().await;
bail!("Internal server error: {:?}", an_error);
}
};
match session.handle_ldap_message(server_op) {
None => return Ok(false),
Some(result) => {
for rmsg in result.into_iter() {
if let Err(e) = resp.send(rmsg).await {
bail!("Error while sending a response: {:?}", e);
}
}
if let Err(e) = resp.flush().await {
bail!("Error while flushing responses: {:?}", e);
}
}
}
Ok(true)
}
pub fn build_ldap_server<Backend>(
config: &Configuration,
backend_handler: Backend,
server_builder: ServerBuilder,
) -> Result<ServerBuilder>
where
Backend: BackendHandler + 'static,
{
2021-03-06 22:39:34 +00:00
use futures_util::StreamExt;
Ok(
server_builder.bind("ldap", ("0.0.0.0", config.ldap_port), move || {
let backend_handler = backend_handler.clone();
2021-03-07 15:13:50 +00:00
pipeline_factory(fn_service(move |mut stream: TcpStream| {
let backend_handler = backend_handler.clone();
2021-03-07 15:13:50 +00:00
async move {
// Configure the codec etc.
let (r, w) = stream.split();
let mut requests = FramedRead::new(r, LdapCodec);
let mut resp = FramedWrite::new(w, LdapCodec);
2021-03-06 22:39:34 +00:00
let mut session = LdapHandler {
dn: "Unauthenticated".to_string(),
backend_handler,
2021-03-07 15:13:50 +00:00
};
2021-03-06 22:39:34 +00:00
2021-03-07 15:13:50 +00:00
while let Some(msg) = requests.next().await {
if !handle_incoming_message(msg, &mut resp, &mut session).await? {
break;
2021-03-06 22:39:34 +00:00
}
2021-03-07 15:13:50 +00:00
}
2021-03-06 22:39:34 +00:00
2021-03-07 15:13:50 +00:00
Ok(stream)
}
}))
2021-03-07 15:13:50 +00:00
.map_err(|err: anyhow::Error| error!("Service Error: {:?}", err))
2021-03-06 22:39:34 +00:00
// catch
.and_then(move |_| {
// finally
ok(())
})
})?,
)
2021-03-06 22:39:34 +00:00
}