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

View file

@ -5,12 +5,14 @@ use std::ops::Deref;
use std::sync::Arc;
use iron::prelude::*;
use iron::headers::{Authorization, Basic, CookiePair};
use iron::headers::{Authorization, Basic, SetCookie};
use iron::{AroundMiddleware, Handler, status};
use mount::Mount;
use oven::prelude::*;
use params;
use secure_session::middleware::{SessionMiddleware, SessionConfig};
use secure_session::session::{SessionManager, ChaCha20Poly1305SessionManager};
use serde_json;
use typemap;
use url::percent_encoding::percent_decode;
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();
{
@ -116,9 +142,11 @@ struct AuthHandler {
}
fn set_cookie(username: &str, response: &mut Response) {
let mut username_cookie = CookiePair::new("username".to_string(), username.to_string());
username_cookie.path = Some("/".to_owned());
response.set_cookie(username_cookie);
response.headers.set(
SetCookie(vec![
format!("username={}; Path=/", username)
])
);
}
impl Handler for AuthHandler {
@ -133,18 +161,20 @@ impl Handler for AuthHandler {
auth_success = self.collection
.auth(auth.username.as_str(), password.as_str());
username = Some(auth.username.clone());
req.extensions.insert::<SessionKey>(Session { username: auth.username.clone() });
}
}
// Auth via Cookie
// Auth via Session
if !auth_success {
auth_success = req.get_cookie("username").is_some();
auth_success = req.extensions.get::<SessionKey>().is_some();
}
// Reject
if !auth_success {
return Err(Error::from(ErrorKind::AuthenticationRequired).into());
}
}
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> {
let input = request.get_ref::<params::Params>().unwrap();
let username = match input.find(&["username"]) {
Some(&params::Value::String(ref username)) => username,
_ => return Err(Error::from(ErrorKind::MissingUsername).into()),
};
let password = match input.find(&["password"]) {
Some(&params::Value::String(ref password)) => password,
_ => return Err(Error::from(ErrorKind::MissingPassword).into()),
};
let username;
let password;
{
let input = request.get_ref::<params::Params>().unwrap();
username = match input.find(&["username"]) {
Some(&params::Value::String(ref username)) => username.clone(),
_ => return Err(Error::from(ErrorKind::MissingUsername).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()) {
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)
} else {
Err(Error::from(ErrorKind::IncorrectCredentials).into())

View file

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

View file

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