# # This file is part of pretix (Community Edition). # # Copyright (C) 2014-2020 Raphael Michel and contributors # Copyright (C) 2020-2021 rami.io GmbH and contributors # # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General # Public License as published by the Free Software Foundation in version 3 of the License. # # ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are # applicable granting you additional permissions and placing additional restrictions on your usage of this software. # Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive # this file, see . # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more # details. # # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # import json import logging from hashlib import sha1 from django.conf import settings from django.http import HttpResponse, JsonResponse from rest_framework import status from rest_framework.views import APIView from pretix.api.models import ApiCall logger = logging.getLogger(__name__) class IdempotencyQueryView(APIView): # Experimental feature, therefore undocumented for now authentication_classes = () permission_classes = () def get(self, request, format=None): idempotency_key = request.GET.get("key") auth_hash_parts = '{}:{}'.format( request.headers.get('Authorization', ''), request.COOKIES.get('__Host-' + settings.SESSION_COOKIE_NAME, request.COOKIES.get(settings.SESSION_COOKIE_NAME, '')) ) auth_hash = sha1(auth_hash_parts.encode()).hexdigest() if not idempotency_key: return JsonResponse({ 'detail': 'No idempotency key given.' }, status=status.HTTP_404_NOT_FOUND) try: call = ApiCall.objects.get( auth_hash=auth_hash, idempotency_key=idempotency_key, ) except ApiCall.DoesNotExist: return JsonResponse({ 'detail': 'Idempotency key not seen before.' }, status=status.HTTP_404_NOT_FOUND) if call.locked: r = JsonResponse( {'detail': 'Concurrent request with idempotency key.'}, status=status.HTTP_409_CONFLICT, ) r['Retry-After'] = 5 return r content = call.response_body if isinstance(content, memoryview): content = content.tobytes() r = HttpResponse( content=content, status=call.response_code, ) for k, v in json.loads(call.response_headers).values(): r[k] = v return r