Skip to main content

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}