forked from CGM_Public/pretix_original
Enforce uniqueness of order codes and ticket secrets (#3988)
* Enforce uniqueness of order codes and ticket secrets * Fix test cases which created orders with identical codes --------- Co-authored-by: Mira Weller <weller@rami.io>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import datetime
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
from decimal import Decimal
|
||||
@@ -27,7 +28,7 @@ from zoneinfo import ZoneInfo
|
||||
|
||||
import django_filters
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import (
|
||||
Exists, F, OuterRef, Prefetch, Q, Subquery, prefetch_related_objects,
|
||||
)
|
||||
@@ -97,6 +98,8 @@ from pretix.base.signals import (
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.control.signals import order_search_filter_q
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
with scopes_disabled():
|
||||
class OrderFilter(FilterSet):
|
||||
email = django_filters.CharFilter(field_name='email', lookup_expr='iexact')
|
||||
@@ -900,7 +903,11 @@ class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
|
||||
order_modified.send(sender=serializer.instance.event, order=serializer.instance)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save()
|
||||
try:
|
||||
serializer.save()
|
||||
except IntegrityError:
|
||||
logger.exception("Integrity error while saving order")
|
||||
raise ValidationError("Integrity error, possibly duplicate submission of same order.")
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if not instance.testmode:
|
||||
|
||||
48
src/pretix/base/migrations/0258_uniq_indx.py
Normal file
48
src/pretix/base/migrations/0258_uniq_indx.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# Generated by Django 4.2.10 on 2024-03-15 09:59
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0257_item_default_price_not_null"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="order",
|
||||
name="organizer",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="orders",
|
||||
to="pretixbase.organizer",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="orderposition",
|
||||
name="organizer",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="order_positions",
|
||||
to="pretixbase.organizer",
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="order",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("organizer", "code"), name="order_organizer_code_uniq"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="orderposition",
|
||||
constraint=models.UniqueConstraint(
|
||||
models.F("organizer"),
|
||||
models.F("secret"),
|
||||
name="orderposition_organizer_secret_uniq",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -188,6 +188,14 @@ class Order(LockModel, LoggedModel):
|
||||
default=False,
|
||||
)
|
||||
testmode = models.BooleanField(default=False)
|
||||
organizer = models.ForeignKey(
|
||||
# Redundant foreign key, but is required for a uniqueness constraint
|
||||
"Organizer",
|
||||
related_name="orders",
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
verbose_name=_("Event"),
|
||||
@@ -286,6 +294,9 @@ class Order(LockModel, LoggedModel):
|
||||
models.Index(fields=["datetime", "id"]),
|
||||
models.Index(fields=["last_modified", "id"]),
|
||||
]
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=["organizer", "code"], name="order_organizer_code_uniq"),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.full_code
|
||||
@@ -499,6 +510,10 @@ class Order(LockModel, LoggedModel):
|
||||
self.set_expires()
|
||||
if 'update_fields' in kwargs:
|
||||
kwargs['update_fields'] = {'expires'}.union(kwargs['update_fields'])
|
||||
if not self.organizer_id:
|
||||
self.organizer_id = self.event.organizer_id
|
||||
if 'update_fields' in kwargs:
|
||||
kwargs['update_fields'] = {'organizer'}.union(kwargs['update_fields'])
|
||||
|
||||
is_new = not self.pk
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
@@ -2356,6 +2371,14 @@ class OrderPosition(AbstractPosition):
|
||||
"""
|
||||
positionid = models.PositiveIntegerField(default=1)
|
||||
|
||||
organizer = models.ForeignKey(
|
||||
# Redundant foreign key, but is required for a uniqueness constraint
|
||||
"Organizer",
|
||||
related_name="order_positions",
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
order = models.ForeignKey(
|
||||
Order,
|
||||
verbose_name=_("Order"),
|
||||
@@ -2429,6 +2452,9 @@ class OrderPosition(AbstractPosition):
|
||||
verbose_name = _("Order position")
|
||||
verbose_name_plural = _("Order positions")
|
||||
ordering = ("positionid", "id")
|
||||
constraints = [
|
||||
models.UniqueConstraint("organizer", "secret", name="orderposition_organizer_secret_uniq")
|
||||
]
|
||||
|
||||
@cached_property
|
||||
def sort_key(self):
|
||||
@@ -2585,6 +2611,10 @@ class OrderPosition(AbstractPosition):
|
||||
if 'update_fields' in kwargs:
|
||||
kwargs['update_fields'] = {'secret'}.union(kwargs['update_fields'])
|
||||
|
||||
if not self.organizer_id:
|
||||
self.organizer_id = self.order.event.organizer_id
|
||||
if 'update_fields' in kwargs:
|
||||
kwargs['update_fields'] = {'organizer'}.union(kwargs['update_fields'])
|
||||
if not self.blocked and self.blocked is not None:
|
||||
self.blocked = None
|
||||
if 'update_fields' in kwargs:
|
||||
|
||||
Reference in New Issue
Block a user