summaryrefslogtreecommitdiff
path: root/src/services/authorization.rs
blob: bfbbb5a40bd17bf1be2ba53c45d0615054a89016 (plain)
use actix_web::{
	error::ParseError,
	http::header::{self, Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue},
};
use base64::Engine;
use raise::yeet;

#[derive(Clone)]
pub struct BasicAuthorization {
	username: Box<str>,
	password: Box<str>,
}

impl TryIntoHeaderValue for BasicAuthorization {
	type Error = InvalidHeaderValue;

	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)
	}
}

impl Header for BasicAuthorization {
	fn name() -> HeaderName {
		header::AUTHORIZATION
	}

	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(bytes) = base64::engine::general_purpose::STANDARD.decode(value) else {
			yeet!(ParseError::Header)
		};

		let Ok(value) = String::from_utf8(bytes) else {
			yeet!(ParseError::Header)
		};

		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
	}
}