| """ |
| SecurityEvent model β structured security event log. |
| |
| Maps to AGENT.md DBML: `security_events` table (Lines 254-260). |
| """ |
| from django.conf import settings |
| from django.db import models |
|
|
| from core.models import BaseModel |
|
|
|
|
| class SecurityEvent(BaseModel): |
| """ |
| Records security-relevant events for a user. |
| |
| Used for security alerts and anomaly detection. |
| Events are append-only β never updated or deleted. |
| """ |
|
|
| class EventType(models.TextChoices): |
| PASSWORD_CHANGE = "password_change", "Password Changed" |
| PASSWORD_RESET = "password_reset", "Password Reset" |
| EMAIL_CHANGE = "email_change", "Email Changed" |
| MFA_ENABLE = "mfa_enable", "MFA Enabled" |
| MFA_DISABLE = "mfa_disable", "MFA Disabled" |
| LOGIN_SUCCESS = "login_success", "Login Success" |
| LOGIN_FAILURE = "login_failure", "Login Failure" |
| DEVICE_NEW = "device_new", "New Device Detected" |
| SESSION_REVOKE = "session_revoke", "Session Revoked" |
| ACCOUNT_LOCKED = "account_locked", "Account Locked" |
| ACCOUNT_SUSPENDED = "account_suspended", "Account Suspended" |
|
|
| user = models.ForeignKey( |
| settings.AUTH_USER_MODEL, |
| on_delete=models.CASCADE, |
| related_name="security_events", |
| ) |
| event_type = models.CharField( |
| max_length=50, |
| choices=EventType.choices, |
| ) |
| metadata = models.JSONField( |
| default=dict, |
| blank=True, |
| help_text="Additional event-specific data (IP, device info, etc.).", |
| ) |
|
|
| class Meta: |
| db_table = "security_events" |
| ordering = ["-created_at"] |
| indexes = [ |
| models.Index(fields=["user", "-created_at"]), |
| models.Index(fields=["event_type", "-created_at"]), |
| ] |
|
|
| def __str__(self) -> str: |
| return f"{self.get_event_type_display()} β {self.user_id}" |
|
|