qrush_engine/services/
basic_auth_service.rs

1// qrush/src/services/basic_auth_service.rs
2use actix_web::{
3    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
4    Error, HttpRequest, HttpResponse,
5};
6use base64::engine::general_purpose::STANDARD;
7use base64::Engine;
8use futures_util::future::{ready, Either, Ready}; // futures_util
9use std::future::Ready as StdReady;              // StdReady = for new_transform
10use crate::config::get_basic_auth;
11
12pub struct BasicAuthMiddleware;
13
14impl<S> Transform<S, ServiceRequest> for BasicAuthMiddleware
15where
16    S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,
17    S::Future: 'static,
18{
19    type Response = ServiceResponse;
20    type Error = Error;
21    type InitError = ();
22    type Transform = BasicAuthMiddlewareService<S>;
23    type Future = StdReady<Result<Self::Transform, Self::InitError>>;
24
25    fn new_transform(&self, service: S) -> Self::Future {
26        std::future::ready(Ok(BasicAuthMiddlewareService { service }))
27    }
28}
29
30pub struct BasicAuthMiddlewareService<S> {
31    service: S,
32}
33
34impl<S> Service<ServiceRequest> for BasicAuthMiddlewareService<S>
35where
36    S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> + 'static,
37    S::Future: 'static,
38{
39    type Response = ServiceResponse;
40    type Error = Error;
41
42    // Correct Future type: Either<Ready<Result<...>>, S::Future>
43    type Future = Either<Ready<Result<Self::Response, Self::Error>>, S::Future>;
44
45    forward_ready!(service);
46
47    fn call(&self, req: ServiceRequest) -> Self::Future {
48        if check_basic_auth(req.request()) {
49            Either::Right(self.service.call(req))
50        } else {
51            let response = req.into_response(unauthorized_response());
52            Either::Left(ready(Ok(response)))
53        }
54    }
55}
56
57pub fn check_basic_auth(req: &HttpRequest) -> bool {
58    if let Some(config) = get_basic_auth() {
59        if let Some(auth_header) = req.headers().get("Authorization") {
60            if let Ok(auth_str) = auth_header.to_str() {
61                if let Some(encoded) = auth_str.strip_prefix("Basic ") {
62                    if let Ok(decoded) = STANDARD.decode(encoded) {
63                        if let Ok(credentials) = std::str::from_utf8(&decoded) {
64                            let mut parts = credentials.splitn(2, ':');
65                            let user = parts.next().unwrap_or_default();
66                            let pass = parts.next().unwrap_or_default();
67                            if user == config.username && pass == config.password {
68                                return true;
69                            }
70                        }
71                    }
72                }
73            }
74        }
75        return false;
76    }
77    // If config is not set, allow by default
78    true
79}
80
81pub fn unauthorized_response() -> HttpResponse {
82    HttpResponse::Unauthorized()
83        .append_header(("WWW-Authenticate", r#"Basic realm="QRush""#))
84        .finish()
85}