Introduce BackendHandler trait and impl

This commit is contained in:
Valentin Tolmer 2021-03-11 10:14:15 +01:00
parent 1a947358fa
commit ff4e986a0d
7 changed files with 98 additions and 30 deletions

View File

@ -3,6 +3,7 @@ authors = ["Valentin Tolmer <valentin@tolmer.fr>", "Steve Barrau <steve.barrau@g
edition = "2018" edition = "2018"
name = "lldap" name = "lldap"
version = "0.1.0" version = "0.1.0"
[dependencies] [dependencies]
actix = "0.11.0-beta.3" actix = "0.11.0-beta.3"
actix-rt = "2.1.0" actix-rt = "2.1.0"
@ -29,3 +30,6 @@ tracing-subscriber = "*"
[dependencies.figment] [dependencies.figment]
features = ["toml", "env"] features = ["toml", "env"]
version = "*" version = "*"
[dev-dependencies]
mockall = "0.9.1"

49
src/domain/handler.rs Normal file
View File

@ -0,0 +1,49 @@
use crate::infra::configuration::Configuration;
use anyhow::{bail, Result};
use sqlx::any::AnyPool;
pub struct BindRequest {
pub name: String,
pub password: String,
}
pub struct SearchRequest {}
pub struct SearchResponse {}
pub trait BackendHandler: Clone + Send {
fn bind(&mut self, request: BindRequest) -> Result<()>;
fn search(&mut self, request: SearchRequest) -> Result<SearchResponse>;
}
#[derive(Debug, Clone)]
pub struct SqlBackendHandler {
config: Configuration,
sql_pool: AnyPool,
authenticated: bool,
}
impl SqlBackendHandler {
pub fn new(config: Configuration, sql_pool: AnyPool) -> Self {
SqlBackendHandler {
config,
sql_pool,
authenticated: false,
}
}
}
impl BackendHandler for SqlBackendHandler {
fn bind(&mut self, request: BindRequest) -> Result<()> {
if request.name == self.config.admin_dn && request.password == self.config.admin_password {
self.authenticated = true;
Ok(())
} else {
bail!(r#"Authentication error for "{}""#, request.name)
}
}
fn search(&mut self, request: SearchRequest) -> Result<SearchResponse> {
Ok(SearchResponse {})
}
}

1
src/domain/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod handler;

View File

@ -13,7 +13,8 @@ pub struct Configuration {
pub ldaps_port: u16, pub ldaps_port: u16,
pub http_port: u16, pub http_port: u16,
pub secret_pepper: String, pub secret_pepper: String,
pub some_text: String, pub admin_dn: String,
pub admin_password: String,
pub verbose: bool, pub verbose: bool,
} }
@ -24,7 +25,8 @@ impl Default for Configuration {
ldaps_port: 6360, ldaps_port: 6360,
http_port: 17170, http_port: 17170,
secret_pepper: String::from("secretsecretpepper"), secret_pepper: String::from("secretsecretpepper"),
some_text: String::new(), admin_dn: String::new(),
admin_password: String::new(),
verbose: false, verbose: false,
} }
} }

View File

