| """ |
| Session model — custom session management with fingerprint binding. |
| |
| Maps to AGENT.md DBML: `sessions` table (Lines 190-200). |
| """ |
| import uuid |
|
|
| from django.conf import settings |
| from django.db import models |
| from django.utils import timezone |
|
|
| from core.models import BaseModel |
|
|
|
|
| class Session(BaseModel): |
| """ |
| Custom user session with device fingerprinting. |
| |
| Provides revocable sessions bound to IP/User-Agent to prevent hijacking. |
| Replaces Django's default session for authenticated users. |
| """ |
|
|
| user = models.ForeignKey( |
| settings.AUTH_USER_MODEL, |
| on_delete=models.CASCADE, |
| related_name="security_sessions", |
| ) |
| refresh_token_id = models.UUIDField( |
| unique=True, |
| null=True, |
| blank=True, |
| help_text="Link to refresh token chain.", |
| ) |
| fingerprint = models.CharField( |
| max_length=255, |
| blank=True, |
| default="", |
| help_text="Device fingerprint (hashed IP+UA+headers).", |
| ) |
| ip_address = models.GenericIPAddressField(null=True, blank=True) |
| user_agent = models.TextField(blank=True, default="") |
| expires_at = models.DateTimeField() |
| revoked_at = models.DateTimeField(null=True, blank=True) |
|
|
| class Meta: |
| db_table = "sessions" |
| ordering = ["-created_at"] |
| indexes = [ |
| models.Index(fields=["user", "-created_at"]), |
| models.Index(fields=["expires_at"]), |
| ] |
|
|
| def __str__(self) -> str: |
| return f"Session {self.pk} for {self.user_id}" |
|
|
| @property |
| def is_active(self) -> bool: |
| """Check if session is still valid.""" |
| return self.revoked_at is None and self.expires_at > timezone.now() |
|
|
| def revoke(self) -> None: |
| """Revoke this session.""" |
| self.revoked_at = timezone.now() |
| self.save(update_fields=["revoked_at"]) |
|
|