// #![deny(warnings)] // extern crate futures; extern crate base64; extern crate hyper; extern crate ldap3; extern crate tokio; use std::str::{ FromStr, from_utf8, }; use std::thread; use hyper::rt::{Future}; use hyper::{Body, Request, Response, Server, StatusCode}; use hyper::header::{AUTHORIZATION}; use hyper_router::{Route, RouterBuilder, RouterService}; use base64::decode; use ldap3::{ LdapConn, Scope, SearchEntry }; #[derive(Debug)] struct BasicAuthentication { pub username: String, pub password: String, } #[derive(Debug)] pub enum AuthError { Parse, Decode, } impl FromStr for BasicAuthentication { type Err = AuthError; fn from_str(s: &str) -> Result { match decode(s) { Ok(bytes) => match from_utf8(&bytes) { Ok(text) => { let mut pair = text.splitn(2, ":"); Ok(BasicAuthentication { username: pair.next().unwrap().to_string(), password: pair.next().unwrap().to_string(), }) }, Err(_) => Err(AuthError::Parse) }, Err(_) => Err(AuthError::Decode) } } } #[derive(Debug)] struct LdapUser { pub dn: String, pub services: Vec, } fn auth_user(auth: &BasicAuthentication) -> Result { let ldap = match LdapConn::new("ldap://192.168.122.61:389") { Ok(conn) => conn, Err(err) => panic!(err), }; let base = format!("uid={},ou=people,dc=xeentech,dc=com", auth.username); match ldap.simple_bind(&base, &auth.password).unwrap().success() { Ok(ldap) => println!("Connected and authenticated"), Err(err) => panic!("Failed to bind with dn+password"), }; let filter = format!("(uid={})", auth.username); let s = match ldap.search(&base, Scope::Subtree, &filter, vec!["mail", "enabledService"]) { Ok(result) => { let (rs, _) = result.success().unwrap(); rs }, Err(err) => panic!("Search failed? {:?}", err), }; // Grab the first, if any, result and discard the rest let se = SearchEntry::construct(s.first().unwrap().to_owned()); let services = match se.attrs.get("enabledService") { Some(services) => services.to_vec(), None => [].to_vec(), }; let mail = match se.attrs.get("mail") { Some(mail) => mail.to_vec(), None => [].to_ved(), }; Ok(LdapUser { dn: base, services: services, }) } fn auth_handler(req: Request) -> Response { let header = match req.headers().get(AUTHORIZATION) { Some(auth_value) => auth_value.to_str().unwrap(), None => return Response::builder() .status(StatusCode::UNAUTHORIZED) .body(Body::from("Authentication header missing")) .unwrap(), }; let (auth_type, credentials) = { let mut split = header.split_ascii_whitespace(); let auth_type = split.next().unwrap(); let credentials = split.next().unwrap(); (auth_type, credentials) }; if auth_type != "Basic" { return Response::builder() .status(StatusCode::UNAUTHORIZED) .body(Body::from("Basic Authentication was expected")) .unwrap(); } let auth = BasicAuthentication::from_str(credentials).unwrap(); let worker = thread::spawn(move || { let user = auth_user(&auth); user }); let user = worker.join().expect("ldap thread threw?"); Response::new(Body::from(format!("BasicAuthentication {:?}", user))) } fn hello(req: Request) -> Response { Response::new(Body::from("Hi!")) } fn router_service() -> Result { let router = RouterBuilder::new() .add(Route::get("/hello").using(hello)) .add(Route::post("/auth").using(auth_handler)) .build(); Ok(RouterService::new(router)) } fn main() { let addr = "0.0.0.0:3000".parse().expect("Bad Address"); let server = Server::bind(&addr) .serve(router_service) .map_err(|e| eprintln!("server error: {}", e)); println!("Listening on http://{}", addr); tokio::run(server); }