From ed2ae209515b32ad2ebd5c28bdd926eb646a0d84 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 28 Oct 2018 19:04:21 -0700 Subject: [PATCH] Allow auth via HTTP authorization header --- src/main.rs | 10 ++++----- src/rocket_api.rs | 53 ++++++++++++++++++++++++++++++++++++----------- src/serve.rs | 3 +-- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index 05ebc6f..0d353c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -239,11 +239,11 @@ fn run() -> Result<()> { let db_server = db.clone(); std::thread::spawn(move || { rocket::ignite() - .manage(db_server) - .manage(command_sender) - .mount(&static_url, StaticFiles::from(web_dir_path)) - .mount(&api_url, rocket_api::get_routes()) - .launch(); + .manage(db_server) + .manage(command_sender) + .mount(&static_url, StaticFiles::from(web_dir_path)) + .mount(&api_url, rocket_api::get_routes()) + .launch(); }); // Start DDNS updates diff --git a/src/rocket_api.rs b/src/rocket_api.rs index 353f602..a7c079f 100644 --- a/src/rocket_api.rs +++ b/src/rocket_api.rs @@ -7,6 +7,7 @@ use std::fs::File; use std::ops::Deref; use std::path::PathBuf; use std::str; +use std::str::FromStr; use std::sync::Arc; use config::{self, Config, Preferences}; @@ -59,19 +60,44 @@ struct Auth { username: String, } +fn get_auth_cookie(username: &str) -> Cookie<'static> { + Cookie::build(SESSION_FIELD_USERNAME, username.to_owned()) + .same_site(rocket::http::SameSite::Lax) + .finish() +} + impl<'a, 'r> FromRequest<'a, 'r> for Auth { type Error = (); fn from_request(request: &'a Request<'r>) -> request::Outcome { let mut cookies = request.guard::().unwrap(); - match cookies.get_private(SESSION_FIELD_USERNAME) { - Some(u) => Outcome::Success(Auth { + if let Some(u) = cookies.get_private(SESSION_FIELD_USERNAME) { + return Outcome::Success(Auth { username: u.value().to_string(), - }), - _ => Outcome::Failure((Status::Forbidden, ())), + }); } - // TODO allow auth via authorization header + if let Some(auth_header_string) = request.headers().get_one("Authorization") { + use rocket::http::hyper::header::*; + if let Ok(Basic { + username, + password: Some(password), + }) = Basic::from_str(auth_header_string.trim_start_matches("Basic ")) // Sadness + { + let db = match request.guard::>>() { + Outcome::Success(d) => d, + _ => return Outcome::Failure((Status::InternalServerError, ())) + }; + if user::auth(db.deref().deref(), &username, &password).unwrap_or(false) { + cookies.add_private(get_auth_cookie(&username)); + return Outcome::Success(Auth { + username: username.to_string(), + }); + } + } + } + + Outcome::Failure((Status::Unauthorized, ())) } } @@ -147,7 +173,10 @@ fn initial_setup(db: State>) -> Result, errors::Error } #[get("/settings")] -fn get_settings(db: State>, _admin_rights: AdminRights) -> Result, errors::Error> { +fn get_settings( + db: State>, + _admin_rights: AdminRights, +) -> Result, errors::Error> { let config = config::read::(&db)?; Ok(Json(config)) } @@ -205,11 +234,7 @@ fn auth( mut cookies: Cookies, ) -> Result, errors::Error> { user::auth::(&db, &credentials.username, &credentials.password)?; - cookies.add_private( - Cookie::build(SESSION_FIELD_USERNAME, credentials.username.clone()) - .same_site(rocket::http::SameSite::Lax) - .finish(), - ); + cookies.add_private(get_auth_cookie(&credentials.username)); let auth_output = AuthOutput { admin: user::is_admin::(&db, &credentials.username)?, @@ -355,7 +380,11 @@ fn delete_playlist(db: State>, auth: Auth, name: String) -> Result<(), e } #[put("/lastfm/now_playing/")] -fn lastfm_now_playing(db: State>, auth: Auth, path: VFSPathBuf) -> Result<(), errors::Error> { +fn lastfm_now_playing( + db: State>, + auth: Auth, + path: VFSPathBuf, +) -> Result<(), errors::Error> { lastfm::now_playing(db.deref().deref(), &auth.username, &path.into() as &PathBuf)?; Ok(()) } diff --git a/src/serve.rs b/src/serve.rs index 4192b90..780b391 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -3,7 +3,7 @@ use rocket::http::hyper::header::*; use rocket::response::{self, Responder}; use std::cmp; use std::convert::From; -use std::fs::{File}; +use std::fs::File; use std::io::{Read, Seek, SeekFrom}; use std::str::FromStr; @@ -79,7 +79,6 @@ fn truncate_range(range: &PartialFileRange, file_length: &Option) -> Option impl<'r> Responder<'r> for RangeResponder { fn respond_to(mut self, request: &rocket::request::Request) -> response::Result<'r> { - let range_header = request.headers().get_one("Range"); let range_header = match range_header { None => return Ok(self.original.respond_to(request)?),