actix / actix-extras
1
use std::{error::Error as StdError, rc::Rc};
2

3
use actix_web::{
4
    body::{AnyBody, MessageBody},
5
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
6
    Error, HttpMessage, Result,
7
};
8
use futures_util::future::{
9
    ready, FutureExt as _, LocalBoxFuture, Ready, TryFutureExt as _,
10
};
11

12
use crate::{identity::IdentityItem, IdentityPolicy};
13

14
/// Request identity middleware
15
///
16
/// ```
17
/// use actix_web::App;
18
/// use actix_identity::{CookieIdentityPolicy, IdentityService};
19
///
20
/// // create cookie identity backend
21
/// let policy = CookieIdentityPolicy::new(&[0; 32])
22
///            .name("auth-cookie")
23
///            .secure(false);
24
///
25
/// let app = App::new()
26
///     // wrap policy into identity middleware
27
///     .wrap(IdentityService::new(policy));
28
/// ```
29
pub struct IdentityService<T> {
30
    backend: Rc<T>,
31
}
32

33
impl<T> IdentityService<T> {
34
    /// Create new identity service with specified backend.
35 1
    pub fn new(backend: T) -> Self {
36
        IdentityService {
37 1
            backend: Rc::new(backend),
38
        }
39
    }
40
}
41

42
impl<S, T, B> Transform<S, ServiceRequest> for IdentityService<T>
43
where
44
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
45
    S::Future: 'static,
46
    T: IdentityPolicy,
47
    B: MessageBody + 'static,
48
    B::Error: StdError,
49
{
50
    type Response = ServiceResponse;
51
    type Error = Error;
52
    type InitError = ();
53
    type Transform = IdentityServiceMiddleware<S, T>;
54
    type Future = Ready<Result<Self::Transform, Self::InitError>>;
55

56 1
    fn new_transform(&self, service: S) -> Self::Future {
57 1
        ready(Ok(IdentityServiceMiddleware {
58 1
            backend: self.backend.clone(),
59 1
            service: Rc::new(service),
60
        }))
61
    }
62
}
63

64
pub struct IdentityServiceMiddleware<S, T> {
65
    pub(crate) service: Rc<S>,
66
    pub(crate) backend: Rc<T>,
67
}
68

69
impl<S, T> Clone for IdentityServiceMiddleware<S, T> {
70 1
    fn clone(&self) -> Self {
71
        Self {
72 1
            backend: Rc::clone(&self.backend),
73 1
            service: Rc::clone(&self.service),
74
        }
75
    }
76
}
77

78
impl<S, T, B> Service<ServiceRequest> for IdentityServiceMiddleware<S, T>
79
where
80
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
81
    S::Future: 'static,
82
    T: IdentityPolicy,
83
    B: MessageBody + 'static,
84
    B::Error: StdError,
85
{
86
    type Response = ServiceResponse;
87
    type Error = Error;
88
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
89

90
    actix_service::forward_ready!(service);
91

92 1
    fn call(&self, mut req: ServiceRequest) -> Self::Future {
93 1
        let srv = Rc::clone(&self.service);
94 1
        let backend = Rc::clone(&self.backend);
95 1
        let fut = self.backend.from_request(&mut req);
96

97 1
        async move {
98 1
            match fut.await {
99 1
                Ok(id) => {
100 1
                    req.extensions_mut()
101 1
                        .insert(IdentityItem { id, changed: false });
102

103 1
                    let mut res = srv.call(req).await?;
104 1
                    let id = res.request().extensions_mut().remove::<IdentityItem>();
105

106 1
                    if let Some(id) = id {
107 1
                        match backend.to_response(id.id, id.changed, &mut res).await {
108 1
                            Ok(_) => {
109 1
                                Ok(res.map_body(|_, body| AnyBody::from_message(body)))
110
                            }
111 0
                            Err(e) => Ok(res.error_response(e)),
112
                        }
113
                    } else {
114 0
                        Ok(res.map_body(|_, body| AnyBody::from_message(body)))
115
                    }
116
                }
117 0
                Err(err) => Ok(req.error_response(err)),
118
            }
119
        }
120 1
        .map_ok(|res| res.map_body(|_, body| AnyBody::from_message(body)))
121
        .boxed_local()
122
    }
123
}
124

125
#[cfg(test)]
126
mod tests {
127
    use std::{rc::Rc, time::Duration};
128

129
    use actix_service::into_service;
130
    use actix_web::{dev, error, test, Error, Result};
131

132
    use super::*;
133

134 1
    #[actix_rt::test]
135 1
    async fn test_borrowed_mut_error() {
136
        use futures_util::future::{lazy, ok, Ready};
137

138
        struct Ident;
139
        impl IdentityPolicy for Ident {
140
            type Future = Ready<Result<Option<String>, Error>>;
141
            type ResponseFuture = Ready<Result<(), Error>>;
142

143 1
            fn from_request(&self, _: &mut dev::ServiceRequest) -> Self::Future {
144 1
                ok(Some("test".to_string()))
145
            }
146

147 0
            fn to_response<B>(
148
                &self,
149
                _: Option<String>,
150
                _: bool,
151
                _: &mut dev::ServiceResponse<B>,
152
            ) -> Self::ResponseFuture {
153 0
                ok(())
154
            }
155
        }
156

157
        let srv = crate::middleware::IdentityServiceMiddleware {
158 1
            backend: Rc::new(Ident),
159 1
            service: Rc::new(into_service(|_: dev::ServiceRequest| async move {
160
                actix_rt::time::sleep(Duration::from_secs(100)).await;
161
                Err::<dev::ServiceResponse, _>(error::ErrorBadRequest("error"))
162
            })),
163
        };
164

165 1
        let srv2 = srv.clone();
166 1
        let req = test::TestRequest::default().to_srv_request();
167

168 1
        actix_rt::spawn(async move {
169 1
            let _ = srv2.call(req).await;
170
        });
171

172 1
        actix_rt::time::sleep(Duration::from_millis(50)).await;
173

174 1
        let _ = lazy(|cx| srv.poll_ready(cx)).await;
175
    }
176
}

Read our documentation on viewing source code .

Loading