Add authenticated device registration for USB intake
Some checks failed
Tests / postgres (push) Has been cancelled
Some checks failed
Tests / postgres (push) Has been cancelled
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.db import transaction
|
||||
@@ -9,9 +11,9 @@ from rest_framework import generics, serializers, status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from .models import ChoiceValue, Device, Employee
|
||||
from .models import AuditLog, ChoiceValue, Device, DeviceRegistration, Employee, NumberSequence
|
||||
from .query import SPECIFICATION_RELATIONS
|
||||
from .services import update_device_field
|
||||
from .services import add_audit, update_device_field
|
||||
|
||||
|
||||
class SecureAPIResponseMixin:
|
||||
@@ -121,6 +123,16 @@ class DeviceChangesSerializer(StrictSerializer):
|
||||
return attrs
|
||||
|
||||
|
||||
class DeviceCreateSerializer(DeviceChangesSerializer):
|
||||
request_id = serializers.UUIDField()
|
||||
status = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ChoiceValue.objects.filter(kind=ChoiceValue.Kind.STATUS, active=True),
|
||||
)
|
||||
device_type = serializers.PrimaryKeyRelatedField(
|
||||
queryset=ChoiceValue.objects.filter(kind=ChoiceValue.Kind.DEVICE_TYPE, active=True),
|
||||
)
|
||||
|
||||
|
||||
class DevicePatchSerializer(DeviceChangesSerializer):
|
||||
expected_updated_at = serializers.DateTimeField(required=False, write_only=True)
|
||||
expected_revision = serializers.IntegerField(required=False, min_value=1, write_only=True)
|
||||
@@ -169,6 +181,29 @@ def apply_changes(device, changes, *, user, api_client, expected_updated_at=None
|
||||
class DeviceListView(SecureAPIResponseMixin, generics.ListAPIView):
|
||||
serializer_class = DeviceSerializer
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request):
|
||||
serializer = DeviceCreateSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
values = dict(serializer.validated_data)
|
||||
request_id = values.pop("request_id")
|
||||
digest = hashlib.sha256(json.dumps(request.data, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
|
||||
# Share the existing allocation lock, including across simultaneous retries.
|
||||
NumberSequence.objects.select_for_update().get_or_create(name="device")
|
||||
previous = DeviceRegistration.objects.select_related("device").filter(request_id=request_id).first()
|
||||
if previous:
|
||||
if previous.payload_hash != digest:
|
||||
return Response({"detail": "request_id wurde bereits für andere Daten verwendet."}, status=409)
|
||||
return Response(DeviceSerializer(previous.device).data, status=200)
|
||||
try:
|
||||
device = Device.objects.create(**values)
|
||||
except DjangoValidationError as exc:
|
||||
raise serializers.ValidationError(exc.messages)
|
||||
DeviceRegistration.objects.create(request_id=request_id, payload_hash=digest, device=device)
|
||||
add_audit(request.user, device, AuditLog.Action.CREATE,
|
||||
new=f"Gerät #{device.number}", api_client=request.auth.name)
|
||||
return Response(DeviceSerializer(device).data, status=201)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Device.objects.select_related(
|
||||
"status", "recipient", "location", "device_type", "employee",
|
||||
|
||||
Reference in New Issue
Block a user