Files
Inventory/inventory/api_auth.py
2026-09-29 08:43:05 +02:00

65 lines
2.5 KiB
Python

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 <Schlüssel>`."""
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"