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:
parent
425e1f4ad8
commit
86e618d2a5
5 changed files with 720 additions and 419 deletions
968
Cargo.lock
generated
968
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
22
Cargo.toml
22
Cargo.toml
|
@ -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]
|
||||||
|
|
71
src/api.rs
71
src/api.rs
|
@ -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(¶ms::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(¶ms::Value::String(ref username)) => username.clone(),
|
||||||
Some(¶ms::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(¶ms::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())
|
||||||
|
|
54
src/ddns.rs
54
src/ddns.rs
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -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");
|
||||||
|
|
Loading…
Add table
Reference in a new issue