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}