diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 8891a44109..6d16a92bf5 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -40,15 +40,17 @@ def _get_new_csrf_key(): def get_token(request): """ Returns the CSRF token required for a POST form. The token is an - alphanumeric value. + alphanumeric value. A new token is created if one is not already set. A side effect of calling this function is to make the csrf_protect decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie' header to the outgoing response. For this reason, you may need to use this function lazily, as is done by the csrf context processor. """ + if "CSRF_COOKIE" not in request.META: + request.META["CSRF_COOKIE"] = _get_new_csrf_key() request.META["CSRF_COOKIE_USED"] = True - return request.META.get("CSRF_COOKIE", None) + return request.META["CSRF_COOKIE"] def rotate_token(request): @@ -112,9 +114,6 @@ class CsrfViewMiddleware(object): request.META['CSRF_COOKIE'] = csrf_token except KeyError: csrf_token = None - # Generate token and store it in the request, so it's - # available to the view. - request.META["CSRF_COOKIE"] = _get_new_csrf_key() # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works @@ -194,12 +193,6 @@ class CsrfViewMiddleware(object): if getattr(response, 'csrf_processing_done', False): return response - # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was - # never called, probably because a request middleware returned a response - # (for example, contrib.auth redirecting to a login page). - if request.META.get("CSRF_COOKIE") is None: - return response - if not request.META.get("CSRF_COOKIE_USED", False): return response diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index 5ebe9f2938..033d083e18 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -23,7 +23,7 @@ from django.core import mail from django.core.urlresolvers import NoReverseMatch, reverse, reverse_lazy from django.db import connection from django.http import HttpRequest, QueryDict -from django.middleware.csrf import CsrfViewMiddleware +from django.middleware.csrf import CsrfViewMiddleware, get_token from django.test import ( TestCase, ignore_warnings, modify_settings, override_settings, ) @@ -606,7 +606,8 @@ class LoginTest(AuthViewsTestCase): # TestClient isn't used here as we're testing middleware, essentially. req = HttpRequest() CsrfViewMiddleware().process_view(req, login_view, (), {}) - req.META["CSRF_COOKIE_USED"] = True + # get_token() triggers CSRF token inclusion in the response + get_token(req) resp = login_view(req) resp2 = CsrfViewMiddleware().process_response(req, resp) csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None) diff --git a/tests/csrf_tests/tests.py b/tests/csrf_tests/tests.py index 7469da34b2..29ce2c170c 100644 --- a/tests/csrf_tests/tests.py +++ b/tests/csrf_tests/tests.py @@ -5,7 +5,9 @@ import logging from django.conf import settings from django.http import HttpRequest, HttpResponse -from django.middleware.csrf import CSRF_KEY_LENGTH, CsrfViewMiddleware +from django.middleware.csrf import ( + CSRF_KEY_LENGTH, CsrfViewMiddleware, get_token, +) from django.template import RequestContext, Template from django.template.context_processors import csrf from django.test import TestCase, override_settings @@ -237,7 +239,10 @@ class CsrfViewMiddlewareTest(TestCase): """ req = self._get_GET_no_csrf_cookie_request() resp = token_view(req) - self.assertEqual(resp.content, b'') + + token = get_token(req) + self.assertIsNotNone(token) + self._check_token_present(resp, token) def test_token_node_empty_csrf_cookie(self): """ @@ -248,7 +253,9 @@ class CsrfViewMiddlewareTest(TestCase): CsrfViewMiddleware().process_view(req, token_view, (), {}) resp = token_view(req) - self.assertNotEqual("", resp.content) + token = get_token(req) + self.assertIsNotNone(token) + self._check_token_present(resp, token) def test_token_node_with_csrf_cookie(self): """