lmrc_auth/
models.rs

1//! Authentication data models
2//!
3//! Core data structures for authentication and session management.
4
5use serde::{Deserialize, Serialize};
6
7/// User information returned after successful authentication
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct AuthUser {
10    /// User ID
11    pub id: i64,
12    /// User email
13    pub email: String,
14    /// User role (e.g., "admin", "user")
15    pub role: String,
16}
17
18impl AuthUser {
19    /// Create a new authenticated user
20    pub fn new(id: i64, email: impl Into<String>, role: impl Into<String>) -> Self {
21        Self {
22            id,
23            email: email.into(),
24            role: role.into(),
25        }
26    }
27}
28
29/// Session data
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Session {
32    /// Session token (UUID)
33    pub token: String,
34    /// User ID this session belongs to
35    pub user_id: i64,
36    /// Session expiration timestamp
37    pub expires_at: chrono::NaiveDateTime,
38}
39
40impl Session {
41    /// Create a new session
42    pub fn new(token: impl Into<String>, user_id: i64, expires_at: chrono::NaiveDateTime) -> Self {
43        Self {
44            token: token.into(),
45            user_id,
46            expires_at,
47        }
48    }
49
50    /// Check if the session has expired
51    pub fn is_expired(&self) -> bool {
52        chrono::Utc::now().naive_utc() > self.expires_at
53    }
54
55    /// Get remaining time until expiration
56    pub fn remaining_time(&self) -> Option<chrono::Duration> {
57        let now = chrono::Utc::now().naive_utc();
58        if now < self.expires_at {
59            Some(self.expires_at - now)
60        } else {
61            None
62        }
63    }
64}
65
66/// Login credentials
67#[derive(Debug, Clone, Deserialize)]
68pub struct Credentials {
69    /// User email
70    pub email: String,
71    /// User password
72    pub password: String,
73}
74
75impl Credentials {
76    /// Create new credentials
77    pub fn new(email: impl Into<String>, password: impl Into<String>) -> Self {
78        Self {
79            email: email.into(),
80            password: password.into(),
81        }
82    }
83}
84
85/// Login response
86#[derive(Debug, Clone, Serialize)]
87pub struct LoginResponse {
88    /// Session token
89    pub token: String,
90    /// Authenticated user information
91    pub user: AuthUser,
92    /// Session expiration timestamp
93    pub expires_at: chrono::NaiveDateTime,
94}
95
96impl LoginResponse {
97    /// Create a new login response
98    pub fn new(session: Session, user: AuthUser) -> Self {
99        Self {
100            token: session.token,
101            expires_at: session.expires_at,
102            user,
103        }
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use chrono::Duration;
111
112    #[test]
113    fn test_auth_user_creation() {
114        let user = AuthUser::new(1, "test@example.com", "admin");
115        assert_eq!(user.id, 1);
116        assert_eq!(user.email, "test@example.com");
117        assert_eq!(user.role, "admin");
118    }
119
120    #[test]
121    fn test_session_expiration() {
122        let future = chrono::Utc::now().naive_utc() + Duration::hours(1);
123        let session = Session::new("token123", 1, future);
124        assert!(!session.is_expired());
125        assert!(session.remaining_time().is_some());
126
127        let past = chrono::Utc::now().naive_utc() - Duration::hours(1);
128        let expired_session = Session::new("token456", 1, past);
129        assert!(expired_session.is_expired());
130        assert!(expired_session.remaining_time().is_none());
131    }
132
133    #[test]
134    fn test_credentials_creation() {
135        let creds = Credentials::new("user@example.com", "password123");
136        assert_eq!(creds.email, "user@example.com");
137        assert_eq!(creds.password, "password123");
138    }
139}