From 784b5fd79b071937ca1ade12caf9be291b53dff6 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Thu, 16 May 2019 16:53:27 +0700 Subject: Make some changes --- src/args.rs | 22 ++++---------- src/auth.rs | 96 +++++++++++++++++++++++++++++++++++-------------------------- src/main.rs | 2 +- 3 files changed, 62 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/args.rs b/src/args.rs index 4851b41..0ec8a22 100644 --- a/src/args.rs +++ b/src/args.rs @@ -41,7 +41,7 @@ struct CLIArgs { /// Set authentication. Currently supported formats: /// username:password, username:sha256:hash, username:sha512:hash #[structopt(short = "a", long = "auth", parse(try_from_str = "parse_auth"))] - auth: Option, + auth: Vec, /// Generate a random 6-hexdigit route #[structopt(long = "random-route")] @@ -77,21 +77,8 @@ fn parse_interface(src: &str) -> Result { src.parse::() } -/// Parse a string of multiple authentication requirements +/// Parse authentication requirement fn parse_auth(src: &str) -> Result { - let required_auth = src - .split_whitespace() - .map(parse_single_auth) - .collect::, ContextualError>>()? - .iter() - .cloned() - .collect::(); - - Ok(required_auth) -} - -/// Parse a single authentication requirement -fn parse_single_auth(src: &str) -> Result<(String, auth::RequiredAuthPassword), ContextualError> { let mut split = src.splitn(3, ':'); let invalid_auth_format = Err(ContextualError::InvalidAuthFormat); @@ -132,7 +119,10 @@ fn parse_single_auth(src: &str) -> Result<(String, auth::RequiredAuthPassword), auth::RequiredAuthPassword::Plain(second_part.to_owned()) }; - Ok((username.to_owned(), password)) + Ok(auth::RequiredAuth { + username: username.to_owned(), + password, + }) } /// Parses the command line arguments diff --git a/src/auth.rs b/src/auth.rs index 735929b..a30fe60 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -2,7 +2,6 @@ use actix_web::http::header; use actix_web::middleware::{Middleware, Response}; use actix_web::{HttpRequest, HttpResponse, Result}; use sha2::{Digest, Sha256, Sha512}; -use std::collections::HashMap; use crate::errors::{ContextualError}; @@ -23,8 +22,12 @@ pub enum RequiredAuthPassword { Sha512(Vec), } +#[derive(Clone, Debug)] /// Authentication structure to match `BasicAuthParams` against -pub type RequiredAuth = HashMap; +pub struct RequiredAuth { + pub username: String, + pub password: RequiredAuthPassword, +} /// Decode a HTTP basic auth string into a tuple of username and password. pub fn parse_basic_auth( @@ -53,31 +56,36 @@ pub fn parse_basic_auth( } /// Verify authentication -pub fn match_auth(basic_auth: BasicAuthParams, required_auth: &RequiredAuth) -> bool { - if let Some(password) = required_auth.get(&basic_auth.username) { - match &password { - RequiredAuthPassword::Plain(ref required_password) => { - basic_auth.password == *required_password - } - RequiredAuthPassword::Sha256(password_hash) => { - compare_hash::(basic_auth.password, password_hash) - } - RequiredAuthPassword::Sha512(password_hash) => { - compare_hash::(basic_auth.password, password_hash) - } +pub fn match_auth(basic_auth: BasicAuthParams, required_auth: &[RequiredAuth]) -> bool { + required_auth.iter().any( + |RequiredAuth { username, password }| + basic_auth.username == *username && + compare_password(&basic_auth.password, password) + ) +} + +/// Return `true` if `basic_auth_pwd` meets `required_auth_pwd`'s requirement +pub fn compare_password (basic_auth_pwd: &String, required_auth_pwd: &RequiredAuthPassword) -> bool { + match &required_auth_pwd { + RequiredAuthPassword::Plain(required_password) => { + *basic_auth_pwd == *required_password + } + RequiredAuthPassword::Sha256(password_hash) => { + compare_hash::(basic_auth_pwd, password_hash) + } + RequiredAuthPassword::Sha512(password_hash) => { + compare_hash::(basic_auth_pwd, password_hash) } - } else { - false } } /// Return `true` if hashing of `password` by `T` algorithm equals to `hash` -pub fn compare_hash(password: String, hash: &[u8]) -> bool { +pub fn compare_hash(password: &String, hash: &[u8]) -> bool { get_hash::(password) == hash } /// Get hash of a `text` -pub fn get_hash(text: String) -> Vec { +pub fn get_hash(text: &String) -> Vec { let mut hasher = T::new(); hasher.input(text); hasher.result().to_vec() @@ -89,32 +97,38 @@ impl Middleware for Auth { req: &HttpRequest, resp: HttpResponse, ) -> Result { - if let Some(ref required_auth) = req.state().auth { - 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)); - return Ok(Response::Done( - HttpResponse::BadRequest().body(auth_err.to_string()), - )); - } - }; - if !match_auth(auth_req, required_auth) { - let new_resp = HttpResponse::Unauthorized().finish(); - return Ok(Response::Done(new_resp)); + let required_auth = &req.state().auth; + + if required_auth.len() == 0 { + return Ok(Response::Done(resp)); + } + + 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)); + return Ok(Response::Done( + HttpResponse::BadRequest().body(auth_err.to_string()), + )); } - } else { - let new_resp = HttpResponse::Unauthorized() - .header( - header::WWW_AUTHENTICATE, - header::HeaderValue::from_static("Basic realm=\"miniserve\""), - ) - .finish(); - return Ok(Response::Done(new_resp)); + }; + + if match_auth(auth_req, required_auth) { + return Ok(Response::Done(resp)); } + + let new_resp = HttpResponse::Unauthorized().finish(); + return Ok(Response::Done(new_resp)); } - Ok(Response::Done(resp)) + + let new_resp = HttpResponse::Unauthorized() + .header( + header::WWW_AUTHENTICATE, + header::HeaderValue::from_static("Basic realm=\"miniserve\""), + ) + .finish(); + Ok(Response::Done(new_resp)) } } diff --git a/src/main.rs b/src/main.rs index ea58fc6..f227be9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,7 +37,7 @@ pub struct MiniserveConfig { pub interfaces: Vec, /// Enable HTTP basic authentication - pub auth: Option, + pub auth: Vec, /// If false, miniserve will serve the current working directory pub path_explicitly_chosen: bool, -- cgit v1.2.3