diff --git a/src/pretix/presale/views/__init__.py b/src/pretix/presale/views/__init__.py index 640b4532b..3bb9426e3 100644 --- a/src/pretix/presale/views/__init__.py +++ b/src/pretix/presale/views/__init__.py @@ -70,18 +70,21 @@ def cached_invoice_address(request): # do not create a session, if we don't have a session we also don't have an invoice address ;) request._checkout_flow_invoice_address = InvoiceAddress() return request._checkout_flow_invoice_address - cs = cart_session(request) - iapk = cs.get('invoice_address') - if not iapk: + cs = cart_session(request, create=False) + if cs is None: request._checkout_flow_invoice_address = InvoiceAddress() else: - try: - with scopes_disabled(): - request._checkout_flow_invoice_address = InvoiceAddress.objects.get( - pk=iapk, order__isnull=True - ) - except InvoiceAddress.DoesNotExist: + iapk = cs.get('invoice_address') + if not iapk: request._checkout_flow_invoice_address = InvoiceAddress() + else: + try: + with scopes_disabled(): + request._checkout_flow_invoice_address = InvoiceAddress.objects.get( + pk=iapk, order__isnull=True + ) + except InvoiceAddress.DoesNotExist: + request._checkout_flow_invoice_address = InvoiceAddress() return request._checkout_flow_invoice_address @@ -111,6 +114,12 @@ class CartMixin: return cached_invoice_address(self.request) def get_cart(self, answers=False, queryset=None, order=None, downloads=False, payments=None): + if not self.request.session.session_key and not order: + # The user has not even a session ID yet, so they can't have a cart and we can save a lot of work + return { + 'positions': [], + # Other keys are not used on non-checkout pages + } if queryset is not None: prefetch = [] if answers: @@ -166,7 +175,8 @@ class CartMixin: else: fees = [] - if not order: + if not order and lcp: + # Do not re-round for empty cart (useless) or confirmed order (incorrect) apply_rounding(self.request.event.settings.tax_rounding, self.invoice_address, self.request.event.currency, [*lcp, *fees]) total = sum([c.price for c in lcp]) + sum([f.value for f in fees]) @@ -277,6 +287,12 @@ class CartMixin: } def current_selected_payments(self, positions, fees, invoice_address, *, warn=False): + from pretix.presale.views.cart import get_or_create_cart_id + + if not get_or_create_cart_id(self.request, create=False): + # No active cart ID, no payments there + return [] + raw_payments = copy.deepcopy(self.cart_session.get('payments', [])) fees = [f for f in fees if f.fee_type != OrderFee.FEE_TYPE_PAYMENT] # we re-compute these here diff --git a/src/pretix/presale/views/cart.py b/src/pretix/presale/views/cart.py index 892719f67..99af19b19 100644 --- a/src/pretix/presale/views/cart.py +++ b/src/pretix/presale/views/cart.py @@ -417,7 +417,7 @@ def get_or_create_cart_id(request, create=True): return new_id -def cart_session(request): +def cart_session(request, create=True): """ Before pretix 1.8.0, all checkout-related information (like the entered email address) was stored in the user's regular session dictionary. This led to data interference and leaks for example if a @@ -428,7 +428,9 @@ def cart_session(request): active cart session sub-dictionary for read and write access. """ request.session.modified = True - cart_id = get_or_create_cart_id(request) + cart_id = get_or_create_cart_id(request, create=create) + if not cart_id and not create: + return None return request.session['carts'][cart_id] diff --git a/src/tests/presale/test_event.py b/src/tests/presale/test_event.py index 58505426e..64558c413 100644 --- a/src/tests/presale/test_event.py +++ b/src/tests/presale/test_event.py @@ -36,6 +36,7 @@ import datetime import re from decimal import Decimal +from importlib import import_module from json import loads from zoneinfo import ZoneInfo @@ -80,6 +81,34 @@ class EventMiddlewareTest(EventTestMixin, SoupTest): doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug)) self.assertIn(str(self.event.name), doc.find("h1").text) + def test_no_session_cookie_set_on_event_index_view(self): + resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug)) + self.assertEqual(resp.status_code, 200) + assert settings.SESSION_COOKIE_NAME not in self.client.cookies + + def test_no_cart_session_added_on_event_index_view(self): + # Make sure a session is present by doing a cart op on another event + event2 = Event.objects.create( + organizer=self.orga, name='30C3b', slug='30c3b', + date_from=datetime.datetime(now().year + 1, 12, 26, 14, 0, tzinfo=datetime.timezone.utc), + live=True, + ) + self.client.post('/%s/%s/cart/add' % (self.orga.slug, event2.slug), { + 'item_%d' % 1337: '1', # item does not need to exist + 'ajax': 1 + }) + assert settings.SESSION_COOKIE_NAME in self.client.cookies + + # Visit shop, make sure no session is created + resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug)) + self.assertEqual(resp.status_code, 200) + + SessionStore = import_module(settings.SESSION_ENGINE).SessionStore + session = SessionStore(self.client.cookies[settings.SESSION_COOKIE_NAME].value).load() + assert set(session.keys()) == { + f"current_cart_event_{event2.pk}", "carts" + } + def test_not_found(self): resp = self.client.get('/%s/%s/' % ('foo', 'bar')) self.assertEqual(resp.status_code, 404)