Skip to main content

actix_security_core/http/security/
user.rs

1//! User model for authentication and authorization.
2//!
3//! # Spring Equivalent
4//! `UserDetails` interface
5
6use std::fmt;
7
8/// Represents an authenticated user with roles and authorities.
9///
10/// # Spring Equivalent
11/// `UserDetails` / `User`
12///
13/// # Example
14/// ```
15/// use actix_security_core::http::security::User;
16///
17/// let user = User::new("admin".into(), "password".into())
18///     .roles(&["ADMIN".into(), "USER".into()])
19///     .authorities(&["users:read".into(), "users:write".into()]);
20///
21/// assert!(user.has_role("ADMIN"));
22/// assert!(user.has_authority("users:read"));
23/// ```
24#[derive(Clone, Debug)]
25pub struct User {
26    username: String,
27    password: String,
28    roles: Vec<String>,
29    authorities: Vec<String>,
30}
31
32impl User {
33    /// Creates a new user with username and plain-text password.
34    ///
35    /// # Note
36    /// For production use, prefer `with_encoded_password` with a proper
37    /// password encoder like Argon2.
38    pub fn new(username: String, password: String) -> Self {
39        User {
40            username,
41            password,
42            roles: Vec::new(),
43            authorities: Vec::new(),
44        }
45    }
46
47    /// Creates a new user with username and pre-encoded password.
48    ///
49    /// # Spring Security Equivalent
50    /// `User.withUsername().password("{bcrypt}$2a$...").build()`
51    ///
52    /// # Example
53    /// ```
54    /// use actix_security_core::http::security::{User, Argon2PasswordEncoder, PasswordEncoder};
55    ///
56    /// let encoder = Argon2PasswordEncoder::new();
57    /// let encoded = encoder.encode("secret");
58    ///
59    /// let user = User::with_encoded_password("admin", encoded)
60    ///     .roles(&["ADMIN".into()]);
61    /// ```
62    pub fn with_encoded_password(username: &str, encoded_password: String) -> Self {
63        User {
64            username: username.to_string(),
65            password: encoded_password,
66            roles: Vec::new(),
67            authorities: Vec::new(),
68        }
69    }
70
71    /// Returns the username.
72    pub fn get_username(&self) -> &str {
73        &self.username
74    }
75
76    /// Returns the password (for authentication checks).
77    pub fn get_password(&self) -> &str {
78        &self.password
79    }
80
81    /// Returns the user's roles.
82    pub fn get_roles(&self) -> &[String] {
83        &self.roles
84    }
85
86    /// Returns the user's authorities.
87    pub fn get_authorities(&self) -> &[String] {
88        &self.authorities
89    }
90
91    /// Adds roles to the user (builder pattern).
92    pub fn roles(mut self, roles: &[String]) -> Self {
93        for role in roles {
94            if !self.roles.contains(role) {
95                self.roles.push(role.clone());
96            }
97        }
98        self
99    }
100
101    /// Adds authorities to the user (builder pattern).
102    pub fn authorities(mut self, authorities: &[String]) -> Self {
103        for authority in authorities {
104            if !self.authorities.contains(authority) {
105                self.authorities.push(authority.clone());
106            }
107        }
108        self
109    }
110
111    /// Checks if the user has a specific role.
112    pub fn has_role(&self, role: &str) -> bool {
113        self.roles.iter().any(|r| r == role)
114    }
115
116    /// Checks if the user has ANY of the specified roles (OR logic).
117    pub fn has_any_role(&self, roles: &[&str]) -> bool {
118        roles.iter().any(|role| self.has_role(role))
119    }
120
121    /// Checks if the user has ALL of the specified roles (AND logic).
122    pub fn has_all_roles(&self, roles: &[&str]) -> bool {
123        roles.iter().all(|role| self.has_role(role))
124    }
125
126    /// Checks if the user has a specific authority.
127    pub fn has_authority(&self, authority: &str) -> bool {
128        self.authorities.iter().any(|a| a == authority)
129    }
130
131    /// Checks if the user has ANY of the specified authorities (OR logic).
132    pub fn has_any_authority(&self, authorities: &[&str]) -> bool {
133        authorities.iter().any(|auth| self.has_authority(auth))
134    }
135
136    /// Checks if the user has ALL of the specified authorities (AND logic).
137    pub fn has_all_authorities(&self, authorities: &[&str]) -> bool {
138        authorities.iter().all(|auth| self.has_authority(auth))
139    }
140
141    // Legacy methods for backward compatibility with Vec<String> parameters
142
143    /// Checks if the user has ANY of the specified roles (legacy).
144    pub fn has_roles(&self, roles: &[String]) -> bool {
145        roles.iter().any(|role| self.roles.contains(role))
146    }
147
148    /// Checks if the user has ANY of the specified authorities (legacy).
149    #[doc(hidden)]
150    pub fn has_authorities(&self, authorities: &[String]) -> bool {
151        authorities
152            .iter()
153            .any(|auth| self.authorities.contains(auth))
154    }
155}
156
157impl fmt::Display for User {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        write!(
160            f,
161            "User {{ username: {}, roles: {:?}, authorities: {:?} }}",
162            self.username, self.roles, self.authorities
163        )
164    }
165}