From 70d82c3faad9204e47ed514b58c7bd7c5e7cb61a Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sun, 5 May 2019 14:30:13 +0700 Subject: Begin to support multiple auths --- src/args.rs | 30 +++++++++++++++++++++--------- src/auth.rs | 44 ++++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/args.rs b/src/args.rs index 63799a0..3972995 100644 --- a/src/args.rs +++ b/src/args.rs @@ -77,8 +77,20 @@ fn parse_interface(src: &str) -> Result { src.parse::() } -/// Checks wether the auth string is valid, i.e. it follows the syntax username:password +/// Parse a string of multiple authentication requirements fn parse_auth(src: &str) -> Result { + let mut required_auth = auth::RequiredAuth::new(); + + for pair in src.split_whitespace().map(parse_single_auth) { + let (username, password) = pair?; + required_auth.insert(username.to_owned(), password); + } + + 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); @@ -119,10 +131,7 @@ fn parse_auth(src: &str) -> Result { auth::RequiredAuthPassword::Plain(second_part.to_owned()) }; - Ok(auth::RequiredAuth { - username: username.to_owned(), - password, - }) + Ok((username.to_owned(), password)) } /// Parses the command line arguments @@ -172,16 +181,19 @@ mod tests { fn create_required_auth(username: &str, password: &str, encrypt: &str) -> auth::RequiredAuth { use auth::*; use RequiredAuthPassword::*; + let mut required_auth = RequiredAuth::new(); - RequiredAuth { - username: username.to_owned(), - password: match encrypt { + required_auth.insert( + username.to_owned(), + match encrypt { "plain" => Plain(password.to_owned()), "sha256" => Sha256(hex::decode(password.to_owned()).unwrap()), "sha512" => Sha512(hex::decode(password.to_owned()).unwrap()), _ => panic!("Unknown encryption type"), }, - } + ); + + required_auth } #[rstest_parametrize( diff --git a/src/auth.rs b/src/auth.rs index e526923..735929b 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -2,6 +2,7 @@ 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}; @@ -22,12 +23,8 @@ pub enum RequiredAuthPassword { Sha512(Vec), } -#[derive(Clone, Debug, PartialEq)] /// Authentication structure to match `BasicAuthParams` against -pub struct RequiredAuth { - pub username: String, - pub password: RequiredAuthPassword, -} +pub type RequiredAuth = HashMap; /// Decode a HTTP basic auth string into a tuple of username and password. pub fn parse_basic_auth( @@ -57,20 +54,20 @@ pub fn parse_basic_auth( /// Verify authentication pub fn match_auth(basic_auth: BasicAuthParams, required_auth: &RequiredAuth) -> bool { - if basic_auth.username != required_auth.username { - return false; - } - - match &required_auth.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) + 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) + } } + } else { + false } } @@ -150,16 +147,19 @@ mod tests { /// Helper function that creates a `RequiredAuth` structure and encrypt `password` if necessary fn create_required_auth(username: &str, password: &str, encrypt: &str) -> RequiredAuth { use RequiredAuthPassword::*; + let mut required_auth = RequiredAuth::new(); - RequiredAuth { - username: username.to_owned(), - password: match encrypt { + required_auth.insert( + username.to_owned(), + match encrypt { "plain" => Plain(password.to_owned()), "sha256" => Sha256(get_hash::(password.to_owned())), "sha512" => Sha512(get_hash::(password.to_owned())), _ => panic!("Unknown encryption type"), }, - } + ); + + required_auth } #[rstest_parametrize( -- cgit v1.2.3 From fd82250f1b1d594c6cb13943a72626e8a42c08c9 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sun, 5 May 2019 17:08:37 +0700 Subject: Add unit test for where it provides multiple valid auth strings --- src/args.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/args.rs b/src/args.rs index 3972995..3d6cac4 100644 --- a/src/args.rs +++ b/src/args.rs @@ -202,7 +202,7 @@ mod tests { case("username:sha256:abcd", "username", "abcd", "sha256"), case("username:sha512:abcd", "username", "abcd", "sha512") )] - fn parse_auth_valid(auth_string: &str, username: &str, password: &str, encrypt: &str) { + fn parse_single_auth_valid(auth_string: &str, username: &str, password: &str, encrypt: &str) { assert_eq!( parse_auth(auth_string).unwrap(), create_required_auth(username, password, encrypt), @@ -228,8 +228,32 @@ mod tests { "Invalid format for password hash. Expected hex code" ), )] - fn parse_auth_invalid(auth_string: &str, err_msg: &str) { + fn parse_single_auth_invalid(auth_string: &str, err_msg: &str) { let err = parse_auth(auth_string).unwrap_err(); assert_eq!(format!("{}", err), err_msg.to_owned()); } + + #[test] + fn parse_multiple_auth_valid() -> Result<(), ContextualError> { + let hex2str = |x: &str| hex::decode(x).expect("Invalid hex code"); + + let pairs = [ + ("usr0", auth::RequiredAuthPassword::Plain("pwd0".to_owned())), + ("usr1", auth::RequiredAuthPassword::Plain("pwd1".to_owned())), + ("usr2", auth::RequiredAuthPassword::Sha256(hex2str("abcd"))), + ("usr3", auth::RequiredAuthPassword::Sha512(hex2str("abcd"))), + ]; + + let mut expected = auth::RequiredAuth::new(); + for (username, password) in pairs.iter() { + expected.insert(username.to_owned().to_string(), password.clone()); + } + + assert_eq!( + parse_auth("usr0:pwd0 usr1:pwd1 usr2:sha256:abcd usr3:sha512:abcd")?, + expected, + ); + + Ok(()) + } } -- cgit v1.2.3 From 28f35beb4104bf777f3e84e4fe102599ec405ed6 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sun, 5 May 2019 21:21:34 +0700 Subject: Refactoring: Use immutable HashMap This should allow compiler to optimize further --- src/args.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/args.rs b/src/args.rs index 3d6cac4..4851b41 100644 --- a/src/args.rs +++ b/src/args.rs @@ -79,12 +79,13 @@ fn parse_interface(src: &str) -> Result { /// Parse a string of multiple authentication requirements fn parse_auth(src: &str) -> Result { - let mut required_auth = auth::RequiredAuth::new(); - - for pair in src.split_whitespace().map(parse_single_auth) { - let (username, password) = pair?; - required_auth.insert(username.to_owned(), password); - } + let required_auth = src + .split_whitespace() + .map(parse_single_auth) + .collect::, ContextualError>>()? + .iter() + .cloned() + .collect::(); Ok(required_auth) } @@ -237,17 +238,15 @@ mod tests { fn parse_multiple_auth_valid() -> Result<(), ContextualError> { let hex2str = |x: &str| hex::decode(x).expect("Invalid hex code"); - let pairs = [ + let expected: auth::RequiredAuth = [ ("usr0", auth::RequiredAuthPassword::Plain("pwd0".to_owned())), ("usr1", auth::RequiredAuthPassword::Plain("pwd1".to_owned())), ("usr2", auth::RequiredAuthPassword::Sha256(hex2str("abcd"))), ("usr3", auth::RequiredAuthPassword::Sha512(hex2str("abcd"))), - ]; - - let mut expected = auth::RequiredAuth::new(); - for (username, password) in pairs.iter() { - expected.insert(username.to_owned().to_string(), password.clone()); - } + ] + .iter() + .map(|(username, password)| (username.to_owned().to_string(), password.clone())) + .collect(); assert_eq!( parse_auth("usr0:pwd0 usr1:pwd1 usr2:sha256:abcd usr3:sha512:abcd")?, -- cgit v1.2.3 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 From 67a0af2bb3b171b6c3cce1b81e08be5929a3ccad Mon Sep 17 00:00:00 2001 From: khai96_ Date: Thu, 16 May 2019 19:52:12 +0700 Subject: Update unit tests --- src/args.rs | 49 +++++++++++++------------------------------------ src/auth.rs | 35 +++++++++++++++++------------------ 2 files changed, 30 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/args.rs b/src/args.rs index 0ec8a22..39efe7e 100644 --- a/src/args.rs +++ b/src/args.rs @@ -172,19 +172,18 @@ mod tests { fn create_required_auth(username: &str, password: &str, encrypt: &str) -> auth::RequiredAuth { use auth::*; use RequiredAuthPassword::*; - let mut required_auth = RequiredAuth::new(); - - required_auth.insert( - username.to_owned(), - match encrypt { - "plain" => Plain(password.to_owned()), - "sha256" => Sha256(hex::decode(password.to_owned()).unwrap()), - "sha512" => Sha512(hex::decode(password.to_owned()).unwrap()), - _ => panic!("Unknown encryption type"), - }, - ); - required_auth + let password = match encrypt { + "plain" => Plain(password.to_owned()), + "sha256" => Sha256(hex::decode(password.to_owned()).unwrap()), + "sha512" => Sha512(hex::decode(password.to_owned()).unwrap()), + _ => panic!("Unknown encryption type"), + }; + + auth::RequiredAuth { + username: username.to_owned(), + password, + } } #[rstest_parametrize( @@ -193,7 +192,7 @@ mod tests { case("username:sha256:abcd", "username", "abcd", "sha256"), case("username:sha512:abcd", "username", "abcd", "sha512") )] - fn parse_single_auth_valid(auth_string: &str, username: &str, password: &str, encrypt: &str) { + fn parse_auth_valid(auth_string: &str, username: &str, password: &str, encrypt: &str) { assert_eq!( parse_auth(auth_string).unwrap(), create_required_auth(username, password, encrypt), @@ -219,30 +218,8 @@ mod tests { "Invalid format for password hash. Expected hex code" ), )] - fn parse_single_auth_invalid(auth_string: &str, err_msg: &str) { + fn parse_auth_invalid(auth_string: &str, err_msg: &str) { let err = parse_auth(auth_string).unwrap_err(); assert_eq!(format!("{}", err), err_msg.to_owned()); } - - #[test] - fn parse_multiple_auth_valid() -> Result<(), ContextualError> { - let hex2str = |x: &str| hex::decode(x).expect("Invalid hex code"); - - let expected: auth::RequiredAuth = [ - ("usr0", auth::RequiredAuthPassword::Plain("pwd0".to_owned())), - ("usr1", auth::RequiredAuthPassword::Plain("pwd1".to_owned())), - ("usr2", auth::RequiredAuthPassword::Sha256(hex2str("abcd"))), - ("usr3", auth::RequiredAuthPassword::Sha512(hex2str("abcd"))), - ] - .iter() - .map(|(username, password)| (username.to_owned().to_string(), password.clone())) - .collect(); - - assert_eq!( - parse_auth("usr0:pwd0 usr1:pwd1 usr2:sha256:abcd usr3:sha512:abcd")?, - expected, - ); - - Ok(()) - } } diff --git a/src/auth.rs b/src/auth.rs index a30fe60..da5f4a9 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -22,7 +22,7 @@ pub enum RequiredAuthPassword { Sha512(Vec), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] /// Authentication structure to match `BasicAuthParams` against pub struct RequiredAuth { pub username: String, @@ -138,7 +138,7 @@ mod tests { use rstest::rstest_parametrize; /// Return a hashing function corresponds to given name - fn get_hash_func(name: &str) -> impl FnOnce(String) -> Vec { + fn get_hash_func(name: &str) -> impl FnOnce(&String) -> Vec { match name { "sha256" => get_hash::, "sha512" => get_hash::, @@ -154,26 +154,25 @@ mod tests { fn test_get_hash(password: &str, hash_method: &str, hash: &str) { let hash_func = get_hash_func(hash_method); let expected = hex::decode(hash).expect("Provided hash is not a valid hex code"); - let received = hash_func(password.to_owned()); + let received = hash_func(&password.to_owned()); assert_eq!(received, expected); } /// Helper function that creates a `RequiredAuth` structure and encrypt `password` if necessary fn create_required_auth(username: &str, password: &str, encrypt: &str) -> RequiredAuth { use RequiredAuthPassword::*; - let mut required_auth = RequiredAuth::new(); - - required_auth.insert( - username.to_owned(), - match encrypt { - "plain" => Plain(password.to_owned()), - "sha256" => Sha256(get_hash::(password.to_owned())), - "sha512" => Sha512(get_hash::(password.to_owned())), - _ => panic!("Unknown encryption type"), - }, - ); - - required_auth + + let password = match encrypt { + "plain" => Plain(password.to_owned()), + "sha256" => Sha256(get_hash::(&password.to_owned())), + "sha512" => Sha512(get_hash::(&password.to_owned())), + _ => panic!("Unknown encryption type"), + }; + + RequiredAuth { + username: username.to_owned(), + password, + } } #[rstest_parametrize( @@ -185,7 +184,7 @@ mod tests { case(true, "obi", "hello there", "obi", "hello there", "sha512"), case(false, "obi", "hello there", "obi", "hi!", "sha512"), )] - fn test_auth( + fn test_single_auth( should_pass: bool, param_username: &str, param_password: &str, @@ -199,7 +198,7 @@ mod tests { username: param_username.to_owned(), password: param_password.to_owned(), }, - &create_required_auth(required_username, required_password, encrypt), + &[create_required_auth(required_username, required_password, encrypt)], ), should_pass, ) -- cgit v1.2.3 From 5a86a54474ece601925e60dd7fae860d4f38fcf4 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Fri, 17 May 2019 22:27:31 +0700 Subject: Fix Clippy --- src/auth.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/auth.rs b/src/auth.rs index 3828265..1ae2ec8 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -63,7 +63,7 @@ pub fn match_auth(basic_auth: BasicAuthParams, required_auth: &[RequiredAuth]) - } /// 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 { +pub fn compare_password (basic_auth_pwd: &str, required_auth_pwd: &RequiredAuthPassword) -> bool { match &required_auth_pwd { RequiredAuthPassword::Plain(required_password) => { *basic_auth_pwd == *required_password @@ -78,12 +78,12 @@ pub fn compare_password (basic_auth_pwd: &String, required_auth_pwd: &RequiredAu } /// 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: &str, hash: &[u8]) -> bool { get_hash::(password) == hash } /// Get hash of a `text` -pub fn get_hash(text: &String) -> Vec { +pub fn get_hash(text: &str) -> Vec { let mut hasher = T::new(); hasher.input(text); hasher.result().to_vec() @@ -97,7 +97,7 @@ impl Middleware for Auth { ) -> Result { let required_auth = &req.state().auth; - if required_auth.len() == 0 { + if required_auth.is_empty() { return Ok(Response::Done(resp)); } @@ -191,7 +191,7 @@ mod tests { use rstest::rstest_parametrize; /// Return a hashing function corresponds to given name - fn get_hash_func(name: &str) -> impl FnOnce(&String) -> Vec { + fn get_hash_func(name: &str) -> impl FnOnce(&str) -> Vec { match name { "sha256" => get_hash::, "sha512" => get_hash::, -- cgit v1.2.3 From c923fa90b0e21e3dba355978d2d9122e175d0259 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Sat, 18 May 2019 13:46:04 +0700 Subject: Add unit test for multiple accounts --- src/auth.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/auth.rs b/src/auth.rs index 1ae2ec8..7cd11fe 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -188,7 +188,7 @@ fn build_unauthorized_response( #[cfg(test)] mod tests { use super::*; - use rstest::rstest_parametrize; + use rstest::{rstest, rstest_parametrize, fixture}; /// Return a hashing function corresponds to given name fn get_hash_func(name: &str) -> impl FnOnce(&str) -> Vec { @@ -256,4 +256,77 @@ mod tests { should_pass, ) } + + /// Helper function that creates a sample of multiple accounts + #[fixture] + fn account_sample() -> Vec { + [ + ("usr0", "pwd0", "plain"), + ("usr1", "pwd1", "plain"), + ("usr2", "pwd2", "sha256"), + ("usr3", "pwd3", "sha256"), + ("usr4", "pwd4", "sha512"), + ("usr5", "pwd5", "sha512"), + ] + .iter() + .map(|(username, password, encrypt)| create_required_auth(username, password, encrypt)) + .collect() + } + + #[rstest_parametrize( + username, password, + case("usr0", "pwd0"), + case("usr1", "pwd1"), + case("usr2", "pwd2"), + case("usr3", "pwd3"), + case("usr4", "pwd4"), + case("usr5", "pwd5"), + )] + fn test_multiple_auth_pass( + account_sample: Vec, + username: &str, + password: &str, + ) { + assert!(match_auth( + BasicAuthParams { + username: username.to_owned(), + password: password.to_owned(), + }, + &account_sample, + )); + } + + #[rstest] + fn test_multiple_auth_wrong_username(account_sample: Vec) { + assert_eq!(match_auth( + BasicAuthParams { + username: "unregistered user".to_owned(), + password: "pwd0".to_owned(), + }, + &account_sample, + ), false); + } + + #[rstest_parametrize( + username, password, + case("usr0", "pwd5"), + case("usr1", "pwd4"), + case("usr2", "pwd3"), + case("usr3", "pwd2"), + case("usr4", "pwd1"), + case("usr5", "pwd0"), + )] + fn test_multiple_auth_wrong_password( + account_sample: Vec, + username: &str, + password: &str, + ) { + assert_eq!(match_auth( + BasicAuthParams { + username: username.to_owned(), + password: password.to_owned(), + }, + &account_sample, + ), false); + } } -- cgit v1.2.3 From 197a52889f00d2b90fe67cf10d274db6761cbbb3 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Fri, 24 May 2019 13:57:05 +0700 Subject: Improve doc comment --- src/auth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/auth.rs b/src/auth.rs index 7cd11fe..762746b 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -53,7 +53,7 @@ pub fn parse_basic_auth( }) } -/// Verify authentication +/// Return `true` if `basic_auth` is matches any of `required_auth` pub fn match_auth(basic_auth: BasicAuthParams, required_auth: &[RequiredAuth]) -> bool { required_auth.iter().any( |RequiredAuth { username, password }| -- cgit v1.2.3 From 4e6721096f9b11886f8eff45f40ec8fd23facea2 Mon Sep 17 00:00:00 2001 From: khai96_ Date: Thu, 30 May 2019 17:29:50 +0700 Subject: Fix regression where path cannot be placed after '--auth ' --- src/args.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/args.rs b/src/args.rs index 11357c2..ab6e81c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -41,7 +41,12 @@ struct CLIArgs { /// Set authentication. Currently supported formats: /// username:password, username:sha256:hash, username:sha512:hash /// (e.g. joe:123, joe:sha256:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3) - #[structopt(short = "a", long = "auth", parse(try_from_str = "parse_auth"))] + #[structopt( + short = "a", + long = "auth", + parse(try_from_str = "parse_auth"), + raw(number_of_values = "1") + )] auth: Vec, /// Generate a random 6-hexdigit route -- cgit v1.2.3 From e92efb863f10e36522ac1241b16276cd55686e1f Mon Sep 17 00:00:00 2001 From: khai96_ Date: Thu, 30 May 2019 17:34:09 +0700 Subject: Apply WWW_AUTHENTICATE to all Unauthorized branches --- src/auth.rs | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'src') diff --git a/src/auth.rs b/src/auth.rs index 762746b..2c98622 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -122,17 +122,6 @@ impl Middleware for Auth { if match_auth(auth_req, required_auth) { return Ok(Response::Done(resp)); } - - return Ok(Response::Done( - HttpResponse::Unauthorized().body( - build_unauthorized_response( - &req, - ContextualError::InvalidHTTPCredentials, - true, - StatusCode::UNAUTHORIZED, - ) - ) - )); } Ok(Response::Done( -- cgit v1.2.3