59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
from django.contrib.auth.models import AbstractUser
|
|
from django.db import models
|
|
|
|
|
|
class User(AbstractUser):
|
|
"""Custom User model using email as the unique identifier."""
|
|
|
|
email = models.EmailField(
|
|
unique=True,
|
|
max_length=254,
|
|
help_text="Email address used for authentication.",
|
|
)
|
|
username = models.CharField(
|
|
max_length=150,
|
|
blank=True,
|
|
null=True,
|
|
help_text="Optional display name. Not used for authentication.",
|
|
)
|
|
|
|
USERNAME_FIELD = "email"
|
|
REQUIRED_FIELDS: list[str] = []
|
|
|
|
class Meta:
|
|
db_table = "accounts_user"
|
|
verbose_name = "User"
|
|
verbose_name_plural = "Users"
|
|
|
|
def __str__(self) -> str:
|
|
return self.email
|
|
|
|
@property
|
|
def is_admin(self) -> bool:
|
|
"""Check if user belongs to the admin group."""
|
|
return self.groups.filter(name="admin").exists() or self.is_superuser
|
|
|
|
|
|
class BlacklistedToken(models.Model):
|
|
"""Stores blacklisted JWT tokens for revocation."""
|
|
|
|
jti = models.UUIDField(unique=True, help_text="JWT ID from the token payload")
|
|
user = models.ForeignKey(
|
|
User,
|
|
on_delete=models.CASCADE,
|
|
related_name="blacklisted_tokens",
|
|
)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
expires_at = models.DateTimeField(help_text="When the token would have naturally expired")
|
|
|
|
class Meta:
|
|
db_table = "accounts_blacklisted_token"
|
|
verbose_name = "Blacklisted Token"
|
|
verbose_name_plural = "Blacklisted Tokens"
|
|
indexes = [
|
|
models.Index(fields=["jti"]),
|
|
models.Index(fields=["expires_at"]),
|
|
]
|
|
|
|
def __str__(self) -> str:
|
|
return f"Blacklisted {self.jti} (user {self.user_id})" |