summaryrefslogtreecommitdiff
path: root/rust/src/handler.rs
diff options
context:
space:
mode:
authorYves Fischer <yvesf-git@xapek.org>2016-09-04 00:05:26 +0200
committerYves Fischer <yvesf-git@xapek.org>2016-09-14 21:15:14 +0200
commit9416cd3d62f15f5d59f3bff94299f690e69a230d (patch)
tree4972cacce2dcbaa58d4a434f3ea7bce0616e11bc /rust/src/handler.rs
parent53b5c00e625bf700a21be1a3e2070d3b23f1bef4 (diff)
downloadauth-xmppmessage-9416cd3d62f15f5d59f3bff94299f690e69a230d.tar.gz
auth-xmppmessage-9416cd3d62f15f5d59f3bff94299f690e69a230d.zip
switch to using tiny_http
civet fails on FreeBSD because the mongoose Makefile requires gmake.
Diffstat (limited to 'rust/src/handler.rs')
-rw-r--r--rust/src/handler.rs101
1 files changed, 56 insertions, 45 deletions
diff --git a/rust/src/handler.rs b/rust/src/handler.rs
index e16899b..5fb7a03 100644
--- a/rust/src/handler.rs
+++ b/rust/src/handler.rs
@@ -1,22 +1,19 @@
-use std::thread;
-use std::collections::{HashMap, HashSet};
-use std::sync::{Arc, RwLock};
-use std::io::empty;
-use std::error::Error;
use std::cell::Cell;
+use std::collections::{HashMap, HashSet};
+use std::io;
use std::marker::Sync;
use std::ops::Add;
+use std::str::from_utf8;
+use std::sync::{Arc, RwLock};
+use std::thread;
use std::time::Duration;
+use sendxmpp;
use time;
-use base64;
-use civet::response;
-use conduit::{Request, Response, Handler};
-
use token;
-use sendxmpp;
-use apachelog;
+use tiny_http::{Request, Response, StatusCode, Header};
+use rustc_serialize::base64::FromBase64;
pub struct AuthHandler {
bot_jid: String,
@@ -24,10 +21,18 @@ pub struct AuthHandler {
usernames: HashSet<String>,
valid_tokens_cache: Arc<RwLock<HashMap<String, i64>>>,
tg: token::TokenGenerator,
- last_interactive_request: Cell<i64>,
- headers_authenticate: HashMap<String, Vec<String>>,
+ last_interactive_request: Cell<i64>
}
+type EmptyResponse = Response<io::Empty>;
+
+macro_rules! get_header {
+ ($headers:expr, $name:expr) => ($headers.iter()
+ .filter(|h| h.field.equiv($name))
+ .next().ok_or(stringify!(Header not found: $name)));
+}
+
+
impl AuthHandler {
pub fn make(bot_jid: String, bot_password: String,
usernames: HashSet<String>, validity: time::Duration, secret: Vec<u8>) -> AuthHandler {
@@ -38,9 +43,6 @@ impl AuthHandler {
valid_tokens_cache: Arc::new(RwLock::new(HashMap::new())),
tg: token::TokenGenerator::new(validity.num_seconds(), secret),
last_interactive_request: Cell::new(0),
- headers_authenticate: vec!(("WWW-Authenticate".to_string(),
- vec!("Basic realm=\"xmppmessage auth\"".to_string())))
- .into_iter().collect(),
}
}
@@ -55,24 +57,37 @@ impl AuthHandler {
}
// Result<(method, username, password), error-message>
- fn _get_username_password(request: &Request) -> Result<(String, String, String), &'static str> {
+ #[inline(always)]
+ fn _get_username_password<'a>(request: &'a Request) -> Result<(String, String, String), &'static str> {
let headers = request.headers();
- let mut auth_header = try!(headers.find("Authorization").ok_or("No Authorization header found"));
- let authorization = try!(auth_header.pop().ok_or("No Authorization header value"));
+ let auth_header: &Header = { try!(get_header!(headers, "Authorization")) };
+ let authorization: &str = auth_header.value.as_str();
let mut authorization_split = authorization.split(' ');
let method_value = try!(authorization_split.next().ok_or("No method in header value"));
let value = try!(authorization_split.next().ok_or("No username/password value in header value"));
- let decoded_value = try!(base64::decode(value).or(Err("Fail base64 decode")));
- let utf8_decoded_value = try!(String::from_utf8(decoded_value).or(Err("Failed to utf-8 decode username/password")));
+ let decoded_value = try!(value.from_base64().or(Err("Fail base64 decode")));
+ let utf8_decoded_value = try!(from_utf8(&decoded_value).or(Err("fail to decode utf-8")));
let mut username_password_split = utf8_decoded_value.split(':');
let username = try!(username_password_split.next().ok_or("No username in header"));
let password = try!(username_password_split.next().ok_or("No password in header"));
- Ok((method_value.to_string(), username.to_string(), password.to_string()))
+ Ok((String::from(method_value), String::from(username), String::from(password)))
}
- fn _call_internal(&self, req: &Request) -> Result<(), (u32, &'static str)> {
+ fn authenticate_response(status_code: u16) -> io::Result<EmptyResponse> {
+ Ok(Response::new(
+ StatusCode(status_code),
+ vec![
+ Header::from_bytes(&b"WWW-Authenticate"[..], &b"Basic realm=\"xmppmessage auth\""[..]).unwrap()
+ ],
+ io::empty(),
+ Some(0),
+ None,
+ ))
+ }
+
+ fn _call_internal(&self, request: &Request) -> io::Result<EmptyResponse> {
let current_time = time::now().to_timespec().sec;
- return match AuthHandler::_get_username_password(req) {
+ return match AuthHandler::_get_username_password(request) {
Ok((_, username, password)) => {
let is_known_user = self.usernames.contains(&username);
if username.len() > 0 && password.len() == 0 {
@@ -80,29 +95,29 @@ impl AuthHandler {
if current_time - self.last_interactive_request.get() < 2 {
// If last error was not longer then 2 second ago then sleep
thread::sleep(Duration::from_secs(5));
- return Err((429, "Too many requests"))
+ return AuthHandler::authenticate_response(429) // Too many requests
} else {
self.last_interactive_request.set(current_time);
if is_known_user {
self.send_message(&username);
}
- return Err((401, "Token sent, retry now"))
+ return AuthHandler::authenticate_response(401) //Token sent, retry now
}
} else {
match self.verify(&username, &password) {
Ok(true) => {
if is_known_user {
- return Ok(());
+ return Ok(Response::empty(200)) // Ok
} else {
self.last_interactive_request.set(current_time);
- Err((401, "Token sent, retry"))
+ return AuthHandler::authenticate_response(401) // invalid password
}
},
Ok(false) => {
if current_time - self.last_interactive_request.get() < 2 {
// If last error was not longer then 2 seconds ago then sleep 5 seconds
thread::sleep(Duration::from_secs(5));
- return Err((429, "Too Many Requests"))
+ return Ok(Response::empty(429)) // Too Many Requests
} else {
self.last_interactive_request.set(current_time);
// in this case we use the chance to delete outdated cache entries
@@ -110,19 +125,19 @@ impl AuthHandler {
Ok(num) => println!("Removed {} cache entries", num),
Err(e) => println!("{}", e),
};
- return Err((401, "Authentication failed, username or password wrong"));
+ return AuthHandler::authenticate_response(401) // Authentication failed, username or password wrong
}
},
Err(msg) => {
println!("verify failed: {}", msg);
- Err((500, "Server Error"))
+ return Err(io::Error::new(io::ErrorKind::Other, "Server Error")) // Server Error
}
}
}
},
Err(e) => {
- println!("Failed: {}", e);
- return Err((401, e))
+ info!("{}. Request Authentication", e);
+ return AuthHandler::authenticate_response(401) // No Authorization header
},
};
}
@@ -181,19 +196,15 @@ impl AuthHandler {
}
}
}
-}
-unsafe impl Sync for AuthHandler {
+ #[inline(always)]
+ pub fn call(&self, request: &Request) -> Response<io::Empty> {
+ self._call_internal(request).unwrap_or_else(|err: io::Error| {
+ error!("{}", err);
+ Response::empty(500)
+ })
+ }
}
-impl Handler for AuthHandler {
- fn call(&self, req: &mut Request) -> Result<Response, Box<Error + Send>> {
- let mut logentry = apachelog::LogEntry::start(req);
- return match self._call_internal(req) {
- Ok(_) => Ok(response((200, "OK, go ahead"), HashMap::new(), empty())),
- Err((code, message)) => {
- Ok(response((code, message), self.headers_authenticate.clone(), empty()))
- }
- }.map(|r| logentry.done(r))
- }
+unsafe impl Sync for AuthHandler {
} \ No newline at end of file