diff options
Diffstat (limited to 'src/auth.rs')
-rw-r--r-- | src/auth.rs | 140 |
1 files changed, 137 insertions, 3 deletions
diff --git a/src/auth.rs b/src/auth.rs index 10e7a4a..a4b3555 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,6 +1,7 @@ use actix_web::http::header; use actix_web::middleware::{Middleware, Response}; use actix_web::{HttpRequest, HttpResponse, Result}; +use sha2::{Digest, Sha256, Sha512}; pub struct Auth; @@ -16,6 +17,20 @@ pub struct BasicAuthParams { pub password: String, } +#[derive(Clone, Debug, PartialEq)] +pub enum RequiredAuthPassword { + Plain(String), + Sha256(Vec<u8>), + Sha512(Vec<u8>), +} + +#[derive(Clone, Debug, PartialEq)] +/// Authentication structure to match BasicAuthParams against +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( authorization_header: &header::HeaderValue, @@ -34,6 +49,34 @@ pub fn parse_basic_auth( }) } +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::<Sha256>(basic_auth.password, password_hash) + } + RequiredAuthPassword::Sha512(password_hash) => { + compare_hash::<Sha512>(basic_auth.password, password_hash) + } + } +} + +pub fn compare_hash<T: Digest>(password: String, hash: &Vec<u8>) -> bool { + get_hash::<T>(password) == *hash +} + +pub fn get_hash<T: Digest>(text: String) -> Vec<u8> { + let mut hasher = T::new(); + hasher.input(text); + hasher.result().to_vec() +} + impl Middleware<crate::MiniserveConfig> for Auth { fn response( &self, @@ -51,9 +94,7 @@ impl Middleware<crate::MiniserveConfig> for Auth { )))); } }; - if auth_req.username != required_auth.username - || auth_req.password != required_auth.password - { + if !match_auth(auth_req, required_auth) { let new_resp = HttpResponse::Unauthorized().finish(); return Ok(Response::Done(new_resp)); } @@ -70,3 +111,96 @@ impl Middleware<crate::MiniserveConfig> for Auth { Ok(Response::Done(resp)) } } + +#[cfg(test)] +mod tests { + use super::*; + + fn assert_hex_eq(expectation: &str, received: Vec<u8>) { + let bin = hex::decode(expectation).expect("Provided string is not a valid hex code"); + assert_eq!(bin, received); + } + + #[test] + fn get_hash_hex_sha256() { + let expectation = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; + let received = get_hash::<Sha256>("abc".to_owned()); + assert_hex_eq(expectation, received); + } + + #[test] + fn get_hash_hex_sha512() { + let expectation = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; + let received = get_hash::<Sha512>("abc".to_owned()); + assert_hex_eq(expectation, received); + } + + fn create_auth_params(username: &str, password: &str) -> BasicAuthParams { + BasicAuthParams { + username: username.to_owned(), + password: password.to_owned(), + } + } + + fn create_required_auth(username: &str, password: &str, encrypt: &str) -> RequiredAuth { + use RequiredAuthPassword::*; + + RequiredAuth { + username: username.to_owned(), + password: match encrypt { + "plain" => Plain(password.to_owned()), + "sha256" => Sha256(get_hash::<sha2::Sha256>(password.to_owned())), + "sha512" => Sha512(get_hash::<sha2::Sha512>(password.to_owned())), + _ => panic!("Unknown encryption type"), + }, + } + } + + #[test] + fn match_auth_plain_password_should_pass() { + assert!(match_auth( + create_auth_params("obi", "hello there"), + &create_required_auth("obi", "hello there", "plain"), + )); + } + + #[test] + fn match_auth_plain_password_should_fail() { + assert!(!match_auth( + create_auth_params("obi", "hello there"), + &create_required_auth("obi", "hi!", "plain"), + )); + } + + #[test] + fn match_auth_sha256_password_should_pass() { + assert!(match_auth( + create_auth_params("obi", "hello there"), + &create_required_auth("obi", "hello there", "sha256"), + )); + } + + #[test] + fn match_auth_sha256_password_should_fail() { + assert!(!match_auth( + create_auth_params("obi", "hello there"), + &create_required_auth("obi", "hi!", "sha256"), + )); + } + + #[test] + fn match_auth_sha512_password_should_pass() { + assert!(match_auth( + create_auth_params("obi", "hello there"), + &create_required_auth("obi", "hello there", "sha512"), + )); + } + + #[test] + fn match_auth_sha512_password_should_fail() { + assert!(!match_auth( + create_auth_params("obi", "hello there"), + &create_required_auth("obi", "hi!", "sha512"), + )); + } +} |