From 02932ee8b40669b3cbaecda202cf2c949d370904 Mon Sep 17 00:00:00 2001 From: Yves Fischer Date: Sun, 18 Dec 2016 23:24:04 +0100 Subject: Print the original URL with username/password in the message --- Cargo.toml | 1 + README.md | 2 +- src/handler.rs | 20 +++++++++++++------- src/main.rs | 1 + src/message.rs | 48 +++++++++++++++++++++++++++++++++++------------- src/token.rs | 7 ++++--- 6 files changed, 55 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5b183c4..e093f84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ build = "build.rs" [dependencies] ascii = "0.7" tiny_http = "0.5" +url = "0.2" rustc-serialize = "0.3" time = "0.1" rust-crypto = "0.2" diff --git a/README.md b/README.md index 826c929..18a98d3 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Options: proxy_pass http://127.0.0.1:8081/; # --port PORT proxy_pass_request_body off; proxy_set_header Content-Length ""; - proxy_set_header X-Original-URI "$scheme://$host$request_uri"; + proxy_set_header X-Original-Url "$scheme://$host$request_uri"; proxy_set_header X-Allowed-Jid "JID1,JID2"; } diff --git a/src/handler.rs b/src/handler.rs index 97f5d5c..75a2359 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -2,7 +2,6 @@ use std::cell::Cell; use std::collections::{HashMap}; use std::io; use std::marker::Sync; -use std::ops::Add; use std::str; use std::str::FromStr; use std::sync::{Arc, RwLock}; @@ -23,7 +22,8 @@ pub struct HeaderInfos { auth_username: String, auth_password: String, auth_method: String, - allowed_jids: Vec + allowed_jids: Vec, + original_url: Option } pub struct AuthHandler { @@ -41,6 +41,7 @@ type EmptyResponse = Response; // HTTP Statuscodes defined as macro. This way they can be used like literals. macro_rules! http_header_authorization { () => (r"Authorization") } macro_rules! http_header_x_allowed_jid { () => (r"X-Allowed-Jid") } +macro_rules! http_header_x_original_url { () => (r"X-Original-Url") } macro_rules! http_header_www_authenticate { () => (r"WWW-Authenticate") } // Finds a header in a `tiny_http::Header` structure. @@ -67,11 +68,12 @@ impl AuthHandler { } } - fn send_message(&self, user_jid: &str) { + fn send_message(&self, headerinfos: &HeaderInfos) { + let user_jid = &headerinfos.auth_username; let (valid_from, valid_until, token) = self.tg.generate_token(user_jid, get_time().sec); - let message = format_message(token, valid_from, valid_until); + let message = format_message(user_jid, token, valid_from, valid_until, headerinfos.original_url.clone()); if self.nosend { - error!("Would send to {} message: {}", user_jid, message); + error!("Would send to {} message: {}", headerinfos.auth_username, message); } else { if sendxmpp::send_message(self.bot_jid.as_str(), self.bot_password.as_str(), message.as_str(), user_jid).is_err() { @@ -105,11 +107,15 @@ impl AuthHandler { debug!("{}: {}", http_header_x_allowed_jid!(), allowed_jids_header); let allowed_jids_list = allowed_jids_header.split(',').map(String::from).collect(); + let original_url = get_header!(headers, http_header_x_original_url!()) + .map(|v| v.value.to_string()).ok(); + Ok(HeaderInfos { auth_username: String::from(username), auth_password: String::from(password), auth_method: String::from(auth_method), allowed_jids: allowed_jids_list, + original_url: original_url }) } @@ -139,7 +145,7 @@ impl AuthHandler { } else { self.last_interactive_request.set(current_time); if is_known_user { - self.send_message(&headerinfos.auth_username); + self.send_message(&headerinfos); } return self.authenticate_response(401) //Token sent, retry now } @@ -195,7 +201,7 @@ impl AuthHandler { fn verify(&self, headerinfos: &HeaderInfos) -> Result { let pw_token = token::normalize_token(&headerinfos.auth_password); let guard = self.valid_tokens_cache.clone(); - let key = headerinfos.auth_username.clone().add(":").add(pw_token.as_str()); + let key = headerinfos.auth_username.clone() + ":" + pw_token.as_str(); let current_time = get_time().sec; // try cache: diff --git a/src/main.rs b/src/main.rs index 30d4f8b..b536464 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ extern crate time; extern crate rand; extern crate rustc_serialize; extern crate simple_logger; +extern crate url; use crypto::digest::Digest; use crypto::sha1::Sha1; diff --git a/src/message.rs b/src/message.rs index 0af1a13..713e6b4 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,22 +1,44 @@ ///! Formats the message to be sent to the user use time::{at_utc, Timespec, strftime}; +use url::percent_encoding::{USERNAME_ENCODE_SET, PASSWORD_ENCODE_SET, percent_encode}; - -pub fn format_message(token: String, valid_from: i64, valid_until: i64) -> String { - return format!("Token: {}. Valid from {} until {}", +pub fn format_message(user_jid: &str, token: String, valid_from: i64, valid_until: i64, original_url: Option) -> String { + return format!("Token: {}. Valid from {} until {}. \n{}", token, strftime("%F %X", &at_utc(Timespec::new(valid_from, 0))).unwrap(), - strftime("%F %X", &at_utc(Timespec::new(valid_until, 0))).unwrap()); + strftime("%F %X", &at_utc(Timespec::new(valid_until, 0))).unwrap(), + match original_url { + Some(url) => insert_token_password(url, user_jid, &token), + None => "".to_string() + }); } +fn insert_token_password(url: String, user_jid: &str, token: &str) -> String { + match url.find("://") { + Some(pos) => { + let (scheme, rest) = url.split_at(pos + 3); + return scheme.to_string() + + percent_encode(user_jid.as_bytes(), USERNAME_ENCODE_SET).as_str() + ":" + + percent_encode(token.as_bytes(), PASSWORD_ENCODE_SET).as_str() + "@" + rest; + }, + None => return url + } +} -#[cfg(test)] -mod tests { - use super::*; +#[test] +fn test() { + assert_eq!(format_message("foo@bar.com", "7A-74-F4".to_string(), 0, 1481831953, Some("".to_string())), + "Token: 7A-74-F4. Valid from 1970-01-01 00:00:00 until 2016-12-15 19:59:13. \n"); + assert_eq!(format_message("foo@bar.com", "7A-74-F4".to_string(), 0, 1481831953, Some("http".to_string())), + "Token: 7A-74-F4. Valid from 1970-01-01 00:00:00 until 2016-12-15 19:59:13. \nhttp"); - #[test] - fn test1() { - assert_eq!(format_message("7A-74-F4".to_string(), 0, 1481831953), - "Token: 7A-74-F4. Valid from 1970-01-01 00:00:00 until 2016-12-15 19:59:13"); - } -} \ No newline at end of file + assert_eq!( + insert_token_password(String::from("http://foo.bar/ads?123"), "user_jid", "token"), + "http://user_jid:token@foo.bar/ads?123"); + assert_eq!( + insert_token_password(String::from("invalid"), "user_jid", "token"), + "invalid"); + assert_eq!( + insert_token_password(String::from("http://host/path"), "user@host", "@@##"), + "http://user%40host:%40%40%23%23@host/path"); +} diff --git a/src/token.rs b/src/token.rs index 2a2e446..ff5a396 100644 --- a/src/token.rs +++ b/src/token.rs @@ -24,8 +24,9 @@ impl TokenGenerator { /// Return (from, to, token) pub fn generate_token(&self, username: &str, at_time: i64) -> (i64, i64, String) { let timeslot = at_time - (at_time % self.valid_duration_secs); - let input: String = format!("{}{}", username, timeslot); - return (timeslot, timeslot + self.valid_duration_secs, self.make_hash_token(&input.as_bytes())) + let input = format!("{}{}", username, timeslot); + let token = self.make_hash_token(&input.as_bytes()); + return (timeslot, timeslot + self.valid_duration_secs, token) } #[inline(always)] @@ -64,7 +65,7 @@ mod tests { fn test_generate_token() { use time; let tg = TokenGenerator::new(time::Duration::hours(2).num_seconds(), - vec!(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)); + vec!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); let (valid_from, valid_until, result) = tg.generate_token("a", 99999999); assert_eq!( valid_from, 99993600); assert_eq!( valid_until, 100000800); -- cgit v1.2.1