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"
name = "lldap"
version = "0.1.0"
[dependencies]
actix = "0.11.0-beta.3"
actix-rt = "2.1.0"
@ -29,3 +30,6 @@ tracing-subscriber = "*"
[dependencies.figment]
features = ["toml", "env"]
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 http_port: u16,
pub secret_pepper: String,
pub some_text: String,
pub admin_dn: String,
pub admin_password: String,
pub verbose: bool,
}
@ -24,7 +25,8 @@ impl Default for Configuration {
ldaps_port: 6360,
http_port: 17170,
secret_pepper: String::from("secretsecretpepper"),
some_text: String::new(),
admin_dn: String::new(),
admin_password: String::new(),
verbose: false,
}
}

View File

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

View File

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

View File

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