from django.contrib.auth.hashers import check_password from django.utils import timezone from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from ..models import APIKey class SystemUser: """Anonymous system user with scopes as permissions.""" def __init__(self, scopes=None): self.scopes = scopes or [] self.is_authenticated = True self.is_superuser = False self.pk = None self.id = None @property def is_anonymous(self): return False def __str__(self): return f"SystemUser(scopes={self.scopes})" class APIKeyAuthentication(BaseAuthentication): """Service-to-service authentication via X-API-Key header.""" keyword = "X-API-Key" def authenticate(self, request): raw_key = request.META.get(f"HTTP_{self.keyword.replace('-', '_').upper()}") if not raw_key: return None try: prefix, secret = raw_key.split(".", 1) except ValueError: raise AuthenticationFailed("Invalid API key format.") api_key = APIKey.objects.filter(prefix=prefix, is_active=True).first() if not api_key: raise AuthenticationFailed("API key not found.") if not check_password(secret, api_key.key_hash): raise AuthenticationFailed("Invalid API key.") if api_key.expires_at and api_key.expires_at < timezone.now(): raise AuthenticationFailed("API key expired.") api_key.last_used_at = timezone.now() api_key.save(update_fields=["last_used_at"]) return (SystemUser(scopes=api_key.scopes), api_key)