diff options
Diffstat (limited to 'src/services')
| -rw-r--r-- | src/services/authorization.rs | 108 |
1 files changed, 70 insertions, 38 deletions
diff --git a/src/services/authorization.rs b/src/services/authorization.rs index b9d57ae..bfbbb5a 100644 --- a/src/services/authorization.rs +++ b/src/services/authorization.rs @@ -1,50 +1,82 @@ +use actix_web::{ + error::ParseError, + http::header::{self, Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue}, +}; use base64::Engine; use raise::yeet; -use thiserror::Error; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Error)] -pub enum ParseBasicError { - #[error("Basic Authorization is required")] - NotBasic, - #[error("No credentials were provided for authorization")] - NoCredentials, - #[error("The credentials provided were not base64")] - InvalidBase64, - #[error("The decoded base64 credentials were not UTF-8")] - NotUtf8, - #[error("A colon (:) must be used to delimit the username and password")] - NoColon, + +#[derive(Clone)] +pub struct BasicAuthorization { + username: Box<str>, + password: Box<str>, } -/// Returns a username and a password from a Basic authorization header -pub fn parse_basic(value: &str) -> Result<(Box<str>, Box<str>), ParseBasicError> { - if !value.starts_with("Basic") { - yeet!(ParseBasicError::NotBasic); - } +impl TryIntoHeaderValue for BasicAuthorization { + type Error = InvalidHeaderValue; - let value: String = value - .chars() - .skip(5) - .skip_while(|ch| ch.is_whitespace()) - .collect(); + fn try_into_value(self) -> Result<HeaderValue, Self::Error> { + let username = self.username; + let password = self.password; + let utf8 = format!("{username}:{password}"); + let b64 = base64::engine::general_purpose::STANDARD.encode(utf8); + let value = format!("Basic {b64}"); + HeaderValue::from_str(&value) + } +} - if value.is_empty() { - yeet!(ParseBasicError::NoCredentials); +impl Header for BasicAuthorization { + fn name() -> HeaderName { + header::AUTHORIZATION } - let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(value) else { - yeet!(ParseBasicError::InvalidBase64) - }; + fn parse<M: actix_web::HttpMessage>(msg: &M) -> Result<Self, actix_web::error::ParseError> { + let Some(value) = msg.headers().get(Self::name()) else { + yeet!(ParseError::Header) + }; + + let Ok(value) = value.to_str() else { + yeet!(ParseError::Header) + }; + + if !value.starts_with("Basic") { + yeet!(ParseError::Header); + } + + let value: String = value + .chars() + .skip(5) + .skip_while(|ch| ch.is_whitespace()) + .collect(); + + if value.is_empty() { + yeet!(ParseError::Header); + } - let Ok(value) = String::from_utf8(bytes) else { - yeet!(ParseBasicError::NotUtf8) - }; + let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(value) else { + yeet!(ParseError::Header) + }; - let mut parts = value.split(':'); - let username = parts.next().unwrap(); - let Some(password) = parts.next() else { - yeet!(ParseBasicError::NoColon) - }; + let Ok(value) = String::from_utf8(bytes) else { + yeet!(ParseError::Header) + }; - Ok((Box::from(username), Box::from(password))) + let mut parts = value.split(':'); + let username = Box::from(parts.next().unwrap()); + let Some(password) = parts.next() else { + yeet!(ParseError::Header) + }; + let password = Box::from(password); + + Ok(Self { username, password }) + } +} + +impl BasicAuthorization { + pub fn username(&self) -> &str { + &self.username + } + + pub fn password(&self) -> &str { + &self.password + } } |
