""" Login History Model Tracks user login events for security auditing and compliance with the login_history_retention setting on the User model. """ import pghistory from django.conf import settings from django.db import models @pghistory.track() class LoginHistory(models.Model): """ Records each successful login attempt for a user. Used for security auditing, login notifications, and compliance with the user's login_history_retention preference. """ user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="login_history", help_text="User who logged in", ) ip_address = models.GenericIPAddressField( null=True, blank=True, help_text="IP address from which the login occurred", ) user_agent = models.CharField( max_length=500, blank=True, help_text="Browser/client user agent string", ) login_method = models.CharField( max_length=20, choices=[ ("PASSWORD", "Password"), ("GOOGLE", "Google OAuth"), ("DISCORD", "Discord OAuth"), ("MAGIC_LINK", "Magic Link"), ("SESSION", "Session Refresh"), ], default="PASSWORD", help_text="Method used for authentication", ) login_timestamp = models.DateTimeField( auto_now_add=True, db_index=True, help_text="When the login occurred", ) success = models.BooleanField( default=True, help_text="Whether the login was successful", ) # Optional geolocation data (can be populated asynchronously) country = models.CharField( max_length=100, blank=True, help_text="Country derived from IP (optional)", ) city = models.CharField( max_length=100, blank=True, help_text="City derived from IP (optional)", ) class Meta: verbose_name = "Login History" verbose_name_plural = "Login History" ordering = ["-login_timestamp"] indexes = [ models.Index(fields=["user", "-login_timestamp"]), models.Index(fields=["ip_address"]), ] def __str__(self): return f"{self.user.username} login at {self.login_timestamp}" @classmethod def cleanup_old_entries(cls, days=90): """ Remove login history entries older than the specified number of days. Respects each user's login_history_retention preference. """ from datetime import timedelta from django.utils import timezone # Default cleanup for entries older than the specified days cutoff = timezone.now() - timedelta(days=days) deleted_count, _ = cls.objects.filter( login_timestamp__lt=cutoff ).delete() return deleted_count