lmrc-auth 0.3.16

Authentication framework for LMRC Stack applications
Documentation
//! Authentication data models
//!
//! Core data structures for authentication and session management.

use serde::{Deserialize, Serialize};

/// User information returned after successful authentication
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthUser {
    /// User ID
    pub id: i64,
    /// User email
    pub email: String,
    /// User role (e.g., "admin", "user")
    pub role: String,
}

impl AuthUser {
    /// Create a new authenticated user
    pub fn new(id: i64, email: impl Into<String>, role: impl Into<String>) -> Self {
        Self {
            id,
            email: email.into(),
            role: role.into(),
        }
    }
}

/// Session data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
    /// Session token (UUID)
    pub token: String,
    /// User ID this session belongs to
    pub user_id: i64,
    /// Session expiration timestamp
    pub expires_at: chrono::NaiveDateTime,
}

impl Session {
    /// Create a new session
    pub fn new(token: impl Into<String>, user_id: i64, expires_at: chrono::NaiveDateTime) -> Self {
        Self {
            token: token.into(),
            user_id,
            expires_at,
        }
    }

    /// Check if the session has expired
    pub fn is_expired(&self) -> bool {
        chrono::Utc::now().naive_utc() > self.expires_at
    }

    /// Get remaining time until expiration
    pub fn remaining_time(&self) -> Option<chrono::Duration> {
        let now = chrono::Utc::now().naive_utc();
        if now < self.expires_at {
            Some(self.expires_at - now)
        } else {
            None
        }
    }
}

/// Login credentials
#[derive(Debug, Clone, Deserialize)]
pub struct Credentials {
    /// User email
    pub email: String,
    /// User password
    pub password: String,
}

impl Credentials {
    /// Create new credentials
    pub fn new(email: impl Into<String>, password: impl Into<String>) -> Self {
        Self {
            email: email.into(),
            password: password.into(),
        }
    }
}

/// Login response
#[derive(Debug, Clone, Serialize)]
pub struct LoginResponse {
    /// Session token
    pub token: String,
    /// Authenticated user information
    pub user: AuthUser,
    /// Session expiration timestamp
    pub expires_at: chrono::NaiveDateTime,
}

impl LoginResponse {
    /// Create a new login response
    pub fn new(session: Session, user: AuthUser) -> Self {
        Self {
            token: session.token,
            expires_at: session.expires_at,
            user,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::Duration;

    #[test]
    fn test_auth_user_creation() {
        let user = AuthUser::new(1, "test@example.com", "admin");
        assert_eq!(user.id, 1);
        assert_eq!(user.email, "test@example.com");
        assert_eq!(user.role, "admin");
    }

    #[test]
    fn test_session_expiration() {
        let future = chrono::Utc::now().naive_utc() + Duration::hours(1);
        let session = Session::new("token123", 1, future);
        assert!(!session.is_expired());
        assert!(session.remaining_time().is_some());

        let past = chrono::Utc::now().naive_utc() - Duration::hours(1);
        let expired_session = Session::new("token456", 1, past);
        assert!(expired_session.is_expired());
        assert!(expired_session.remaining_time().is_none());
    }

    #[test]
    fn test_credentials_creation() {
        let creds = Credentials::new("user@example.com", "password123");
        assert_eq!(creds.email, "user@example.com");
        assert_eq!(creds.password, "password123");
    }
}