2026-08-27 18:36:23 +05:00
|
|
|
use crate::Rejection;
|
|
|
|
|
use axum::{
|
|
|
|
|
extract::FromRequestParts,
|
2026-08-29 15:32:08 +02:00
|
|
|
http::{header::AUTHORIZATION, request::Parts},
|
2026-08-27 18:36:23 +05:00
|
|
|
};
|
|
|
|
|
use base64::{engine::general_purpose, Engine};
|
2026-08-29 15:32:08 +02:00
|
|
|
use tuliprox_core::model::{AuthRejection, AuthScheme};
|
2025-04-23 20:20:14 +02:00
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub struct AuthBasic(pub (String, String));
|
|
|
|
|
|
|
|
|
|
impl<B> FromRequestParts<B> for AuthBasic
|
|
|
|
|
where
|
|
|
|
|
B: Send + Sync,
|
|
|
|
|
{
|
|
|
|
|
type Rejection = Rejection;
|
|
|
|
|
|
2026-06-15 14:09:31 +02:00
|
|
|
fn from_request_parts(
|
|
|
|
|
req: &mut Parts,
|
|
|
|
|
_: &B,
|
|
|
|
|
) -> impl std::future::Future<Output = Result<Self, Self::Rejection>> + Send {
|
|
|
|
|
std::future::ready(Self::decode_request_parts(req))
|
2025-04-23 20:20:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AuthBasic {
|
2026-08-27 18:36:23 +05:00
|
|
|
fn from_header(contents: (String, String)) -> Self { Self(contents) }
|
2025-04-23 20:20:14 +02:00
|
|
|
|
|
|
|
|
fn decode_request_parts(req: &mut Parts) -> Result<Self, Rejection> {
|
|
|
|
|
let authorization = req
|
|
|
|
|
.headers
|
2026-08-29 15:32:08 +02:00
|
|
|
.get(AUTHORIZATION)
|
|
|
|
|
.ok_or(AuthRejection::MissingHeader(AuthScheme::Basic))?
|
2025-04-23 20:20:14 +02:00
|
|
|
.to_str()
|
2026-08-29 15:32:08 +02:00
|
|
|
.map_err(|_| AuthRejection::MalformedHeader(AuthScheme::Basic))?;
|
2025-04-23 20:20:14 +02:00
|
|
|
|
2026-08-29 15:32:08 +02:00
|
|
|
match authorization.split_once(' ') {
|
2026-03-17 14:02:46 +01:00
|
|
|
Some((scheme, contents)) if scheme.eq_ignore_ascii_case("Basic") => {
|
2026-08-29 15:32:08 +02:00
|
|
|
Ok(Self::from_header(decode(contents)?))
|
2026-08-27 18:36:23 +05:00
|
|
|
}
|
2026-08-29 15:32:08 +02:00
|
|
|
_ => Err(AuthRejection::WrongScheme(AuthScheme::Basic)),
|
2025-04-23 20:20:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Decodes the two parts of basic auth using the colon
|
|
|
|
|
fn decode(input: &str) -> Result<(String, String), Rejection> {
|
|
|
|
|
// Decode from base64 into a string
|
2026-08-29 15:32:08 +02:00
|
|
|
let decoded =
|
|
|
|
|
general_purpose::STANDARD.decode(input).map_err(|_| AuthRejection::MalformedHeader(AuthScheme::Basic))?;
|
|
|
|
|
let decoded = String::from_utf8(decoded).map_err(|_| AuthRejection::MalformedHeader(AuthScheme::Basic))?;
|
2025-04-23 20:20:14 +02:00
|
|
|
|
|
|
|
|
// Return depending on if password is present
|
2026-08-29 15:32:08 +02:00
|
|
|
decoded
|
|
|
|
|
.split_once(':')
|
|
|
|
|
.map(|(username, password)| (username.trim().to_string(), password.trim().to_string()))
|
|
|
|
|
.ok_or(AuthRejection::MissingBasicPassword)
|
2026-06-15 14:09:31 +02:00
|
|
|
}
|