Removed the dependency on OpenSSL on Windows

- Replaced session handling by secure_session
- Required updating a bunch of dependencies
- Replaced Hyper Client usage in DDNS by Reqwest (which only uses OpenSSL on Linux)
This commit is contained in:
Antoine Gersant 2017-06-04 13:17:27 -07:00
parent 425e1f4ad8
commit 86e618d2a5
5 changed files with 720 additions and 419 deletions

968
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -11,24 +11,26 @@ ape = "0.1.2"
app_dirs = { git = "https://github.com/agersant/app-dirs-rs" } app_dirs = { git = "https://github.com/agersant/app-dirs-rs" }
error-chain = "0.7.1" error-chain = "0.7.1"
getopts = "0.2.14" getopts = "0.2.14"
hyper = "0.9.10" hyper = "0.10.0"
id3 = "0.1.11" id3 = "0.1.11"
image = "0.10.3" image = "0.10.3"
iron = "0.4.0" iron = "0.5.1"
lewton = "0.4.1" lewton = "0.4.1"
metaflac = "0.1.4" metaflac = "0.1.4"
mount = "0.2.1" mount = "0.3.0"
ogg = "0.4.0" ogg = "0.4.0"
oven = { git = "https://github.com/agersant/oven", branch = "remove_cookie_dep" } params = "*"
params = "0.5.1"
regex = "0.1" regex = "0.1"
reqwest = "0.6.2"
router = "0.4.0" router = "0.4.0"
serde = "1.0.2" secure-session = "0.1.0"
serde_derive = "1.0.2" serde = "0.9"
serde_json = "1.0.2" serde_derive = "0.9"
serde_json = "0.9"
sqlite = "0.23.0" sqlite = "0.23.0"
staticfile = "0.3.0" staticfile = "0.4.0"
toml = "0.4" toml = "=0.3.2"
typemap = "0.3"
url = "1.2.0" url = "1.2.0"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]

View file

