actix_permissions/
service.rs

1//! [`PermissionService`] struct and `Service<ServiceRequest>` implementation.
2use std::convert::Infallible;
3use std::future::Future;
4use std::marker::PhantomData;
5use std::pin::Pin;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::Arc;
8use std::task::{Context, Poll};
9
10use actix_web::dev::*;
11use actix_web::web::{Buf, Bytes, BytesMut};
12use actix_web::*;
13use futures_util::stream::StreamExt as _;
14
15use crate::permission::Permission;
16
17/// Service that intercepts a request and validates it with a permission.
18/// If permission fails, `deny_handler` is called.
19/// If permission succeeds, request is proxied to `handler`.
20///
21/// # Properties
22/// * `handler` - handler, a function that returns http (serializable) response.
23/// * `pd_handler` - marker for handler type args, needed to avoid warnings of unused `Args`.
24/// * `deny_handler` - function argument, called when permission check is false.
25/// * `permission` - permission
26/// * `pd_permission` - marker for permission type args, needed to avoid warnings of unused `P1Args`.
27/// * `ready` - flag that tells if future in `call` is completed or pending.
28pub struct PermissionService<F, Args, P1, P1Args>
29where
30    F: Handler<Args>,
31    Args: FromRequest,
32    F::Output: Responder,
33    P1: Permission<P1Args>,
34    P1Args: FromRequest,
35{
36    handler: F,
37    pd_handler: PhantomData<Args>,
38    deny_handler: fn(HttpRequest) -> HttpResponse,
39    permission: P1,
40    pd_permission: PhantomData<P1Args>,
41    ready: Arc<AtomicBool>,
42}
43
44impl<F, Args, P1, P1Args> PermissionService<F, Args, P1, P1Args>
45where
46    F: Handler<Args>,
47    Args: FromRequest,
48    P1: Permission<P1Args>,
49    P1Args: FromRequest,
50    F::Output: Responder,
51{
52    /// Creates new `PermissionService`.
53    pub fn new(permission: P1, handler: F, deny_handler: fn(HttpRequest) -> HttpResponse) -> Self {
54        Self {
55            handler,
56            pd_handler: Default::default(),
57            deny_handler,
58            permission,
59            pd_permission: Default::default(),
60            ready: Arc::new(AtomicBool::new(false)),
61        }
62    }
63}
64
65/// Converts bytes to payload stream
66pub fn get_payload(bytes: Bytes) -> Payload {
67    let mut repack_payload = actix_http::h1::Payload::create(true);
68    repack_payload.1.unread_data(bytes);
69    repack_payload.1.into()
70}
71
72impl<F, Args, P1, P1Args> Service<ServiceRequest> for PermissionService<F, Args, P1, P1Args>
73where
74    F: Handler<Args>,
75    Args: FromRequest,
76    P1: Permission<P1Args>,
77    P1Args: FromRequest,
78    F::Output: Responder,
79{
80    type Response = ServiceResponse;
81    type Error = Infallible;
82    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
83
84    fn poll_ready(&self, _: &mut Context<'_>) -> Poll<std::result::Result<(), Self::Error>> {
85        if self.ready.load(Ordering::Relaxed) {
86            Poll::Ready(Ok(()))
87        } else {
88            Poll::Pending
89        }
90    }
91
92    fn call(&self, args: ServiceRequest) -> Self::Future {
93        let handler = self.handler.clone();
94        let deny_handler = self.deny_handler;
95        let permission = self.permission.clone();
96        let ready = self.ready.clone();
97        Box::pin(async move {
98            let (req, mut payload) = args.into_parts();
99
100            // reading payload into BytesMut, so `clone` can be performed.
101            // clone is necessary because `P1Args` and `Args` both consume `Payload`.
102            let mut body = BytesMut::new();
103            while let Some(chunk) = payload.next().await {
104                body.extend_from_slice(chunk.unwrap().chunk())
105            }
106
107            let handler_body = body.clone();
108
109            let mut p1_payload = get_payload(body.freeze());
110
111            let service_response = match P1Args::from_request(&req, &mut p1_payload).await {
112                Err(err) => ServiceResponse::new(req, HttpResponse::from_error(err)),
113                Ok(data) => {
114                    let permission_check_result = permission.call(req.clone(), data).await;
115                    let mut handler_payload = get_payload(handler_body.freeze());
116                    match permission_check_result {
117                        Ok(true) => match Args::from_request(&req, &mut handler_payload).await {
118                            Err(err) => ServiceResponse::new(req, HttpResponse::from_error(err)),
119                            Ok(data) => {
120                                let handler_response = handler
121                                    .call(data)
122                                    .await
123                                    .respond_to(&req)
124                                    .map_into_boxed_body();
125                                ServiceResponse::new(req, handler_response)
126                            }
127                        },
128                        Ok(false) => {
129                            let response = deny_handler(req.clone());
130                            ServiceResponse::new(req, response)
131                        }
132                        Err(err) => ServiceResponse::from_err(err, req),
133                    }
134                }
135            };
136
137            ready.store(true, Ordering::Relaxed);
138            Ok(service_response)
139        })
140    }
141}