actix_security_core/http/security/context.rs
1//! Security Context for accessing the current authenticated user.
2//!
3//! # Spring Security Equivalent
4//! `org.springframework.security.core.context.SecurityContextHolder`
5//!
6//! # Overview
7//! The SecurityContext provides access to the current authenticated user
8//! from anywhere in your application, not just from request handlers.
9//!
10//! # Usage
11//! ```ignore
12//! use actix_security_core::http::security::context::SecurityContext;
13//!
14//! // In a service layer function
15//! fn get_current_username() -> Option<String> {
16//! SecurityContext::get_user()
17//! .map(|user| user.get_username().to_string())
18//! }
19//!
20//! // Check if current user has a role
21//! fn is_admin() -> bool {
22//! SecurityContext::get_user()
23//! .map(|user| user.has_role("ADMIN"))
24//! .unwrap_or(false)
25//! }
26//! ```
27//!
28//! # Thread Safety
29//! The context uses task-local storage, making it safe for async code.
30//! Each async task has its own isolated security context.
31
32use std::cell::RefCell;
33
34use crate::http::security::User;
35
36tokio::task_local! {
37 static SECURITY_CONTEXT: RefCell<Option<User>>;
38}
39
40/// Holder for the current security context.
41///
42/// # Spring Security Equivalent
43/// `SecurityContextHolder`
44///
45/// Provides static methods to access the current authenticated user
46/// from anywhere in the application.
47pub struct SecurityContext;
48
49impl SecurityContext {
50 /// Gets the current authenticated user.
51 ///
52 /// # Spring Security Equivalent
53 /// ```java
54 /// SecurityContextHolder.getContext().getAuthentication().getPrincipal()
55 /// ```
56 ///
57 /// # Returns
58 /// - `Some(User)` if a user is authenticated in the current context
59 /// - `None` if no user is authenticated
60 ///
61 /// # Example
62 /// ```ignore
63 /// use actix_security_core::http::security::context::SecurityContext;
64 ///
65 /// if let Some(user) = SecurityContext::get_user() {
66 /// println!("Current user: {}", user.get_username());
67 /// }
68 /// ```
69 pub fn get_user() -> Option<User> {
70 SECURITY_CONTEXT
71 .try_with(|ctx| ctx.borrow().clone())
72 .ok()
73 .flatten()
74 }
75
76 /// Gets the current username if authenticated.
77 ///
78 /// # Example
79 /// ```ignore
80 /// let username = SecurityContext::get_username();
81 /// ```
82 pub fn get_username() -> Option<String> {
83 Self::get_user().map(|u| u.get_username().to_string())
84 }
85
86 /// Checks if the current user is authenticated.
87 ///
88 /// # Spring Security Equivalent
89 /// `SecurityContextHolder.getContext().getAuthentication().isAuthenticated()`
90 pub fn is_authenticated() -> bool {
91 Self::get_user().is_some()
92 }
93
94 /// Checks if the current user has the specified role.
95 ///
96 /// # Example
97 /// ```ignore
98 /// if SecurityContext::has_role("ADMIN") {
99 /// // Admin-only logic
100 /// }
101 /// ```
102 pub fn has_role(role: &str) -> bool {
103 Self::get_user().map(|u| u.has_role(role)).unwrap_or(false)
104 }
105
106 /// Checks if the current user has any of the specified roles.
107 pub fn has_any_role(roles: &[&str]) -> bool {
108 Self::get_user()
109 .map(|u| u.has_any_role(roles))
110 .unwrap_or(false)
111 }
112
113 /// Checks if the current user has the specified authority.
114 ///
115 /// # Example
116 /// ```ignore
117 /// if SecurityContext::has_authority("users:write") {
118 /// // Write access logic
119 /// }
120 /// ```
121 pub fn has_authority(authority: &str) -> bool {
122 Self::get_user()
123 .map(|u| u.has_authority(authority))
124 .unwrap_or(false)
125 }
126
127 /// Checks if the current user has any of the specified authorities.
128 pub fn has_any_authority(authorities: &[&str]) -> bool {
129 Self::get_user()
130 .map(|u| u.has_any_authority(authorities))
131 .unwrap_or(false)
132 }
133
134 /// Runs a closure with the given user set in the security context.
135 ///
136 /// # Spring Security Equivalent
137 /// `SecurityContextHolder.setContext(context)`
138 ///
139 /// This is primarily used internally by the security middleware.
140 ///
141 /// # Example
142 /// ```ignore
143 /// let user = User::new("admin".into(), "password".into());
144 ///
145 /// SecurityContext::run_with(Some(user), async {
146 /// // Code here can access SecurityContext::get_user()
147 /// let username = SecurityContext::get_username();
148 /// }).await;
149 /// ```
150 pub async fn run_with<F, R>(user: Option<User>, f: F) -> R
151 where
152 F: std::future::Future<Output = R>,
153 {
154 SECURITY_CONTEXT.scope(RefCell::new(user), f).await
155 }
156
157 /// Sets the user in the current security context.
158 ///
159 /// # Warning
160 /// This should only be called from within a `run_with` scope.
161 /// Calling it outside a scope will have no effect.
162 pub fn set_user(user: Option<User>) {
163 let _ = SECURITY_CONTEXT.try_with(|ctx| {
164 *ctx.borrow_mut() = user;
165 });
166 }
167
168 /// Clears the security context.
169 ///
170 /// # Spring Security Equivalent
171 /// `SecurityContextHolder.clearContext()`
172 pub fn clear() {
173 Self::set_user(None);
174 }
175}
176
177/// Guard that clears the security context when dropped.
178///
179/// Useful for ensuring the context is cleared even if a panic occurs.
180pub struct SecurityContextGuard;
181
182impl Drop for SecurityContextGuard {
183 fn drop(&mut self) {
184 SecurityContext::clear();
185 }
186}