rust_serv/basic_auth/
validator.rs1use std::collections::HashMap;
4
5use super::credentials::Credentials;
6
7#[derive(Debug, Clone)]
9pub struct AuthValidator {
10 users: HashMap<String, String>,
12 protected_paths: Vec<String>,
14}
15
16impl AuthValidator {
17 pub fn new() -> Self {
19 Self {
20 users: HashMap::new(),
21 protected_paths: Vec::new(),
22 }
23 }
24
25 pub fn add_user(&mut self, username: impl Into<String>, password: impl Into<String>) {
27 self.users.insert(username.into(), password.into());
28 }
29
30 pub fn add_protected_path(&mut self, path: impl Into<String>) {
32 self.protected_paths.push(path.into());
33 }
34
35 pub fn requires_auth(&self, path: &str) -> bool {
37 self.protected_paths.iter().any(|p| path.starts_with(p))
38 }
39
40 pub fn validate(&self, credentials: &Credentials) -> bool {
42 if let Some(stored_password) = self.users.get(&credentials.username) {
43 stored_password == &credentials.password
45 } else {
46 false
47 }
48 }
49
50 pub fn user_count(&self) -> usize {
52 self.users.len()
53 }
54
55 pub fn protected_path_count(&self) -> usize {
57 self.protected_paths.len()
58 }
59
60 pub fn clear_users(&mut self) {
62 self.users.clear();
63 }
64
65 pub fn clear_paths(&mut self) {
67 self.protected_paths.clear();
68 }
69
70 pub fn has_user(&self, username: &str) -> bool {
72 self.users.contains_key(username)
73 }
74
75 pub fn remove_user(&mut self, username: &str) -> bool {
77 self.users.remove(username).is_some()
78 }
79}
80
81impl Default for AuthValidator {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_validator_creation() {
93 let validator = AuthValidator::new();
94 assert_eq!(validator.user_count(), 0);
95 assert_eq!(validator.protected_path_count(), 0);
96 }
97
98 #[test]
99 fn test_add_user() {
100 let mut validator = AuthValidator::new();
101 validator.add_user("admin", "secret");
102
103 assert_eq!(validator.user_count(), 1);
104 assert!(validator.has_user("admin"));
105 }
106
107 #[test]
108 fn test_add_multiple_users() {
109 let mut validator = AuthValidator::new();
110 validator.add_user("user1", "pass1");
111 validator.add_user("user2", "pass2");
112 validator.add_user("user3", "pass3");
113
114 assert_eq!(validator.user_count(), 3);
115 }
116
117 #[test]
118 fn test_remove_user() {
119 let mut validator = AuthValidator::new();
120 validator.add_user("admin", "secret");
121
122 assert!(validator.remove_user("admin"));
123 assert_eq!(validator.user_count(), 0);
124 assert!(!validator.has_user("admin"));
125 }
126
127 #[test]
128 fn test_remove_nonexistent_user() {
129 let mut validator = AuthValidator::new();
130 assert!(!validator.remove_user("nonexistent"));
131 }
132
133 #[test]
134 fn test_add_protected_path() {
135 let mut validator = AuthValidator::new();
136 validator.add_protected_path("/admin");
137
138 assert_eq!(validator.protected_path_count(), 1);
139 }
140
141 #[test]
142 fn test_requires_auth_exact() {
143 let mut validator = AuthValidator::new();
144 validator.add_protected_path("/admin");
145
146 assert!(validator.requires_auth("/admin"));
147 }
148
149 #[test]
150 fn test_requires_auth_prefix() {
151 let mut validator = AuthValidator::new();
152 validator.add_protected_path("/admin");
153
154 assert!(validator.requires_auth("/admin/settings"));
155 assert!(validator.requires_auth("/admin/users/list"));
156 }
157
158 #[test]
159 fn test_requires_auth_no_match() {
160 let mut validator = AuthValidator::new();
161 validator.add_protected_path("/admin");
162
163 assert!(!validator.requires_auth("/public"));
164 assert!(!validator.requires_auth("/api/data"));
165 }
166
167 #[test]
168 fn test_requires_auth_multiple_paths() {
169 let mut validator = AuthValidator::new();
170 validator.add_protected_path("/admin");
171 validator.add_protected_path("/api/private");
172
173 assert!(validator.requires_auth("/admin"));
174 assert!(validator.requires_auth("/api/private/secret"));
175 assert!(!validator.requires_auth("/api/public"));
176 }
177
178 #[test]
179 fn test_validate_correct_credentials() {
180 let mut validator = AuthValidator::new();
181 validator.add_user("admin", "secret");
182
183 let creds = Credentials::new("admin", "secret");
184 assert!(validator.validate(&creds));
185 }
186
187 #[test]
188 fn test_validate_wrong_password() {
189 let mut validator = AuthValidator::new();
190 validator.add_user("admin", "secret");
191
192 let creds = Credentials::new("admin", "wrong");
193 assert!(!validator.validate(&creds));
194 }
195
196 #[test]
197 fn test_validate_nonexistent_user() {
198 let validator = AuthValidator::new();
199
200 let creds = Credentials::new("hacker", "password");
201 assert!(!validator.validate(&creds));
202 }
203
204 #[test]
205 fn test_validate_empty_password() {
206 let mut validator = AuthValidator::new();
207 validator.add_user("guest", "");
208
209 let creds = Credentials::new("guest", "");
210 assert!(validator.validate(&creds));
211 }
212
213 #[test]
214 fn test_clear_users() {
215 let mut validator = AuthValidator::new();
216 validator.add_user("user1", "pass1");
217 validator.add_user("user2", "pass2");
218
219 validator.clear_users();
220 assert_eq!(validator.user_count(), 0);
221 }
222
223 #[test]
224 fn test_clear_paths() {
225 let mut validator = AuthValidator::new();
226 validator.add_protected_path("/admin");
227 validator.add_protected_path("/api");
228
229 validator.clear_paths();
230 assert_eq!(validator.protected_path_count(), 0);
231 }
232
233 #[test]
234 fn test_default() {
235 let validator = AuthValidator::default();
236 assert_eq!(validator.user_count(), 0);
237 }
238
239 #[test]
240 fn test_overwrite_user() {
241 let mut validator = AuthValidator::new();
242 validator.add_user("admin", "old_pass");
243 validator.add_user("admin", "new_pass");
244
245 assert_eq!(validator.user_count(), 1);
246
247 let old_creds = Credentials::new("admin", "old_pass");
248 let new_creds = Credentials::new("admin", "new_pass");
249
250 assert!(!validator.validate(&old_creds));
251 assert!(validator.validate(&new_creds));
252 }
253}