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