use crate::{ config::DynNode, error::ServiceError, modules::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse}, }; use base64::Engine; use futures::Future; use http_body_util::{combinators::BoxBody, BodyExt}; use hyper::{ header::{HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE}, Response, StatusCode, }; use log::debug; use serde::Deserialize; use serde_yaml::Value; use std::{pin::Pin, sync::Arc}; use super::Credentials; pub struct HttpBasicAuthKind; impl NodeKind for HttpBasicAuthKind { fn name(&self) -> &'static str { "http_basic_auth" } fn instanciate(&self, config: Value) -> anyhow::Result> { Ok(Arc::new(serde_yaml::from_value::(config)?)) } } #[derive(Deserialize)] pub struct HttpBasicAuth { realm: String, users: Credentials, next: DynNode, } impl Node for HttpBasicAuth { fn handle<'a>( &'a self, context: &'a mut NodeContext, request: NodeRequest, ) -> Pin> + Send + Sync + 'a>> { Box::pin(async move { if let Some(auth) = request.headers().get(AUTHORIZATION) { let k = auth .as_bytes() .strip_prefix(b"Basic ") .ok_or(ServiceError::BadAuth)?; let k = base64::engine::general_purpose::STANDARD.decode(k)?; let k = String::from_utf8(k)?; let (username, password) = k.split_once(":").ok_or(ServiceError::BadAuth)?; if self.users.authentificate(username, password) { debug!("valid auth"); return self.next.handle(context, request).await; } else { debug!("invalid auth"); } } debug!("unauthorized; sending auth challenge"); let mut r = Response::new(BoxBody::<_, ServiceError>::new( String::new().map_err(|_| unreachable!()), )); *r.status_mut() = StatusCode::UNAUTHORIZED; r.headers_mut().insert( WWW_AUTHENTICATE, HeaderValue::from_str(&format!("Basic realm=\"{}\"", self.realm)).unwrap(), ); Ok(r) }) } }