fix: Update cors_csrf middleware for Django 4.0 (#33262)

Remove use of Django's `CSRF_COOKIE_USED`, which is no longer leaked
outside of the dynamic scope of the Django csrf middleware as of Django
4.0. Specifically, https://github.com/django/django/pull/14688 replaced
that META entry and a request attribute with a single META entry
`CSRF_COOKIE_NEEDS_UPDATE`, which is then set back to False once the CSRF
cookie is set by the middleware's process_response.

We'll send the cross-domain cookie if the decorator requests it and the
value is present, regardless of whether the same-domain cookie would have
been sent. (And we'll still *set* `CSRF_COOKIE_NEEDS_UPDATE` to ensure that
a cookie gets generated.)

See https://github.com/openedx/edx-platform/issues/33207
This commit is contained in:
Tim McCormack
2023-09-15 10:34:25 -04:00
committed by GitHub
parent 265701c01d
commit eeb35733bb
3 changed files with 16 additions and 6 deletions

View File

@@ -120,12 +120,18 @@ class CsrfCrossDomainCookieMiddleware(MiddlewareMixin):
log.debug("Could not set cross-domain CSRF cookie.")
return response
# Check whether (a) the CSRF middleware has already set a cookie, and
# (b) this is a view decorated with `@ensure_cross_domain_csrf_cookie`
# If so, we can send the cross-domain CSRF cookie.
# Send the cross-domain CSRF cookie if this is a view decorated with
# `@ensure_cross_domain_csrf_cookie` and the same-domain CSRF cookie
# value is available.
#
# Because CSRF_COOKIE can be set either by an inbound CSRF token or
# by the middleware generating a new one or echoing the old one for
# the response, this might result in sending the cookie more often
# than the CSRF value actually changes, but as of Django 4.0 we no
# longer have a good way of finding out when the csrf middleware has
# updated the value.
should_set_cookie = (
request.META.get('CROSS_DOMAIN_CSRF_COOKIE_USED', False) and
request.META.get('CSRF_COOKIE_USED', False) and
request.META.get('CSRF_COOKIE') is not None
)

View File

@@ -3,6 +3,8 @@
import json
from unittest import mock
import django
from django.http import HttpResponse
from django.test import TestCase
@@ -25,4 +27,7 @@ class TestEnsureCsrfCookieCrossDomain(TestCase):
response = wrapped_view(request)
response_meta = json.loads(response.content.decode('utf-8'))
assert response_meta['CROSS_DOMAIN_CSRF_COOKIE_USED'] is True
assert response_meta['CSRF_COOKIE_USED'] is True
if django.VERSION < (4, 0):
assert response_meta['CSRF_COOKIE_USED'] is True
else:
assert response_meta['CSRF_COOKIE_NEEDS_UPDATE'] is True

View File

@@ -258,7 +258,6 @@ class TestCsrfCrossDomainCookieMiddleware(TestCase):
del request.META['HTTP_REFERER']
if csrf_cookie_used:
request.META['CSRF_COOKIE_USED'] = True
request.META['CSRF_COOKIE'] = self.COOKIE_VALUE
if cross_domain_decorator: