1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use crate::permissions::AttachPermissions;
use crate::permissions::PermissionsExtractor;
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::Error;
use std::future::{self, Future, Ready};
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};

/// Built-in middleware for extracting user permission.
///
///
/// # Examples
/// ```
/// use actix_web::dev::ServiceRequest;
/// use actix_web::{get, App, Error, HttpResponse, HttpServer, Responder};
///
/// use actix_web_grants::permissions::{AuthDetails, PermissionsCheck};
/// use actix_web_grants::{proc_macro::has_permissions, GrantsMiddleware};
///
/// fn main() {
///     HttpServer::new(|| {
///         let auth = GrantsMiddleware::with_extractor(extract);
///         App::new()
///             .wrap(auth)
///             .service(you_service)
///     });
/// }
///
/// // You can use both &ServiceRequest and &mut ServiceRequest
/// // Futhermore, you can use you own type instead of `String` (e.g. Enum).
/// async fn extract(_req: &ServiceRequest) -> Result<Vec<String>, Error> {
///    // Here is a place for your code to get user permissions/grants/permissions from a request
///    // For example from a token or database
///
///    // Stub example
///    Ok(vec!["ROLE_ADMIN".to_string()])
/// }
///
/// // `has_permissions` is one of options to validate permissions.
/// // `proc-macro` crate has additional features, like ABAC security and custom types. See examples and `proc-macro` crate docs.
/// #[get("/admin")]
/// #[has_permissions("ROLE_ADMIN")]
/// async fn you_service() -> impl Responder {
///     HttpResponse::Ok().finish()
/// }
/// ```
pub struct GrantsMiddleware<E, Req, Type>
where
    for<'a> E: PermissionsExtractor<'a, Req, Type>,
    Type: PartialEq + Clone + 'static,
{
    extractor: Rc<E>,
    phantom_req: PhantomData<Req>,
    phantom_type: PhantomData<Type>,
}

impl<E, Req, Type> GrantsMiddleware<E, Req, Type>
where
    for<'a> E: PermissionsExtractor<'a, Req, Type>,
    Type: PartialEq + Clone + 'static,
{
    /// Create middleware by [`PermissionsExtractor`].
    ///
    /// You can use a built-in implementation for `async fn` with a suitable signature (see example below).
    /// Or you can define your own implementation of trait.
    ///
    /// # Example of function with implementation of [`PermissionsExtractor`]
    /// ```
    /// use actix_web::dev::ServiceRequest;
    /// use actix_web::Error;
    ///
    /// async fn extract(_req: &ServiceRequest) -> Result<Vec<String>, Error> {
    ///     // Here is a place for your code to get user permissions/grants/permissions from a request
    ///      // For example from a token or database
    ///     Ok(vec!["WRITE_ACCESS".to_string()])
    /// }
    ///
    /// // Or with you own type:
    /// #[derive(PartialEq, Clone)] // required bounds
    /// enum Permission { WRITE, READ }
    /// async fn extract_enum(_req: &ServiceRequest) -> Result<Vec<Permission>, Error> {
    ///     // Here is a place for your code to get user permissions/grants/permissions from a request
    ///      // For example from a token, database or external service
    ///     Ok(vec![Permission::WRITE])
    /// }
    /// ```
    ///
    ///[`PermissionsExtractor`]: crate::permissions::PermissionsExtractor
    pub fn with_extractor(extractor: E) -> GrantsMiddleware<E, Req, Type> {
        GrantsMiddleware {
            extractor: Rc::new(extractor),
            phantom_req: PhantomData,
            phantom_type: PhantomData,
        }
    }
}

impl<S, B, E, Req, Type> Transform<S, ServiceRequest> for GrantsMiddleware<E, Req, Type>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    for<'a> E: PermissionsExtractor<'a, Req, Type> + 'static,
    Type: PartialEq + Clone + 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = GrantsService<S, E, Req, Type>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        future::ready(Ok(GrantsService {
            service: Rc::new(service),
            extractor: self.extractor.clone(),
            phantom_req: PhantomData,
            phantom_type: PhantomData,
        }))
    }
}

pub struct GrantsService<S, E, Req, Type>
where
    for<'a> E: PermissionsExtractor<'a, Req, Type> + 'static,
{
    service: Rc<S>,
    extractor: Rc<E>,
    phantom_req: PhantomData<Req>,
    phantom_type: PhantomData<Type>,
}

impl<S, B, E, Req, Type> Service<ServiceRequest> for GrantsService<S, E, Req, Type>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    for<'a> E: PermissionsExtractor<'a, Req, Type>,
    Type: PartialEq + Clone + 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Error>>>>;

    fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let service = Rc::clone(&self.service);
        let extractor = Rc::clone(&self.extractor);

        Box::pin(async move {
            let permissions: Vec<Type> = extractor.extract(&mut req).await?;
            req.attach(permissions);

            service.call(req).await
        })
    }
}