Initial commit
This commit is contained in:
commit
9f8c47712a
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
**/*.rs.bk
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "auth"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["alex"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
futures = "0.1.21"
|
||||||
|
hyper = "0.12.25"
|
||||||
|
hyper-router = "0.5"
|
||||||
|
ldap3 = "0.6"
|
||||||
|
tokio = "0.1.0"
|
||||||
|
base64 = "0.10.0"
|
||||||
|
#[dependencies.ldap3]
|
||||||
|
# version = "0.6"
|
|
@ -0,0 +1,148 @@
|
||||||
|
// #![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<BasicAuthentication, AuthError> {
|
||||||
|
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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn auth_user(auth: &BasicAuthentication) -> Result<LdapUser, AuthError> {
|
||||||
|
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<Body>) -> Response<Body> {
|
||||||
|
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<Body>) -> Response<Body> {
|
||||||
|
Response::new(Body::from("Hi!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn router_service() -> Result<RouterService, std::io::Error> {
|
||||||
|
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);
|
||||||
|
}
|
Loading…
Reference in New Issue