1use crate::{AuthError, AuthResult};
4use async_trait::async_trait;
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[async_trait]
11pub trait Authenticatable: Send + Sync + Clone {
12 type Id: Clone + Send + Sync + std::fmt::Debug + PartialEq;
13 type Credentials: Send + Sync;
14
15 fn id(&self) -> &Self::Id;
17
18 fn username(&self) -> &str;
20
21 fn is_active(&self) -> bool {
23 true
24 }
25
26 fn is_locked(&self) -> bool {
28 false
29 }
30
31 fn roles(&self) -> Vec<String> {
33 vec![]
34 }
35
36 fn permissions(&self) -> Vec<String> {
38 vec![]
39 }
40
41 async fn verify_credentials(&self, credentials: &Self::Credentials) -> AuthResult<bool>;
43
44 fn additional_data(&self) -> HashMap<String, serde_json::Value> {
46 HashMap::new()
47 }
48}
49
50#[async_trait]
52pub trait AuthProvider<User>: Send + Sync
53where
54 User: Authenticatable,
55{
56 type Token: Clone + Send + Sync + std::fmt::Debug;
57 type Credentials: Send + Sync;
58
59 async fn authenticate(
61 &self,
62 credentials: &Self::Credentials,
63 ) -> AuthResult<AuthenticationResult<User, Self::Token>>;
64
65 async fn validate_token(&self, token: &Self::Token) -> AuthResult<User>;
67
68 async fn refresh_token(&self, _token: &Self::Token) -> AuthResult<Self::Token> {
70 Err(AuthError::token_error("Token refresh not supported"))
71 }
72
73 async fn revoke_token(&self, _token: &Self::Token) -> AuthResult<()> {
75 Ok(()) }
77
78 fn provider_name(&self) -> &str;
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct AuthenticationResult<User, Token> {
85 pub user: User,
87
88 pub token: Token,
90
91 pub refresh_token: Option<Token>,
93
94 pub requires_mfa: bool,
96
97 pub mfa_setup: Option<MfaSetup>,
99
100 pub expires_at: Option<DateTime<Utc>>,
102
103 pub metadata: HashMap<String, serde_json::Value>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct MfaSetup {
110 pub secret: String,
112
113 pub qr_code_url: String,
115
116 pub backup_codes: Vec<String>,
118}
119
120#[async_trait]
122pub trait AuthorizationProvider: Send + Sync {
123 type User: Authenticatable;
124 type Role: Send + Sync + Clone;
125 type Permission: Send + Sync + Clone;
126
127 async fn has_role(&self, user: &Self::User, role: &str) -> AuthResult<bool>;
129
130 async fn has_permission(&self, user: &Self::User, permission: &str) -> AuthResult<bool>;
132
133 async fn has_permission_with_context(
135 &self,
136 user: &Self::User,
137 resource: &str,
138 action: &str,
139 context: Option<&HashMap<String, serde_json::Value>>,
140 ) -> AuthResult<bool>;
141
142 async fn has_any_role(&self, user: &Self::User, roles: &[String]) -> AuthResult<bool> {
144 for role in roles {
145 if self.has_role(user, role).await? {
146 return Ok(true);
147 }
148 }
149 Ok(false)
150 }
151
152 async fn has_all_roles(&self, user: &Self::User, roles: &[String]) -> AuthResult<bool> {
154 for role in roles {
155 if !self.has_role(user, role).await? {
156 return Ok(false);
157 }
158 }
159 Ok(true)
160 }
161
162 async fn has_any_permission(
164 &self,
165 user: &Self::User,
166 permissions: &[String],
167 ) -> AuthResult<bool> {
168 for permission in permissions {
169 if self.has_permission(user, permission).await? {
170 return Ok(true);
171 }
172 }
173 Ok(false)
174 }
175
176 async fn has_all_permissions(
178 &self,
179 user: &Self::User,
180 permissions: &[String],
181 ) -> AuthResult<bool> {
182 for permission in permissions {
183 if !self.has_permission(user, permission).await? {
184 return Ok(false);
185 }
186 }
187 Ok(true)
188 }
189
190 async fn get_user_roles(&self, user: &Self::User) -> AuthResult<Vec<Self::Role>>;
192
193 async fn get_user_permissions(&self, user: &Self::User) -> AuthResult<Vec<Self::Permission>>;
195}
196
197#[async_trait]
199pub trait SessionStorage: Send + Sync {
200 type SessionId: Clone + Send + Sync + std::fmt::Debug + PartialEq;
201 type SessionData: Clone + Send + Sync;
202
203 async fn create_session(
205 &self,
206 data: Self::SessionData,
207 expires_at: DateTime<Utc>,
208 ) -> AuthResult<Self::SessionId>;
209
210 async fn get_session(&self, id: &Self::SessionId) -> AuthResult<Option<Self::SessionData>>;
212
213 async fn update_session(
215 &self,
216 id: &Self::SessionId,
217 data: Self::SessionData,
218 expires_at: DateTime<Utc>,
219 ) -> AuthResult<()>;
220
221 async fn delete_session(&self, id: &Self::SessionId) -> AuthResult<()>;
223
224 async fn cleanup_expired_sessions(&self) -> AuthResult<u64>;
226
227 async fn get_session_expiry(&self, id: &Self::SessionId) -> AuthResult<Option<DateTime<Utc>>>;
229
230 async fn extend_session(
232 &self,
233 id: &Self::SessionId,
234 expires_at: DateTime<Utc>,
235 ) -> AuthResult<()>;
236}
237
238#[async_trait]
240pub trait MfaProvider: Send + Sync {
241 type User: Authenticatable;
242 type Secret: Clone + Send + Sync;
243 type Code: Send + Sync;
244
245 async fn setup_mfa(&self, user: &Self::User) -> AuthResult<MfaSetup>;
247
248 async fn verify_code(
250 &self,
251 user: &Self::User,
252 code: &Self::Code,
253 secret: &Self::Secret,
254 ) -> AuthResult<bool>;
255
256 async fn generate_backup_codes(&self, user: &Self::User) -> AuthResult<Vec<String>>;
258
259 async fn verify_backup_code(&self, user: &Self::User, code: &str) -> AuthResult<bool>;
261
262 async fn is_mfa_enabled(&self, user: &Self::User) -> AuthResult<bool>;
264
265 async fn disable_mfa(&self, user: &Self::User) -> AuthResult<()>;
267}
268
269pub trait PasswordHasher: Send + Sync {
271 fn hash_password(&self, password: &str) -> AuthResult<String>;
273
274 fn verify_password(&self, password: &str, hash: &str) -> AuthResult<bool>;
276
277 fn hasher_name(&self) -> &str;
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct UserContext {
284 pub user_id: String,
286
287 pub username: String,
289
290 pub roles: Vec<String>,
292
293 pub permissions: Vec<String>,
295
296 pub auth_provider: String,
298
299 pub authenticated_at: DateTime<Utc>,
301
302 pub expires_at: Option<DateTime<Utc>>,
304
305 pub additional_data: HashMap<String, serde_json::Value>,
307}
308
309impl UserContext {
310 pub fn new(user_id: String, username: String, auth_provider: String) -> Self {
312 Self {
313 user_id,
314 username,
315 roles: vec![],
316 permissions: vec![],
317 auth_provider,
318 authenticated_at: Utc::now(),
319 expires_at: None,
320 additional_data: HashMap::new(),
321 }
322 }
323
324 pub fn has_role(&self, role: &str) -> bool {
326 self.roles.contains(&role.to_string())
327 }
328
329 pub fn has_permission(&self, permission: &str) -> bool {
331 self.permissions.contains(&permission.to_string())
332 }
333
334 pub fn has_any_role(&self, roles: &[String]) -> bool {
336 roles.iter().any(|role| self.has_role(role))
337 }
338
339 pub fn has_all_roles(&self, roles: &[String]) -> bool {
341 roles.iter().all(|role| self.has_role(role))
342 }
343
344 pub fn is_expired(&self) -> bool {
346 self.expires_at.is_some_and(|exp| Utc::now() > exp)
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353
354 #[test]
355 fn test_user_context_creation() {
356 let context = UserContext::new(
357 "123".to_string(),
358 "user@example.com".to_string(),
359 "jwt".to_string(),
360 );
361
362 assert_eq!(context.user_id, "123");
363 assert_eq!(context.username, "user@example.com");
364 assert_eq!(context.auth_provider, "jwt");
365 assert!(context.roles.is_empty());
366 assert!(context.permissions.is_empty());
367 }
368
369 #[test]
370 fn test_user_context_role_checking() {
371 let mut context = UserContext::new(
372 "123".to_string(),
373 "user@example.com".to_string(),
374 "jwt".to_string(),
375 );
376
377 context.roles = vec!["admin".to_string(), "editor".to_string()];
378
379 assert!(context.has_role("admin"));
380 assert!(context.has_role("editor"));
381 assert!(!context.has_role("viewer"));
382
383 assert!(context.has_any_role(&["admin".to_string(), "viewer".to_string()]));
384 assert!(!context.has_any_role(&["viewer".to_string(), "guest".to_string()]));
385
386 assert!(context.has_all_roles(&["admin".to_string(), "editor".to_string()]));
387 assert!(!context.has_all_roles(&["admin".to_string(), "viewer".to_string()]));
388 }
389
390 #[test]
391 fn test_user_context_permission_checking() {
392 let mut context = UserContext::new(
393 "123".to_string(),
394 "user@example.com".to_string(),
395 "jwt".to_string(),
396 );
397
398 context.permissions = vec!["read".to_string(), "write".to_string()];
399
400 assert!(context.has_permission("read"));
401 assert!(context.has_permission("write"));
402 assert!(!context.has_permission("delete"));
403 }
404
405 #[test]
406 fn test_user_context_expiration() {
407 let mut context = UserContext::new(
408 "123".to_string(),
409 "user@example.com".to_string(),
410 "jwt".to_string(),
411 );
412
413 assert!(!context.is_expired());
415
416 context.expires_at = Some(Utc::now() - chrono::Duration::hours(1));
418 assert!(context.is_expired());
419
420 context.expires_at = Some(Utc::now() + chrono::Duration::hours(1));
422 assert!(!context.is_expired());
423 }
424}