diff options
Diffstat (limited to 'src/auth.rs')
-rw-r--r-- | src/auth.rs | 74 |
1 files changed, 51 insertions, 23 deletions
diff --git a/src/auth.rs b/src/auth.rs index 1a913d5..7c77758 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,8 +1,9 @@ -use actix_web::dev::ServiceRequest; +use actix_web::dev::{Service, ServiceRequest, ServiceResponse}; use actix_web::http::{header, StatusCode}; -use actix_web::{HttpRequest, HttpResponse, Result}; -use actix_web_httpauth::extractors::basic::BasicAuth; +use actix_web::{HttpRequest, HttpResponse}; +use futures::future::Either; use sha2::{Digest, Sha256, Sha512}; +use std::future::{ready, Future}; use crate::errors::{self, ContextualError}; use crate::renderer; @@ -14,12 +15,16 @@ pub struct BasicAuthParams { pub password: String, } -impl From<BasicAuth> for BasicAuthParams { - fn from(auth: BasicAuth) -> Self { - Self { +impl BasicAuthParams { + fn try_from_request(req: &HttpRequest) -> actix_web::Result<Self> { + use actix_web::http::header::Header; + use actix_web_httpauth::headers::authorization::{Authorization, Basic}; + + let auth = Authorization::<Basic>::parse(req)?.into_scheme(); + Ok(Self { username: auth.user_id().to_string(), password: auth.password().unwrap_or(&"".into()).to_string(), - } + }) } } @@ -72,25 +77,48 @@ pub fn get_hash<T: Digest>(text: &str) -> Vec<u8> { hasher.finalize().to_vec() } -pub async fn handle_auth(req: ServiceRequest, cred: BasicAuth) -> Result<ServiceRequest> { +/// When authentication succedes, return the request to be passed to downstream services. +/// Otherwise, return an error response +fn handle_auth(req: ServiceRequest) -> Result<ServiceRequest, ServiceResponse> { let (req, pl) = req.into_parts(); let required_auth = &req.app_data::<crate::MiniserveConfig>().unwrap().auth; - if match_auth(cred.into(), required_auth) { - Ok(ServiceRequest::from_parts(req, pl).unwrap_or_else(|_| unreachable!())) - } else { - Err(HttpResponse::Unauthorized() - .header( - header::WWW_AUTHENTICATE, - header::HeaderValue::from_static("Basic realm=\"miniserve\""), - ) - .body(build_unauthorized_response( - &req, - ContextualError::InvalidHttpCredentials, - true, - StatusCode::UNAUTHORIZED, - )) - .into()) + if required_auth.is_empty() { + // auth is disabled by configuration + return Ok(ServiceRequest::from_parts(req, pl)); + } else if let Ok(cred) = BasicAuthParams::try_from_request(&req) { + if match_auth(cred, required_auth) { + return Ok(ServiceRequest::from_parts(req, pl)); + } + } + + // auth failed; render and return the error response + let resp = HttpResponse::Unauthorized() + .append_header(( + header::WWW_AUTHENTICATE, + header::HeaderValue::from_static("Basic realm=\"miniserve\""), + )) + .body(build_unauthorized_response( + &req, + ContextualError::InvalidHttpCredentials, + true, + StatusCode::UNAUTHORIZED, + )); + + Err(ServiceResponse::new(req, resp)) +} + +pub fn auth_middleware<S>( + req: ServiceRequest, + srv: &S, +) -> impl Future<Output = actix_web::Result<ServiceResponse>> + 'static +where + S: Service<ServiceRequest, Response = ServiceResponse, Error = actix_web::Error>, + S::Future: 'static, +{ + match handle_auth(req) { + Ok(req) => Either::Left(srv.call(req)), + Err(resp) => Either::Right(ready(Ok(resp))), } } |