Initialize clean CSR inventory repository
This commit is contained in:
64
inventory/api_auth.py
Normal file
64
inventory/api_auth.py
Normal file
@@ -0,0 +1,64 @@
|
||||
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"
|
||||
Reference in New Issue
Block a user