actix_web_grants/permissions/
mod.rs1use 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}