drogue_bazaar/actix/auth/authorization/
middleware.rs

1use super::{AuthZ, Context};
2use crate::auth::{UserInformation, ANONYMOUS};
3use actix_http::body::EitherBody;
4use actix_service::{Service, Transform};
5use actix_web::{
6    dev::{ServiceRequest, ServiceResponse},
7    Error, HttpMessage,
8};
9use futures_util::future::{ok, LocalBoxFuture, Ready};
10use std::rc::Rc;
11
12pub struct AuthMiddleware<S> {
13    service: Rc<S>,
14    authorizer: AuthZ,
15}
16
17// 1. Middleware initialization
18// Middleware factory is `Transform` trait from actix-service crate
19// `S` - type of the next service
20// `B` - type of response's body
21impl<S, B> Transform<S, ServiceRequest> for AuthZ
22where
23    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
24    S::Future: 'static,
25    B: 'static,
26{
27    type Response = ServiceResponse<EitherBody<B>>;
28    type Error = S::Error;
29    type Transform = AuthMiddleware<S>;
30    type InitError = ();
31    type Future = Ready<Result<Self::Transform, Self::InitError>>;
32
33    fn new_transform(&self, service: S) -> Self::Future {
34        ok(AuthMiddleware {
35            service: Rc::new(service),
36            authorizer: self.clone(),
37        })
38    }
39}
40
41// 2. Middleware's call method gets called with normal request.
42impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
43where
44    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
45    S::Future: 'static,
46    B: 'static,
47{
48    type Response = ServiceResponse<EitherBody<B>>;
49    type Error = Error;
50    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
51
52    actix_service::forward_ready!(service);
53
54    fn call(&self, req: ServiceRequest) -> Self::Future {
55        let srv = Rc::clone(&self.service);
56        let auth = self.authorizer.clone();
57
58        Box::pin(async move {
59            let result = {
60                // extract user information and application from the request
61                let ext = req.extensions();
62                let identity = ext.get::<UserInformation>().unwrap_or(&ANONYMOUS);
63
64                let context = Context {
65                    identity,
66                    request: &req,
67                };
68
69                auth.authorize(context).await
70            };
71
72            match result {
73                Ok(()) => {
74                    // forward request to the next service
75                    srv.call(req).await.map(|res| res.map_into_left_body())
76                }
77                Err(err) => {
78                    log::debug!("Authorization error: {err}");
79                    Ok(req.error_response(err).map_into_right_body())
80                }
81            }
82        })
83    }
84}