from __future__ import annotations from datetime import timedelta from ipaddress import ip_address from django.utils import timezone from rest_framework import authentication, exceptions, permissions, throttling from rest_framework_api_key.permissions import KeyParser from .models import InventoryAPIKey from .permissions import can_edit class InventoryAPIKeyAuthentication(authentication.BaseAuthentication): """Authentifiziert ausschließlich `Authorization: Api-Key `.""" key_parser = KeyParser() def authenticate(self, request): raw_key = self.key_parser.get(request) if not raw_key: return None try: api_key = InventoryAPIKey.objects.get_from_key(raw_key) except (InventoryAPIKey.DoesNotExist, ValueError): raise exceptions.AuthenticationFailed("Ungültiger API-Schlüssel.") if api_key.has_expired: raise exceptions.AuthenticationFailed("Der API-Schlüssel ist abgelaufen.") if not api_key.service_user.is_active: raise exceptions.AuthenticationFailed("Das zugehörige Konto ist deaktiviert.") # Letzte Nutzung höchstens alle fünf Minuten schreiben, damit häufige # Lesezugriffe nicht unnötig viele Datenbankänderungen erzeugen. now = timezone.now() if not api_key.last_used_at or api_key.last_used_at < now - timedelta(minutes=5): remote_addr = request.META.get("REMOTE_ADDR", "") try: last_used_ip = str(ip_address(remote_addr)) if remote_addr else None except ValueError: last_used_ip = None InventoryAPIKey.objects.filter(pk=api_key.pk).update(last_used_at=now, last_used_ip=last_used_ip) api_key.last_used_at = now api_key.last_used_ip = last_used_ip return api_key.service_user, api_key def authenticate_header(self, request): return 'Api-Key realm="inventory-api"' class InventoryAPIPermission(permissions.BasePermission): message = "Dieser API-Schlüssel besitzt nicht die erforderliche Berechtigung." def has_permission(self, request, view): api_key = request.auth if not isinstance(api_key, InventoryAPIKey): return False if request.method in permissions.SAFE_METHODS: return api_key.can_read return api_key.can_write and can_edit(request.user) class APIClientRateThrottle(throttling.UserRateThrottle): scope = "api_client"