actix_security_core/http/security/expression/root.rs
1//! Expression root trait for extensible security expressions.
2//!
3//! # Spring Security Equivalent
4//! `org.springframework.security.access.expression.SecurityExpressionRoot`
5
6use crate::http::security::User;
7
8/// Trait for evaluating security expression functions.
9///
10/// # Spring Security Equivalent
11/// `SecurityExpressionRoot` + `MethodSecurityExpressionOperations`
12///
13/// Implement this trait to add custom security expressions.
14///
15/// # Example
16/// ```ignore
17/// use actix_security_core::http::security::expression::{ExpressionRoot, DefaultExpressionRoot};
18/// use actix_security_core::http::security::User;
19///
20/// struct CustomExpressionRoot {
21/// default: DefaultExpressionRoot,
22/// allowed_departments: Vec<String>,
23/// }
24///
25/// impl ExpressionRoot for CustomExpressionRoot {
26/// fn evaluate_function(&self, name: &str, args: &[String], user: Option<&User>) -> Option<bool> {
27/// match name {
28/// "inDepartment" => {
29/// let dept = args.first()?;
30/// Some(self.allowed_departments.contains(dept))
31/// }
32/// _ => self.default.evaluate_function(name, args, user),
33/// }
34/// }
35/// }
36/// ```
37pub trait ExpressionRoot: Send + Sync {
38 /// Evaluates a function expression.
39 ///
40 /// # Arguments
41 /// * `name` - The function name (e.g., "hasRole", "hasAuthority")
42 /// * `args` - The function arguments (e.g., ["ADMIN"])
43 /// * `user` - The authenticated user, if any
44 ///
45 /// # Returns
46 /// * `Some(true)` - Function evaluated to true
47 /// * `Some(false)` - Function evaluated to false
48 /// * `None` - Unknown function (will result in an error)
49 fn evaluate_function(&self, name: &str, args: &[String], user: Option<&User>) -> Option<bool>;
50}
51
52/// Default implementation of security expression functions.
53///
54/// # Spring Security Equivalent
55/// `SecurityExpressionRoot`
56///
57/// Provides the standard Spring Security-like functions:
58/// - `hasRole(role)` - Check single role
59/// - `hasAnyRole(role1, role2, ...)` - Check any of multiple roles
60/// - `hasAuthority(authority)` - Check single authority
61/// - `hasAnyAuthority(auth1, auth2, ...)` - Check any of multiple authorities
62/// - `isAuthenticated()` - Check if authenticated
63/// - `isAnonymous()` - Check if anonymous (not authenticated)
64/// - `permitAll()` - Always true
65/// - `denyAll()` - Always false
66#[derive(Debug, Clone, Default)]
67pub struct DefaultExpressionRoot;
68
69impl DefaultExpressionRoot {
70 /// Creates a new default expression root.
71 pub fn new() -> Self {
72 DefaultExpressionRoot
73 }
74}
75
76impl ExpressionRoot for DefaultExpressionRoot {
77 fn evaluate_function(&self, name: &str, args: &[String], user: Option<&User>) -> Option<bool> {
78 match name {
79 // Role-based functions
80 "hasRole" => {
81 let role = args.first()?;
82 Some(user.is_some_and(|u| u.has_role(role)))
83 }
84 "hasAnyRole" => {
85 if args.is_empty() {
86 return Some(false);
87 }
88 Some(user.is_some_and(|u| args.iter().any(|role| u.has_role(role))))
89 }
90
91 // Authority-based functions
92 "hasAuthority" => {
93 let authority = args.first()?;
94 Some(user.is_some_and(|u| u.has_authority(authority)))
95 }
96 "hasAnyAuthority" => {
97 if args.is_empty() {
98 return Some(false);
99 }
100 Some(user.is_some_and(|u| args.iter().any(|auth| u.has_authority(auth))))
101 }
102
103 // Authentication state functions
104 "isAuthenticated" => Some(user.is_some()),
105 "isAnonymous" => Some(user.is_none()),
106
107 // Permission functions
108 "permitAll" => Some(true),
109 "denyAll" => Some(false),
110
111 // Unknown function
112 _ => None,
113 }
114 }
115}