@ -1,3 +1,4 @@
use crate::domain::handler::BackendHandler;
use crate::infra::configuration::Configuration; use crate::infra::configuration::Configuration;
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_server::ServerBuilder; use actix_server::ServerBuilder;
@ -6,28 +7,30 @@ use anyhow::bail;
use anyhow::Result; use anyhow::Result;
use futures_util::future::ok; use futures_util::future::ok;
use log::*; use log::*;
use sqlx::any::AnyPool;
use tokio::net::tcp::WriteHalf; use tokio::net::tcp::WriteHalf;
use tokio_util::codec::{FramedRead, FramedWrite}; use tokio_util::codec::{FramedRead, FramedWrite};
use ldap3_server::simple::*; use ldap3_server::simple::*;
use ldap3_server::LdapCodec; use ldap3_server::LdapCodec;
pub struct LdapSession { pub struct LdapHandler<Backend: BackendHandler> {
dn: String, dn: String,
sql_pool: AnyPool, backend_handler: Backend,
} }
impl LdapSession { impl<Backend: BackendHandler> LdapHandler<Backend> {
pub fn do_bind(&mut self, sbr: &SimpleBindRequest) -> LdapMsg { pub fn do_bind(&mut self, sbr: &SimpleBindRequest) -> LdapMsg {
if sbr.dn == "cn=Directory Manager" && sbr.pw == "password" { match self
self.dn = sbr.dn.to_string(); .backend_handler
.bind(crate::domain::handler::BindRequest {
name: sbr.dn.clone(),
password: sbr.pw.clone(),
}) {
Ok(()) => {
self.dn = sbr.dn.clone();
sbr.gen_success() sbr.gen_success()
} else if sbr.dn == "" && sbr.pw == "" { }
self.dn = "Anonymous".to_string(); Err(_) => sbr.gen_invalid_cred(),
sbr.gen_success()
} else {
sbr.gen_invalid_cred()
} }
} }
@ -81,10 +84,10 @@ impl LdapSession {
} }
} }
async fn handle_incoming_message( async fn handle_incoming_message<Backend: BackendHandler>(
msg: Result<LdapMsg, std::io::Error>, msg: Result<LdapMsg, std::io::Error>,
resp: &mut FramedWrite<WriteHalf<'_>, LdapCodec>, resp: &mut FramedWrite<WriteHalf<'_>, LdapCodec>,
session: &mut LdapSession, session: &mut LdapHandler<Backend>,
) -> Result<bool> { ) -> Result<bool> {
use futures_util::SinkExt; use futures_util::SinkExt;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -122,27 +125,30 @@ async fn handle_incoming_message(
Ok(true) Ok(true)
} }
pub fn build_ldap_server( pub fn build_ldap_server<Backend>(
config: &Configuration, config: &Configuration,
sql_pool: AnyPool, backend_handler: Backend,
server_builder: ServerBuilder, server_builder: ServerBuilder,
) -> Result<ServerBuilder> { ) -> Result<ServerBuilder>
where
Backend: BackendHandler + 'static,
{
use futures_util::StreamExt; use futures_util::StreamExt;
Ok( Ok(
server_builder.bind("ldap", ("0.0.0.0", config.ldap_port), move || { server_builder.bind("ldap", ("0.0.0.0", config.ldap_port), move || {
let sql_pool = sql_pool.clone(); let backend_handler = backend_handler.clone();
pipeline_factory(fn_service(move |mut stream: TcpStream| { pipeline_factory(fn_service(move |mut stream: TcpStream| {
let sql_pool = sql_pool.clone(); let backend_handler = backend_handler.clone();
async move { async move {
// Configure the codec etc. // Configure the codec etc.
let (r, w) = stream.split(); let (r, w) = stream.split();
let mut requests = FramedRead::new(r, LdapCodec); let mut requests = FramedRead::new(r, LdapCodec);
let mut resp = FramedWrite::new(w, LdapCodec); let mut resp = FramedWrite::new(w, LdapCodec);
let mut session = LdapSession { let mut session = LdapHandler {
dn: "Anonymous".to_string(), dn: "Unauthenticated".to_string(),
sql_pool, backend_handler,
}; };
while let Some(msg) = requests.next().await { while let Some(msg) = requests.next().await {

View File

@ -1,3 +1,4 @@
use crate::domain::handler::*;
use crate::infra::configuration::Configuration; use crate::infra::configuration::Configuration;
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_server::ServerBuilder; use actix_server::ServerBuilder;
@ -5,14 +6,16 @@ use actix_service::pipeline_factory;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use futures_util::future::ok; use futures_util::future::ok;
use log::*; use log::*;
use sqlx::any::AnyPool;
use std::sync::Arc; use std::sync::Arc;
pub fn build_tcp_server( pub fn build_tcp_server<Backend>(
config: &Configuration, config: &Configuration,
sql_pool: AnyPool, backend_handler: Backend,
server_builder: ServerBuilder, server_builder: ServerBuilder,
) -> Result<ServerBuilder> { ) -> Result<ServerBuilder>
where
Backend: BackendHandler + 'static,
{
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;

View File

@ -4,6 +4,7 @@ use futures_util::TryFutureExt;
use log::*; use log::*;
use sqlx::any::AnyPoolOptions; use sqlx::any::AnyPoolOptions;
mod domain;
mod infra; mod infra;
async fn run_server(config: Configuration) -> Result<()> { async fn run_server(config: Configuration) -> Result<()> {
@ -11,12 +12,14 @@ async fn run_server(config: Configuration) -> Result<()> {
.max_connections(5) .max_connections(5)
.connect("sqlite://users.db?mode=rwc") .connect("sqlite://users.db?mode=rwc")
.await?; .await?;
let backend_handler = domain::handler::SqlBackendHandler::new(config.clone(), sql_pool.clone());
let server_builder = infra::ldap_server::build_ldap_server( let server_builder = infra::ldap_server::build_ldap_server(
&config, &config,
sql_pool.clone(), backend_handler.clone(),
actix_server::Server::build(), actix_server::Server::build(),
)?; )?;
let server_builder = infra::tcp_server::build_tcp_server(&config, sql_pool, server_builder)?; let server_builder =
infra::tcp_server::build_tcp_server(&config, backend_handler, server_builder)?;
server_builder.workers(1).run().await?; server_builder.workers(1).run().await?;
Ok(()) Ok(())
} }