diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 9ca727fca9f..10fab290c9a 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -62,9 +62,6 @@ class CsrfViewMiddleware(object): tag. """ def process_view(self, request, callback, callback_args, callback_kwargs): - if getattr(callback, 'csrf_exempt', False): - return None - if getattr(request, 'csrf_processing_done', False): return None @@ -90,6 +87,11 @@ class CsrfViewMiddleware(object): # place of a CSRF cookie for this request only. cookie_is_new = True + # Wait until request.META["CSRF_COOKIE"] has been manipulated before + # bailing out, so that get_token still works + if getattr(callback, 'csrf_exempt', False): + return None + if request.method == 'POST': if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. It comes after diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index 0a24522d9c9..f383978266c 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -3,7 +3,7 @@ from django.test import TestCase from django.http import HttpRequest, HttpResponse from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware -from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt from django.core.context_processors import csrf from django.contrib.sessions.middleware import SessionMiddleware from django.utils.importlib import import_module @@ -123,6 +123,23 @@ class CsrfMiddlewareTest(TestCase): # Check the Vary header got patched correctly self.assert_('Cookie' in resp2.get('Vary','')) + def test_process_response_for_exempt_view(self): + """ + Check that a view decorated with 'csrf_view_exempt' is still + post-processed to add the CSRF token. + """ + req = self._get_GET_no_csrf_cookie_request() + CsrfMiddleware().process_view(req, csrf_view_exempt(post_form_view), (), {}) + + resp = post_form_response() + resp_content = resp.content # needed because process_response modifies resp + resp2 = CsrfMiddleware().process_response(req, resp) + + csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) + self.assertNotEqual(csrf_cookie, False) + self.assertNotEqual(resp_content, resp2.content) + self._check_token_present(resp2, csrf_cookie.value) + def test_process_response_no_csrf_cookie_view_only_get_token_used(self): """ When no prior CSRF cookie exists, check that the cookie is created, even @@ -279,6 +296,15 @@ class CsrfMiddlewareTest(TestCase): resp = token_view(req) self._check_token_present(resp) + def test_get_token_for_exempt_view(self): + """ + Check that get_token still works for a view decorated with 'csrf_view_exempt'. + """ + req = self._get_GET_csrf_cookie_request() + CsrfViewMiddleware().process_view(req, csrf_view_exempt(token_view), (), {}) + resp = token_view(req) + self._check_token_present(resp) + def test_token_node_with_new_csrf_cookie(self): """ Check that CsrfTokenNode works when a CSRF cookie is created by