qrush_engine/services/
basic_auth_service.rs1use actix_web::{
8 body::EitherBody,
9 dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
10 Error,
11};
12use base64::engine::general_purpose::STANDARD;
13use base64::Engine;
14use futures_util::future::{ready, Either, Ready};
15use std::future::Ready as StdReady;
16
17use crate::config::get_basic_auth;
18
19pub struct BasicAuthMiddleware;
20
21impl<S, B> Transform<S, ServiceRequest> for BasicAuthMiddleware
22where
23 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
24 B: actix_web::body::MessageBody + 'static,
25{
26 type Response = ServiceResponse<EitherBody<B>>;
27 type Error = Error;
28 type Transform = BasicAuthMiddlewareService<S>;
29 type InitError = ();
30 type Future = StdReady<Result<Self::Transform, Self::InitError>>;
31
32 fn new_transform(&self, service: S) -> Self::Future {
33 std::future::ready(Ok(BasicAuthMiddlewareService { service }))
34 }
35}
36
37pub struct BasicAuthMiddlewareService<S> {
38 service: S,
39}
40
41impl<S, B> Service<ServiceRequest> for BasicAuthMiddlewareService<S>
42where
43 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
44 B: actix_web::body::MessageBody + 'static,
45{
46 type Response = ServiceResponse<EitherBody<B>>;
47 type Error = Error;
48 type Future = Either<
49 futures_util::future::LocalBoxFuture<'static, Result<Self::Response, Self::Error>>,
50 Ready<Result<Self::Response, Self::Error>>,
51 >;
52
53 forward_ready!(service);
54
55 fn call(&self, req: ServiceRequest) -> Self::Future {
56 let Some(cfg) = get_basic_auth() else {
58 let fut = self.service.call(req);
59 return Either::Left(Box::pin(async move {
60 let res = fut.await?;
61 Ok(res.map_into_left_body())
62 }));
63 };
64
65 let auth_hdr = req
67 .headers()
68 .get(actix_web::http::header::AUTHORIZATION)
69 .and_then(|v| v.to_str().ok())
70 .unwrap_or("");
71
72 if let Some(encoded) = auth_hdr.strip_prefix("Basic ").map(str::trim) {
73 if let Ok(decoded) = STANDARD.decode(encoded) {
74 if let Ok(pair) = String::from_utf8(decoded) {
75 let mut it = pair.splitn(2, ':');
76 let user = it.next().unwrap_or("");
77 let pass = it.next().unwrap_or("");
78 if user == cfg.username && pass == cfg.password {
79 let fut = self.service.call(req);
80 return Either::Left(Box::pin(async move {
81 let res = fut.await?;
82 Ok(res.map_into_left_body())
83 }));
84 }
85 }
86 }
87 }
88
89 let res = actix_web::HttpResponse::Unauthorized()
90 .finish()
91 .map_into_right_body();
92
93 Either::Right(ready(Ok(req.into_response(res))))
94 }
95}