@ -5,12 +5,14 @@ use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
use iron::prelude::*; use iron::prelude::*;
use iron::headers::{Authorization, Basic, CookiePair}; use iron::headers::{Authorization, Basic, SetCookie};
use iron::{AroundMiddleware, Handler, status}; use iron::{AroundMiddleware, Handler, status};
use mount::Mount; use mount::Mount;
use oven::prelude::*;
use params; use params;
use secure_session::middleware::{SessionMiddleware, SessionConfig};
use secure_session::session::{SessionManager, ChaCha20Poly1305SessionManager};
use serde_json; use serde_json;
use typemap;
use url::percent_encoding::percent_decode; use url::percent_encoding::percent_decode;
use collection::*; use collection::*;
@ -36,7 +38,31 @@ impl Version {
} }
} }
pub fn get_api_handler(collection: Arc<Collection>) -> Mount { #[derive(Deserialize, Serialize)]
struct Session {
username: String,
}
struct SessionKey {}
impl typemap::Key for SessionKey {
type Value = Session;
}
pub fn get_handler(collection: Collection, secret: &str) -> Chain {
let collection = Arc::new(collection);
let api_handler = get_endpoints(collection);
let mut api_chain = Chain::new(api_handler);
let manager = ChaCha20Poly1305SessionManager::<Session>::from_password(secret.as_bytes());
let config = SessionConfig::default();
let session_middleware = SessionMiddleware::<Session, SessionKey, ChaCha20Poly1305SessionManager<Session>>::new(manager, config);
api_chain.link_around(session_middleware);
api_chain
}
fn get_endpoints(collection: Arc<Collection>) -> Mount {
let mut api_handler = Mount::new(); let mut api_handler = Mount::new();
{ {
@ -116,9 +142,11 @@ struct AuthHandler {
} }
fn set_cookie(username: &str, response: &mut Response) { fn set_cookie(username: &str, response: &mut Response) {
let mut username_cookie = CookiePair::new("username".to_string(), username.to_string()); response.headers.set(
username_cookie.path = Some("/".to_owned()); SetCookie(vec![
response.set_cookie(username_cookie); format!("username={}; Path=/", username)
])
);
} }
impl Handler for AuthHandler { impl Handler for AuthHandler {
@ -133,18 +161,20 @@ impl Handler for AuthHandler {
auth_success = self.collection auth_success = self.collection
.auth(auth.username.as_str(), password.as_str()); .auth(auth.username.as_str(), password.as_str());
username = Some(auth.username.clone()); username = Some(auth.username.clone());
req.extensions.insert::<SessionKey>(Session { username: auth.username.clone() });
} }
} }
// Auth via Cookie // Auth via Session
if !auth_success { if !auth_success {
auth_success = req.get_cookie("username").is_some(); auth_success = req.extensions.get::<SessionKey>().is_some();
} }
// Reject // Reject
if !auth_success { if !auth_success {
return Err(Error::from(ErrorKind::AuthenticationRequired).into()); return Err(Error::from(ErrorKind::AuthenticationRequired).into());
} }
} }
let mut response = self.handler.handle(req); let mut response = self.handler.handle(req);
@ -169,18 +199,23 @@ fn version(_: &mut Request) -> IronResult<Response> {
} }
fn auth(request: &mut Request, collection: &Collection) -> IronResult<Response> { fn auth(request: &mut Request, collection: &Collection) -> IronResult<Response> {
let input = request.get_ref::<params::Params>().unwrap(); let username;
let username = match input.find(&["username"]) { let password;
Some(&params::Value::String(ref username)) => username, {
_ => return Err(Error::from(ErrorKind::MissingUsername).into()), let input = request.get_ref::<params::Params>().unwrap();
}; username = match input.find(&["username"]) {
let password = match input.find(&["password"]) { Some(&params::Value::String(ref username)) => username.clone(),
Some(&params::Value::String(ref password)) => password, _ => return Err(Error::from(ErrorKind::MissingUsername).into()),
_ => return Err(Error::from(ErrorKind::MissingPassword).into()), };
}; password = match input.find(&["password"]) {
Some(&params::Value::String(ref password)) => password.clone(),
_ => return Err(Error::from(ErrorKind::MissingPassword).into()),
};
}
if collection.auth(username.as_str(), password.as_str()) { if collection.auth(username.as_str(), password.as_str()) {
let mut response = Response::with((status::Ok, "")); let mut response = Response::with((status::Ok, ""));
set_cookie(username.as_str(), &mut response); set_cookie(&username, &mut response);
request.extensions.insert::<SessionKey>(Session { username: username.clone() });
Ok(response) Ok(response)
} else { } else {
Err(Error::from(ErrorKind::IncorrectCredentials).into()) Err(Error::from(ErrorKind::IncorrectCredentials).into())

View file

@ -1,8 +1,6 @@
use hyper; use reqwest;
use hyper::client::Client; use reqwest::header::{Authorization, Basic};
use hyper::header::{Authorization, Basic};
use std::io; use std::io;
use std::io::Read;
use std::thread; use std::thread;
use std::time; use std::time;
@ -16,8 +14,8 @@ pub struct DDNSConfig {
#[derive(Debug)] #[derive(Debug)]
enum DDNSError { enum DDNSError {
IoError(io::Error), IoError(io::Error),
HyperError(hyper::Error), ReqwestError(reqwest::Error),
UpdateError(hyper::status::StatusCode), UpdateError(reqwest::StatusCode),
} }
impl From<io::Error> for DDNSError { impl From<io::Error> for DDNSError {
@ -26,54 +24,38 @@ impl From<io::Error> for DDNSError {
} }
} }
impl From<hyper::Error> for DDNSError { impl From<reqwest::Error> for DDNSError {
fn from(err: hyper::Error) -> DDNSError { fn from(err: reqwest::Error) -> DDNSError {
DDNSError::HyperError(err) DDNSError::ReqwestError(err)
} }
} }
const MY_IP_API_URL: &'static str = "http://api.ipify.org"; const DDNS_UPDATE_URL: &'static str = "https://ydns.io/api/v1/update/";
const DDNS_UPDATE_URL: &'static str = "http://ydns.io/api/v1/update/";
fn get_my_ip() -> Result<String, DDNSError> {
let client = Client::new();
let mut res = client.get(MY_IP_API_URL).send()?;
let mut buf = String::new();
res.read_to_string(&mut buf)?;
Ok(buf)
}
fn update_my_ip(ip: &String, config: &DDNSConfig) -> Result<(), DDNSError> { fn update_my_ip(config: &DDNSConfig) -> Result<(), DDNSError> {
let client = Client::new(); let full_url = format!("{}?host={}", DDNS_UPDATE_URL, &config.host);
let url = DDNS_UPDATE_URL;
let host = &config.host;
let full_url = format!("{}?host={}&ip={}", url, host, ip);
let auth_header = Authorization(Basic { let auth_header = Authorization(Basic {
username: config.username.clone(), username: config.username.clone(),
password: Some(config.password.to_owned()), password: Some(config.password.to_owned()),
}); });
let client = reqwest::Client::new()?;
let res = client let res = client
.get(full_url.as_str()) .get(full_url.as_str())
.header(auth_header) .header(auth_header)
.send()?; .send()?;
match res.status { if !res.status().is_success() {
hyper::status::StatusCode::Ok => Ok(()), return Err(DDNSError::UpdateError(*res.status()));
s => Err(DDNSError::UpdateError(s)),
} }
Ok(())
} }
pub fn run(config: DDNSConfig) { pub fn run(config: DDNSConfig) {
loop { loop {
let my_ip_res = get_my_ip(); match update_my_ip(&config) {
if let Ok(my_ip) = my_ip_res { Err(e) => println!("Dynamic DNS Error: {:?}", e),
match update_my_ip(&my_ip, &config) { Ok(_) => (),
Err(e) => println!("Dynamic DNS Error: {:?}", e), };
Ok(_) => (),
};
} else {
println!("Dynamic DNS Error: could not retrieve our own IP address");
}
thread::sleep(time::Duration::from_secs(60 * 30)); thread::sleep(time::Duration::from_secs(60 * 30));
} }
} }

View file

@ -14,9 +14,10 @@ extern crate lewton;
extern crate metaflac; extern crate metaflac;
extern crate mount; extern crate mount;
extern crate ogg; extern crate ogg;
extern crate oven;
extern crate params; extern crate params;
extern crate reqwest;
extern crate regex; extern crate regex;
extern crate secure_session;
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
@ -24,6 +25,7 @@ extern crate serde_json;
extern crate staticfile; extern crate staticfile;
extern crate sqlite; extern crate sqlite;
extern crate toml; extern crate toml;
extern crate typemap;
extern crate url; extern crate url;
#[cfg(windows)] #[cfg(windows)]
@ -116,22 +118,10 @@ fn run() -> Result<()> {
// Mount API // Mount API
println!("Mounting API"); println!("Mounting API");
let mut mount = Mount::new(); let mut mount = Mount::new();
let mut api_chain; let mut collection = collection::Collection::new(vfs, index);
{ collection.load_config(&config)?;
let api_handler; let handler = api::get_handler(collection, &config.secret);
{ mount.mount("/api/", handler);
let mut collection = collection::Collection::new(vfs, index);
collection.load_config(&config)?;
let collection = Arc::new(collection);
api_handler = api::get_api_handler(collection);
}
api_chain = Chain::new(api_handler);
let auth_secret = config.secret.to_owned();
let cookie_middleware = oven::new(auth_secret.into_bytes());
api_chain.link(cookie_middleware);
}
mount.mount("/api/", api_chain);
// Mount static files // Mount static files
println!("Mounting static files"); println!("Mounting static files");