use anyhow::Result;
use async_trait::async_trait;
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use chrono::{DateTime, Utc};
pub trait AuthenticatableUser: Clone + Send + Sync {
fn id(&self) -> &Uuid;
fn email(&self) -> &str;
fn password_hash(&self) -> &str;
fn is_active(&self) -> bool;
fn is_locked(&self) -> bool;
fn roles(&self) -> &[String];
fn two_factor_enabled(&self) -> bool { false }
fn two_factor_methods(&self) -> &[String] { &[] }
fn account_expires_at(&self) -> Option<DateTime<Utc>> { None }
fn requires_password_change(&self) -> bool { false }
fn last_login_at(&self) -> Option<DateTime<Utc>> { None }
fn failed_login_attempts(&self) -> u32 { 0 }
fn locked_until(&self) -> Option<DateTime<Utc>> { None }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimpleUser {
pub id: Uuid,
pub email: String,
pub password_hash: String,
pub is_active: bool,
pub is_locked: bool,
pub roles: Vec<String>,
pub two_factor_enabled: bool,
pub two_factor_methods: Vec<String>,
pub account_expires_at: Option<DateTime<Utc>>,
pub requires_password_change: bool,
pub last_login_at: Option<DateTime<Utc>>,
pub failed_login_attempts: u32,
pub locked_until: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl AuthenticatableUser for SimpleUser {
fn id(&self) -> &Uuid { &self.id }
fn email(&self) -> &str { &self.email }
fn password_hash(&self) -> &str { &self.password_hash }
fn is_active(&self) -> bool { self.is_active }
fn is_locked(&self) -> bool { self.is_locked }
fn roles(&self) -> &[String] { &self.roles }
fn two_factor_enabled(&self) -> bool { self.two_factor_enabled }
fn two_factor_methods(&self) -> &[String] { &self.two_factor_methods }
fn account_expires_at(&self) -> Option<DateTime<Utc>> { self.account_expires_at }
fn requires_password_change(&self) -> bool { self.requires_password_change }
fn last_login_at(&self) -> Option<DateTime<Utc>> { self.last_login_at }
fn failed_login_attempts(&self) -> u32 { self.failed_login_attempts }
fn locked_until(&self) -> Option<DateTime<Utc>> { self.locked_until }
}
pub type User = SimpleUser;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefreshTokenClaims {
pub sub: String,
pub exp: usize,
pub iat: usize,
pub iss: String,
pub token_type: String,
}
#[async_trait]
pub trait UserRepository<U: AuthenticatableUser = SimpleUser>: Send + Sync {
async fn find_by_email(&self, email: &str) -> Result<Option<U>>;
async fn find_by_id(&self, id: &Uuid) -> Result<Option<U>>;
async fn create(&self, user: &U) -> Result<U>;
async fn update(&self, user: &U) -> Result<U>;
async fn update_last_login(&self, user_id: &Uuid) -> Result<()>;
async fn increment_failed_attempts(&self, user_id: &Uuid) -> Result<()>;
async fn reset_failed_attempts(&self, user_id: &Uuid) -> Result<()>;
async fn lock_account(&self, user_id: &Uuid, locked_until: Option<DateTime<Utc>>) -> Result<()>;
async fn unlock_account(&self, user_id: &Uuid) -> Result<()>;
async fn exists_by_email(&self, email: &str) -> Result<bool>;
}
#[async_trait]
pub trait SecurityService: Send + Sync {
async fn check_rate_limit(&self, email: &str, ip_address: Option<&str>) -> Result<()>;
async fn log_failed_auth_attempt(&self, user_id: &Uuid, ip_address: Option<&str>) -> Result<()>;
async fn log_successful_auth(&self, user_id: &Uuid, ip_address: Option<&str>) -> Result<()>;
async fn analyze_login_attempt(
&self,
user_id: &Uuid,
device_info: &Option<DeviceInfo>,
ip_address: Option<&str>
) -> Result<SecurityFlags>;
async fn generate_password_reset_token(&self, user_id: &Uuid) -> Result<String>;
async fn validate_password_reset_token(&self, token: &str) -> Result<Option<Uuid>>;
async fn send_security_alert(&self, user_id: &Uuid, alert_type: SecurityAlertType, details: &str) -> Result<()>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceInfo {
pub device_id: Option<String>,
pub device_type: String, pub platform: Option<String>, pub user_agent: Option<String>,
pub fingerprint: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SecurityFlags {
pub new_device: bool,
pub new_location: bool,
pub suspicious_activity: bool,
pub requires_password_change: bool,
pub risk_score: f32, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SecurityAlertType {
NewDeviceLogin,
NewLocationLogin,
SuspiciousActivity,
AccountLocked,
PasswordReset,
MultipleFailedAttempts,
}
#[derive(Debug, Clone)]
pub struct AuthContext {
pub ip_address: Option<String>,
pub user_agent: Option<String>,
pub device_fingerprint: Option<String>,
pub timestamp: DateTime<Utc>,
pub session_id: Option<String>,
}
impl Default for AuthContext {
fn default() -> Self {
Self {
ip_address: None,
user_agent: None,
device_fingerprint: None,
timestamp: Utc::now(),
session_id: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PasswordPolicy {
pub min_length: usize,
pub max_length: usize,
pub require_uppercase: bool,
pub require_lowercase: bool,
pub require_numbers: bool,
pub require_special_chars: bool,
pub forbidden_patterns: Vec<String>,
pub common_passwords: Vec<String>,
}
impl Default for PasswordPolicy {
fn default() -> Self {
Self {
min_length: 8,
max_length: 128,
require_uppercase: true,
require_lowercase: true,
require_numbers: true,
require_special_chars: false,
forbidden_patterns: vec![
"password".to_string(),
"123456".to_string(),
"qwerty".to_string(),
],
common_passwords: vec![
"password".to_string(),
"123456".to_string(),
"123456789".to_string(),
"qwerty".to_string(),
"abc123".to_string(),
"password123".to_string(),
"admin".to_string(),
"letmein".to_string(),
"welcome".to_string(),
"monkey".to_string(),
],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TwoFactorMethod {
TOTP, SMS, Email, BackupCode, }
#[derive(Debug, Clone)]
pub struct TwoFactorChallenge {
pub user_id: Uuid,
pub method: TwoFactorMethod,
pub challenge: String,
pub expires_at: DateTime<Utc>,
}
#[derive(Debug, Clone)]
pub struct PasswordResetRequest {
pub email: String,
pub context: AuthContext,
}
#[derive(Debug, Clone)]
pub struct PasswordResetConfirmation {
pub token: String,
pub new_password: String,
pub context: AuthContext,
}
#[derive(Debug, Clone)]
pub struct AuthRequest {
pub email: String,
pub password: String,
pub remember_me: Option<bool>,
pub device_info: Option<DeviceInfo>,
pub ip_address: Option<String>,
pub user_agent: Option<String>,
}
#[derive(Debug, Clone)]
pub struct AuthResultEnhanced {
pub user_id: Uuid,
pub token: String,
pub refresh_token: Option<String>,
pub expires_at: DateTime<Utc>,
pub requires_2fa: bool,
pub security_flags: SecurityFlags,
}