Skip to main content

rust_serv/basic_auth/
validator.rs

1//! Authentication validator
2
3use std::collections::HashMap;
4
5use super::credentials::Credentials;
6
7/// Authentication validator
8#[derive(Debug, Clone)]
9pub struct AuthValidator {
10    /// User database
11    users: HashMap<String, String>,
12    /// Protected paths
13    protected_paths: Vec<String>,
14}
15
16impl AuthValidator {
17    /// Create a new auth validator
18    pub fn new() -> Self {
19        Self {
20            users: HashMap::new(),
21            protected_paths: Vec::new(),
22        }
23    }
24
25    /// Add a user
26    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    /// Add a protected path
31    pub fn add_protected_path(&mut self, path: impl Into<String>) {
32        self.protected_paths.push(path.into());
33    }
34
35    /// Check if path requires authentication
36    pub fn requires_auth(&self, path: &str) -> bool {
37        self.protected_paths.iter().any(|p| path.starts_with(p))
38    }
39
40    /// Validate credentials
41    pub fn validate(&self, credentials: &Credentials) -> bool {
42        if let Some(stored_password) = self.users.get(&credentials.username) {
43            // Simple password comparison (in production, use secure comparison)
44            stored_password == &credentials.password
45        } else {
46            false
47        }
48    }
49
50    /// Get user count
51    pub fn user_count(&self) -> usize {
52        self.users.len()
53    }
54
55    /// Get protected paths count
56    pub fn protected_path_count(&self) -> usize {
57        self.protected_paths.len()
58    }
59
60    /// Clear all users
61    pub fn clear_users(&mut self) {
62        self.users.clear();
63    }
64
65    /// Clear all protected paths
66    pub fn clear_paths(&mut self) {
67        self.protected_paths.clear();
68    }
69
70    /// Check if user exists
71    pub fn has_user(&self, username: &str) -> bool {
72        self.users.contains_key(username)
73    }
74
75    /// Remove a user
76    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}