71 lines
2.4 KiB
Rust
71 lines
2.4 KiB
Rust
|
use anyhow::{anyhow, Result};
|
||
|
use base64::Engine;
|
||
|
use hyper::{Request, Response, body::Bytes};
|
||
|
use hyper::body::Incoming;
|
||
|
use http_body_util::combinators::BoxBody;
|
||
|
|
||
|
use aero_user::login::ArcLoginProvider;
|
||
|
use aero_collections::user::User;
|
||
|
|
||
|
use super::codec::text_body;
|
||
|
|
||
|
type ArcUser = std::sync::Arc<User>;
|
||
|
|
||
|
pub(super) async fn auth<'a>(
|
||
|
login: ArcLoginProvider,
|
||
|
req: Request<Incoming>,
|
||
|
next: impl Fn(ArcUser, Request<Incoming>) -> futures::future::BoxFuture<'a, Result<Response<BoxBody<Bytes, std::io::Error>>>>,
|
||
|
) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
||
|
let auth_val = match req.headers().get(hyper::header::AUTHORIZATION) {
|
||
|
Some(hv) => hv.to_str()?,
|
||
|
None => {
|
||
|
tracing::info!("Missing authorization field");
|
||
|
return Ok(Response::builder()
|
||
|
.status(401)
|
||
|
.header("WWW-Authenticate", "Basic realm=\"Aerogramme\"")
|
||
|
.body(text_body("Missing Authorization field"))?)
|
||
|
},
|
||
|
};
|
||
|
|
||
|
let b64_creds_maybe_padded = match auth_val.split_once(" ") {
|
||
|
Some(("Basic", b64)) => b64,
|
||
|
_ => {
|
||
|
tracing::info!("Unsupported authorization field");
|
||
|
return Ok(Response::builder()
|
||
|
.status(400)
|
||
|
.body(text_body("Unsupported Authorization field"))?)
|
||
|
},
|
||
|
};
|
||
|
|
||
|
// base64urlencoded may have trailing equals, base64urlsafe has not
|
||
|
// theoretically authorization is padded but "be liberal in what you accept"
|
||
|
let b64_creds_clean = b64_creds_maybe_padded.trim_end_matches('=');
|
||
|
|
||
|
// Decode base64
|
||
|
let creds = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64_creds_clean)?;
|
||
|
let str_creds = std::str::from_utf8(&creds)?;
|
||
|
|
||
|
// Split username and password
|
||
|
let (username, password) = str_creds
|
||
|
.split_once(':')
|
||
|
.ok_or(anyhow!("Missing colon in Authorization, can't split decoded value into a username/password pair"))?;
|
||
|
|
||
|
// Call login provider
|
||
|
let creds = match login.login(username, password).await {
|
||
|
Ok(c) => c,
|
||
|
Err(_) => {
|
||
|
tracing::info!(user=username, "Wrong credentials");
|
||
|
return Ok(Response::builder()
|
||
|
.status(401)
|
||
|
.header("WWW-Authenticate", "Basic realm=\"Aerogramme\"")
|
||
|
.body(text_body("Wrong credentials"))?)
|
||
|
},
|
||
|
};
|
||
|
|
||
|
// Build a user
|
||
|
let user = User::new(username.into(), creds).await?;
|
||
|
|
||
|
// Call router with user
|
||
|
next(user, req).await
|
||
|
}
|