Fix customer views using wrong csrf middleware (#6027)

* Fix customer views using wrong csrf middleware

This lead to persistent csrf validation errors if the token from the cookie expired, which could only be solved by clearing cookies.

* Remove unneccesary csrf_protect decorators

* Fix typo

Co-authored-by: Raphael Michel <michel@pretix.eu>

---------

Co-authored-by: Raphael Michel <michel@pretix.eu>
This commit is contained in:
pajowu
2026-07-10 13:25:40 +02:00
committed by pajowu
co-authored by Raphael Michel
parent e19908571c
commit 76e6803eac
4 changed files with 38 additions and 19 deletions
+1 -1
View File
@@ -27,11 +27,11 @@ from django.template import TemplateDoesNotExist, loader
from django.template.loader import get_template
from django.utils.functional import Promise
from django.utils.translation import gettext as _
from django.views.decorators.csrf import requires_csrf_token
from sentry_sdk import last_event_id
from pretix.base.i18n import language
from pretix.base.middleware import get_language_from_request
from pretix.multidomain.middlewares import requires_csrf_token
def csrf_failure(request, reason=""):
+1 -1
View File
@@ -22,7 +22,7 @@
import itertools
import re
from http.cookies import Morsel
import logging
from django.conf import settings
+36 -8
View File
@@ -51,6 +51,7 @@ from django.middleware.csrf import (
from django.shortcuts import render
from django.urls import set_urlconf
from django.utils.cache import patch_vary_headers
from django.utils.decorators import decorator_from_middleware
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import http_date
from django_scopes import scopes_disabled
@@ -216,7 +217,8 @@ class SessionMiddleware(BaseSessionMiddleware):
request.session.save()
if is_secure and settings.SESSION_COOKIE_NAME in request.COOKIES: # remove legacy cookie
# response.delete_cookie does not work as we might have set a partitioned cookie
thoroughly_delete_cookie(response,
thoroughly_delete_cookie(
response,
settings.SESSION_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH,
secure=is_secure,
@@ -278,12 +280,13 @@ class CsrfViewMiddleware(BaseCsrfMiddleware):
# remove legacy cookie
if request.is_secure() and settings.CSRF_COOKIE_NAME in request.COOKIES:
thoroughly_delete_cookie(response,
settings.CSRF_COOKIE_NAME,
path=settings.CSRF_COOKIE_PATH,
secure=request.is_secure(),
httponly=settings.CSRF_COOKIE_HTTPONLY
)
thoroughly_delete_cookie(
response,
settings.CSRF_COOKIE_NAME,
path=settings.CSRF_COOKIE_PATH,
secure=request.is_secure(),
httponly=settings.CSRF_COOKIE_HTTPONLY
)
handle_duplicated_csrftoken(request, response)
@@ -299,6 +302,19 @@ class CsrfViewMiddleware(BaseCsrfMiddleware):
# Content varies with the CSRF cookie, so set the Vary header.
patch_vary_headers(response, ('Cookie',))
def process_response(self, request, response):
if (
not settings.CSRF_USE_SESSIONS
and request.is_secure()
and settings.CSRF_COOKIE_NAME in response.cookies
and response.cookies[settings.CSRF_COOKIE_NAME].value
):
logger.warning("Usage of djangos CsrfViewMiddleware detected (legacy cookie found in response). "
"This may be caused by using csrf_project or requires_csrf_token from django.views.decorators.csrf. "
"Use the pretix.multidomain.middlewares equivalent instead.")
return super().process_response(request, response)
def handle_duplicated_csrftoken(request, response):
# Due to a Safari bug, in some browser, two csrftoken cookies can exist:
@@ -317,4 +333,16 @@ def handle_duplicated_csrftoken(request, response):
secure=request.is_secure(),
path=settings.CSRF_COOKIE_PATH,
httponly=settings.CSRF_COOKIE_HTTPONLY
)
)
csrf_protect = decorator_from_middleware(CsrfViewMiddleware)
class _EnsureCsrfToken(CsrfViewMiddleware):
# Behave like CsrfViewMiddleware but don't reject requests or log warnings.
def _reject(self, request, reason):
return None
requires_csrf_token = decorator_from_middleware(_EnsureCsrfToken)
-9
View File
@@ -44,7 +44,6 @@ from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import FormView, ListView, View
@@ -101,7 +100,6 @@ class LoginView(RedirectBackMixin, FormView):
redirect_authenticated_user = True
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts:
@@ -213,7 +211,6 @@ class RegistrationView(RedirectBackMixin, FormView):
redirect_authenticated_user = True
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts:
@@ -257,7 +254,6 @@ class SetPasswordView(FormView):
template_name = 'pretixpresale/organizers/customer_setpassword.html'
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts:
@@ -301,7 +297,6 @@ class ResetPasswordView(FormView):
template_name = 'pretixpresale/organizers/customer_resetpw.html'
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts:
@@ -527,7 +522,6 @@ class ChangePasswordView(CustomerAccountBaseMixin, FormView):
form_class = ChangePasswordForm
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts:
@@ -561,7 +555,6 @@ class ChangeInformationView(CustomerAccountBaseMixin, FormView):
form_class = ChangeInfoForm
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts:
@@ -669,7 +662,6 @@ class SSOLoginView(RedirectBackMixin, View):
redirect_authenticated_user = True
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts:
@@ -732,7 +724,6 @@ class SSOLoginReturnView(RedirectBackMixin, View):
redirect_authenticated_user = True
@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if not request.organizer.settings.customer_accounts: