aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/args.rs22
-rw-r--r--src/auth.rs96
-rw-r--r--src/main.rs2
3 files changed, 62 insertions, 58 deletions
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::RequiredAuth>,
+ auth: Vec<auth::RequiredAuth>,
/// Generate a random 6-hexdigit route
#[structopt(long = "random-route")]
@@ -77,21 +77,8 @@ fn parse_interface(src: &str) -> Result<IpAddr, std::net::AddrParseError> {
src.parse::<IpAddr>()
}
-/// Parse a string of multiple authentication requirements
+/// Parse authentication requirement
fn parse_auth(src: &str) -> Result<auth::RequiredAuth, ContextualError> {
- let required_auth = src
- .split_whitespace()
- .map(parse_single_auth)
- .collect::<Result<Vec<_>, ContextualError>>()?
- .iter()
- .cloned()
- .collect::<auth::RequiredAuth>();
-
- 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<u8>),
}
+#[derive(Clone, Debug)]
/// Authentication structure to match `BasicAuthParams` against
-pub type RequiredAuth = HashMap<String, RequiredAuthPassword>;
+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::<Sha256>(basic_auth.password, password_hash)
- }
- RequiredAuthPassword::Sha512(password_hash) => {
- compare_hash::<Sha512>(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::<Sha256>(basic_auth_pwd, password_hash)
+ }
+ RequiredAuthPassword::Sha512(password_hash) => {
+ compare_hash::<Sha512>(basic_auth_pwd, password_hash)
}
- } else {
- false
}
}
/// Return `true` if hashing of `password` by `T` algorithm equals to `hash`
-pub fn compare_hash<T: Digest>(password: String, hash: &[u8]) -> bool {
+pub fn compare_hash<T: Digest>(password: &String, hash: &[u8]) -> bool {
get_hash::<T>(password) == hash
}
/// Get hash of a `text`
-pub fn get_hash<T: Digest>(text: String) -> Vec<u8> {
+pub fn get_hash<T: Digest>(text: &String) -> Vec<u8> {
let mut hasher = T::new();
hasher.input(text);
hasher.result().to_vec()
@@ -89,32 +97,38 @@ impl Middleware<crate::MiniserveConfig> for Auth {
req: &HttpRequest<crate::MiniserveConfig>,
resp: HttpResponse,
) -> Result<Response> {
- 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<IpAddr>,
/// Enable HTTP basic authentication
- pub auth: Option<auth::RequiredAuth>,
+ pub auth: Vec<auth::RequiredAuth>,
/// If false, miniserve will serve the current working directory
pub path_explicitly_chosen: bool,