actix_web_grants/permissions/
mod.rs

1//! A set of traits and structures for custom integration.
2//!
3//! Via [`PermissionsExtractor`] implementations, the library gets a user permissions from you.
4//! The default implementation of the [`PermissionsExtractor`] is provided via a function.
5//!
6//! See [`GrantsMiddleware`] for more details.
7//!
8//! ## If you already have middleware authorization
9//! You can integrate it with this library using [`AttachPermissions`]
10//!
11//!
12//! [`PermissionsExtractor`]: PermissionsExtractor
13//! [`AttachPermissions`]: AttachPermissions
14//! [`GrantsMiddleware`]: actix_web_grants::GrantsMiddleware;
15
16use actix_web::dev::Payload;
17use actix_web::error::ErrorUnauthorized;
18use actix_web::{Error, FromRequest, HttpMessage, HttpRequest};
19use std::future::Future;
20use std::pin::Pin;
21
22mod attache;
23mod extractors;
24
25pub use attache::AttachPermissions;
26pub use extractors::*;
27
28#[derive(Clone)]
29pub struct AuthDetails<T = String>
30where
31    T: PartialEq,
32{
33    pub permissions: Vec<T>,
34}
35
36impl<T> AuthDetails<T>
37where
38    T: PartialEq + Clone,
39{
40    pub fn new(permissions: Vec<T>) -> AuthDetails<T> {
41        AuthDetails { permissions }
42    }
43}
44
45pub trait PermissionsCheck<T: PartialEq> {
46    fn has_permission(&self, permission: T) -> bool;
47    fn has_permissions(&self, permissions: &[T]) -> bool;
48    fn has_any_permission(&self, permissions: &[T]) -> bool;
49}
50
51impl<T: PartialEq + Clone> PermissionsCheck<&T> for AuthDetails<T> {
52    fn has_permission(&self, permission: &T) -> bool {
53        self.permissions.iter().any(|auth| auth == permission)
54    }
55
56    fn has_permissions(&self, permissions: &[&T]) -> bool {
57        permissions.iter().all(|auth| self.has_permission(auth))
58    }
59
60    fn has_any_permission(&self, permissions: &[&T]) -> bool {
61        permissions.iter().any(|auth| self.has_permission(auth))
62    }
63}
64
65impl PermissionsCheck<&str> for AuthDetails {
66    fn has_permission(&self, permission: &str) -> bool {
67        self.permissions
68            .iter()
69            .any(|auth| auth.as_str() == permission)
70    }
71
72    fn has_permissions(&self, permissions: &[&str]) -> bool {
73        permissions.iter().all(|auth| self.has_permission(*auth))
74    }
75
76    fn has_any_permission(&self, permissions: &[&str]) -> bool {
77        permissions.iter().any(|auth| self.has_permission(*auth))
78    }
79}
80
81pub trait RolesCheck<T> {
82    fn has_role(&self, permission: T) -> bool;
83    fn has_roles(&self, permissions: &[T]) -> bool;
84    fn has_any_role(&self, permissions: &[T]) -> bool;
85}
86
87pub(crate) const ROLE_PREFIX: &str = "ROLE_";
88
89impl RolesCheck<&str> for AuthDetails {
90    fn has_role(&self, permission: &str) -> bool {
91        let permission = format!("{}{}", ROLE_PREFIX, permission);
92
93        self.permissions.iter().any(|auth| auth == &permission)
94    }
95
96    fn has_roles(&self, permissions: &[&str]) -> bool {
97        permissions.iter().all(|auth| self.has_role(*auth))
98    }
99
100    fn has_any_role(&self, permissions: &[&str]) -> bool {
101        permissions.iter().any(|auth| self.has_role(*auth))
102    }
103}
104
105impl<T: PartialEq + Clone> RolesCheck<&T> for AuthDetails<T> {
106    fn has_role(&self, permission: &T) -> bool {
107        self.permissions.iter().any(|auth| auth == permission)
108    }
109
110    fn has_roles(&self, permissions: &[&T]) -> bool {
111        permissions.iter().all(|auth| self.has_role(auth))
112    }
113
114    fn has_any_role(&self, permissions: &[&T]) -> bool {
115        permissions.iter().any(|auth| self.has_role(auth))
116    }
117}
118
119impl<T: PartialEq + Clone + 'static> FromRequest for AuthDetails<T> {
120    type Error = Error;
121    type Future = Pin<Box<dyn Future<Output = Result<Self, Error>>>>;
122
123    fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
124        let req = req.clone();
125
126        Box::pin(async move {
127            req.extensions()
128                .get::<AuthDetails<T>>()
129                .map(AuthDetails::clone)
130                .ok_or_else(|| ErrorUnauthorized("User unauthorized!"))
131        })
132    }
133}