Audit entries written only inside the caller's transaction, database throttle counters that refuse to run inside a transaction that could roll back, and a database session store with the user key in its own column, end_sessions and idle and absolute limits. Ticket 1.3. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sk72AxLwQnYttF6VFSxQLt
32 lines
1.1 KiB
Python
32 lines
1.1 KiB
Python
from django.contrib.sessions.base_session import AbstractBaseSession
|
|
from django.db import models
|
|
from django.utils import timezone
|
|
|
|
|
|
class UserSession(AbstractBaseSession):
|
|
"""Database session with the user key in its own column (AD-6)."""
|
|
|
|
username = models.CharField(max_length=256, blank=True, default="", db_index=True)
|
|
created_at = models.DateTimeField(default=timezone.now) # UTC
|
|
|
|
@classmethod
|
|
def get_session_store_class(cls):
|
|
from .session_store import SessionStore
|
|
|
|
return SessionStore
|
|
|
|
|
|
class ThrottleCounter(models.Model):
|
|
"""One row per (endpoint, subject); subject is `user:<key>` or `ip:<address>` (AD-7)."""
|
|
|
|
endpoint = models.CharField(max_length=64)
|
|
subject = models.CharField(max_length=256)
|
|
count = models.PositiveIntegerField(default=0)
|
|
window_started = models.DateTimeField(default=timezone.now)
|
|
blocked_until = models.DateTimeField(null=True, blank=True)
|
|
|
|
class Meta:
|
|
constraints = [
|
|
models.UniqueConstraint(fields=["endpoint", "subject"], name="uniq_throttle_subject")
|
|
]
|