aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/auth.rs151
-rw-r--r--src/errors.rs7
-rw-r--r--src/main.rs6
3 files changed, 35 insertions, 129 deletions
diff --git a/src/auth.rs b/src/auth.rs
index 31826d8..91c7bb0 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -1,14 +1,12 @@
-use actix_web::dev::{Body, Service, ServiceRequest, ServiceResponse, Transform};
+use actix_web::dev::ServiceRequest;
use actix_web::http::{header, StatusCode};
use actix_web::{HttpRequest, HttpResponse, Result};
-use futures::future;
+use actix_web_httpauth::extractors::basic::BasicAuth;
use sha2::{Digest, Sha256, Sha512};
use crate::errors::{self, ContextualError};
use crate::renderer;
-pub struct Auth;
-
#[derive(Clone, Debug)]
/// HTTP Basic authentication parameters
pub struct BasicAuthParams {
@@ -16,6 +14,15 @@ pub struct BasicAuthParams {
pub password: String,
}
+impl From<BasicAuth> for BasicAuthParams {
+ fn from(auth: BasicAuth) -> Self {
+ Self {
+ username: auth.user_id().to_string(),
+ password: auth.password().unwrap_or(&"".into()).to_string(),
+ }
+ }
+}
+
#[derive(Clone, Debug, PartialEq)]
/// `password` field of `RequiredAuth`
pub enum RequiredAuthPassword {
@@ -31,29 +38,6 @@ pub struct RequiredAuth {
pub password: RequiredAuthPassword,
}
-/// Decode a HTTP basic auth string into a tuple of username and password.
-pub fn parse_basic_auth(
- authorization_header: &header::HeaderValue,
-) -> Result<BasicAuthParams, ContextualError> {
- let basic_removed = authorization_header
- .to_str()
- .map_err(|e| {
- ContextualError::ParseError("HTTP authentication header".to_string(), e.to_string())
- })?
- .replace("Basic ", "");
- let decoded = base64::decode(&basic_removed).map_err(ContextualError::Base64DecodeError)?;
- let decoded_str = String::from_utf8_lossy(&decoded);
- let credentials: Vec<&str> = decoded_str.splitn(2, ':').collect();
-
- // If argument parsing went fine, it means the HTTP credentials string is well formatted
- // So we can safely unpack the username and the password
-
- Ok(BasicAuthParams {
- username: credentials[0].to_owned(),
- password: credentials[1].to_owned(),
- })
-}
-
/// Return `true` if `basic_auth` is matches any of `required_auth`
pub fn match_auth(basic_auth: BasicAuthParams, required_auth: &[RequiredAuth]) -> bool {
required_auth
@@ -88,100 +72,25 @@ pub fn get_hash<T: Digest>(text: &str) -> Vec<u8> {
hasher.finalize().to_vec()
}
-pub struct AuthMiddleware<S> {
- service: S,
-}
-
-impl<S> Transform<S> for Auth
-where
- S: Service<
- Request = ServiceRequest,
- Response = ServiceResponse<Body>,
- Error = actix_web::Error,
- >,
- S::Future: 'static,
-{
- type Request = ServiceRequest;
- type Response = ServiceResponse<Body>;
- type Error = S::Error;
- type Future = future::Ready<Result<Self::Transform, Self::InitError>>;
- type Transform = AuthMiddleware<S>;
- type InitError = ();
- fn new_transform(&self, service: S) -> Self::Future {
- future::ok(AuthMiddleware { service })
- }
-}
-
-impl<S> Service for AuthMiddleware<S>
-where
- S: Service<
- Request = ServiceRequest,
- Response = ServiceResponse<Body>,
- Error = actix_web::Error,
- >,
- S::Future: 'static,
-{
- type Request = ServiceRequest;
- type Response = ServiceResponse<Body>;
- type Error = S::Error;
- type Future =
- std::pin::Pin<Box<dyn future::Future<Output = Result<Self::Response, Self::Error>>>>;
-
- fn poll_ready(
- &mut self,
- cx: &mut std::task::Context,
- ) -> std::task::Poll<Result<(), Self::Error>> {
- self.service.poll_ready(cx)
- }
-
- fn call(&mut self, req: Self::Request) -> Self::Future {
- let (req, pl) = req.into_parts();
- let required_auth = &req.app_data::<crate::MiniserveConfig>().unwrap().auth;
-
- if required_auth.is_empty() {
- let resp = self
- .service
- .call(ServiceRequest::from_parts(req, pl).unwrap_or_else(|_| unreachable!()));
- return Box::pin(async { resp.await });
- }
-
- if let Some(auth_headers) = req.headers().get(header::AUTHORIZATION) {
- let auth_req = match parse_basic_auth(auth_headers) {
- Ok(auth_req) => auth_req,
- Err(err) => {
- let auth_err = ContextualError::HTTPAuthenticationError(Box::new(err));
- let body =
- build_unauthorized_response(&req, auth_err, true, StatusCode::BAD_REQUEST);
- return Box::pin(future::ok(ServiceResponse::new(
- req,
- HttpResponse::BadRequest().body(body),
- )));
- }
- };
-
- if match_auth(auth_req, required_auth) {
- let resp = self
- .service
- .call(ServiceRequest::from_parts(req, pl).unwrap_or_else(|_| unreachable!()));
- return Box::pin(async { resp.await });
- }
- }
-
- let body = build_unauthorized_response(
- &req,
- ContextualError::InvalidHTTPCredentials,
- true,
- StatusCode::UNAUTHORIZED,
- );
- Box::pin(future::ok(ServiceResponse::new(
- req,
- HttpResponse::Unauthorized()
- .header(
- header::WWW_AUTHENTICATE,
- header::HeaderValue::from_static("Basic realm=\"miniserve\""),
- )
- .body(body),
- )))
+pub async fn handle_auth(req: ServiceRequest, cred: BasicAuth) -> Result<ServiceRequest> {
+ 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())
}
}
diff --git a/src/errors.rs b/src/errors.rs
index feb91e2..cb6efa8 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -14,13 +14,6 @@ pub enum ContextualError {
#[fail(display = "Failed to process multipart request\ncaused by: {}", _0)]
MultipartError(actix_multipart::MultipartError),
- /// This error might occur when decoding the HTTP authentication header.
- #[fail(
- display = "Failed to decode HTTP authentication header\ncaused by: {}",
- _0
- )]
- Base64DecodeError(base64::DecodeError),
-
/// Any error related to an invalid path (failed to retrieve entry name, unexpected entry type, etc)
#[fail(display = "Invalid path\ncaused by: {}", _0)]
InvalidPathError(String),
diff --git a/src/main.rs b/src/main.rs
index 1477652..d27e9c2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,7 @@
use actix_web::http::StatusCode;
use actix_web::web;
use actix_web::{middleware, App, HttpRequest, HttpResponse};
+use actix_web_httpauth::middleware::HttpAuthentication;
use std::io::{self, Write};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::thread;
@@ -233,7 +234,10 @@ async fn run() -> Result<(), ContextualError> {
let srv = actix_web::HttpServer::new(move || {
App::new()
.app_data(inside_config.clone())
- .wrap(auth::Auth)
+ .wrap(middleware::Condition::new(
+ !inside_config.auth.is_empty(),
+ HttpAuthentication::basic(auth::handle_auth),
+ ))
.wrap(middleware::Logger::default())
.configure(|c| configure_app(c, &inside_config))
.default_service(web::get().to(error_404))