Skip to main content

actix_security_core/http/security/
extractor.rs

1//! Extractors for accessing security context in handlers.
2//!
3//! # Spring Equivalent
4//! `@AuthenticationPrincipal` annotation / `SecurityContextHolder`
5
6use std::future::{ready, Ready};
7use std::ops::Deref;
8
9use actix_web::dev::Payload;
10use actix_web::{FromRequest, HttpMessage, HttpRequest};
11
12use crate::http::error::AuthError;
13use crate::http::security::User;
14
15/// Extractor for the authenticated user.
16///
17/// # Spring Equivalent
18/// `@AuthenticationPrincipal User user` parameter or `SecurityContextHolder.getContext().getAuthentication().getPrincipal()`
19///
20/// # Usage
21/// ```ignore
22/// use actix_security_core::http::security::AuthenticatedUser;
23///
24/// async fn handler(user: AuthenticatedUser) -> impl Responder {
25///     format!("Hello, {}!", user.get_username())
26/// }
27/// ```
28///
29/// # Errors
30/// Returns `401 Unauthorized` if the user is not authenticated.
31#[derive(Debug, Clone)]
32pub struct AuthenticatedUser(User);
33
34impl AuthenticatedUser {
35    /// Creates a new AuthenticatedUser wrapper.
36    pub fn new(user: User) -> Self {
37        AuthenticatedUser(user)
38    }
39
40    /// Returns the inner User.
41    pub fn into_inner(self) -> User {
42        self.0
43    }
44}
45
46impl Deref for AuthenticatedUser {
47    type Target = User;
48
49    fn deref(&self) -> &Self::Target {
50        &self.0
51    }
52}
53
54impl FromRequest for AuthenticatedUser {
55    type Error = AuthError;
56    type Future = Ready<Result<Self, Self::Error>>;
57
58    fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
59        match req.extensions().get::<User>().cloned() {
60            Some(user) => ready(Ok(AuthenticatedUser(user))),
61            None => ready(Err(AuthError::Unauthorized)),
62        }
63    }
64}
65
66/// Optional extractor for the authenticated user.
67///
68/// Returns `None` if not authenticated instead of an error.
69///
70/// # Usage
71/// ```ignore
72/// use actix_security_core::http::security::OptionalUser;
73///
74/// async fn handler(user: OptionalUser) -> impl Responder {
75///     match user.into_inner() {
76///         Some(u) => format!("Hello, {}!", u.get_username()),
77///         None => "Hello, guest!".to_string(),
78///     }
79/// }
80/// ```
81#[derive(Debug, Clone)]
82pub struct OptionalUser(Option<User>);
83
84impl OptionalUser {
85    /// Returns the inner `Option<User>`.
86    pub fn into_inner(self) -> Option<User> {
87        self.0
88    }
89
90    /// Returns true if a user is present.
91    pub fn is_authenticated(&self) -> bool {
92        self.0.is_some()
93    }
94}
95
96impl Deref for OptionalUser {
97    type Target = Option<User>;
98
99    fn deref(&self) -> &Self::Target {
100        &self.0
101    }
102}
103
104impl FromRequest for OptionalUser {
105    type Error = actix_web::Error;
106    type Future = Ready<Result<Self, Self::Error>>;
107
108    fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
109        let user = req.extensions().get::<User>().cloned();
110        ready(Ok(OptionalUser(user)))
111    }
112}
113
114/// Extension trait for HttpRequest to check authentication.
115pub trait SecurityExt {
116    /// Returns a clone of the authenticated user if present.
117    fn get_user(&self) -> Option<User>;
118
119    /// Returns true if a user is authenticated.
120    fn is_authenticated(&self) -> bool;
121
122    /// Checks if the authenticated user has the specified role.
123    fn has_role(&self, role: &str) -> bool;
124
125    /// Checks if the authenticated user has any of the specified roles.
126    fn has_any_role(&self, roles: &[&str]) -> bool;
127
128    /// Checks if the authenticated user has the specified authority.
129    fn has_authority(&self, authority: &str) -> bool;
130
131    /// Checks if the authenticated user has any of the specified authorities.
132    fn has_any_authority(&self, authorities: &[&str]) -> bool;
133}
134
135impl SecurityExt for HttpRequest {
136    fn get_user(&self) -> Option<User> {
137        self.extensions().get::<User>().cloned()
138    }
139
140    fn is_authenticated(&self) -> bool {
141        self.extensions().get::<User>().is_some()
142    }
143
144    fn has_role(&self, role: &str) -> bool {
145        self.extensions()
146            .get::<User>()
147            .is_some_and(|u| u.has_role(role))
148    }
149
150    fn has_any_role(&self, roles: &[&str]) -> bool {
151        self.extensions()
152            .get::<User>()
153            .is_some_and(|u| u.has_any_role(roles))
154    }
155
156    fn has_authority(&self, authority: &str) -> bool {
157        self.extensions()
158            .get::<User>()
159            .is_some_and(|u| u.has_authority(authority))
160    }
161
162    fn has_any_authority(&self, authorities: &[&str]) -> bool {
163        self.extensions()
164            .get::<User>()
165            .is_some_and(|u| u.has_any_authority(authorities))
166    }
167}