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()
104 .map(|u| u.has_role(role))
105 .unwrap_or(false)
106 }
107
108 /// Checks if the current user has any of the specified roles.
109 pub fn has_any_role(roles: &[&str]) -> bool {
110 Self::get_user()
111 .map(|u| u.has_any_role(roles))
112 .unwrap_or(false)
113 }
114
115 /// Checks if the current user has the specified authority.
116 ///
117 /// # Example
118 /// ```ignore
119 /// if SecurityContext::has_authority("users:write") {
120 /// // Write access logic
121 /// }
122 /// ```
123 pub fn has_authority(authority: &str) -> bool {
124 Self::get_user()
125 .map(|u| u.has_authority(authority))
126 .unwrap_or(false)
127 }
128
129 /// Checks if the current user has any of the specified authorities.
130 pub fn has_any_authority(authorities: &[&str]) -> bool {
131 Self::get_user()
132 .map(|u| u.has_any_authority(authorities))
133 .unwrap_or(false)
134 }
135
136 /// Runs a closure with the given user set in the security context.
137 ///
138 /// # Spring Security Equivalent
139 /// `SecurityContextHolder.setContext(context)`
140 ///
141 /// This is primarily used internally by the security middleware.
142 ///
143 /// # Example
144 /// ```ignore
145 /// let user = User::new("admin".into(), "password".into());
146 ///
147 /// SecurityContext::run_with(Some(user), async {
148 /// // Code here can access SecurityContext::get_user()
149 /// let username = SecurityContext::get_username();
150 /// }).await;
151 /// ```
152 pub async fn run_with<F, R>(user: Option<User>, f: F) -> R
153 where
154 F: std::future::Future<Output = R>,
155 {
156 SECURITY_CONTEXT
157 .scope(RefCell::new(user), f)
158 .await
159 }
160
161 /// Sets the user in the current security context.
162 ///
163 /// # Warning
164 /// This should only be called from within a `run_with` scope.
165 /// Calling it outside a scope will have no effect.
166 pub fn set_user(user: Option<User>) {
167 let _ = SECURITY_CONTEXT.try_with(|ctx| {
168 *ctx.borrow_mut() = user;
169 });
170 }
171
172 /// Clears the security context.
173 ///
174 /// # Spring Security Equivalent
175 /// `SecurityContextHolder.clearContext()`
176 pub fn clear() {
177 Self::set_user(None);
178 }
179}
180
181/// Guard that clears the security context when dropped.
182///
183/// Useful for ensuring the context is cleared even if a panic occurs.
184pub struct SecurityContextGuard;
185
186impl Drop for SecurityContextGuard {
187 fn drop(&mut self) {
188 SecurityContext::clear();
189 }
190}