Fixed #15354 - provide method to ensure CSRF token is always available for AJAX requests
Thanks to sayane for the report. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16192 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
e9342e9b32
commit
b6c5f8060d
|
@ -1,6 +1,6 @@
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.middleware.csrf import CsrfViewMiddleware
|
from django.middleware.csrf import CsrfViewMiddleware, get_token
|
||||||
from django.utils.decorators import decorator_from_middleware, available_attrs
|
from django.utils.decorators import decorator_from_middleware, available_attrs
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
@ -28,6 +28,26 @@ RequestContext, but without the CSRF protection that csrf_protect
|
||||||
enforces.
|
enforces.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class _EnsureCsrfCookie(CsrfViewMiddleware):
|
||||||
|
def _reject(self, request, reason):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||||
|
retval = super(_EnsureCsrfCookie, self).process_view(request, callback, callback_args, callback_kwargs)
|
||||||
|
# Forces process_response to send the cookie
|
||||||
|
get_token(request)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
|
||||||
|
ensure_csrf_cookie = decorator_from_middleware(_EnsureCsrfCookie)
|
||||||
|
ensure_csrf_cookie.__name__ = 'ensure_csrf_cookie'
|
||||||
|
ensure_csrf_cookie.__doc__ = """
|
||||||
|
Use this decorator to ensure that a view sets a CSRF cookie, whether or not it
|
||||||
|
uses the csrf_token template tag, or the CsrfViewMiddleware is used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def csrf_response_exempt(view_func):
|
def csrf_response_exempt(view_func):
|
||||||
"""
|
"""
|
||||||
Modifies a view function so that its response is exempt
|
Modifies a view function so that its response is exempt
|
||||||
|
|
|
@ -132,6 +132,10 @@ The above code could be simplified by using the `jQuery cookie plugin
|
||||||
`settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in jQuery 1.5 and
|
`settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in jQuery 1.5 and
|
||||||
later to replace ``sameOrigin``.
|
later to replace ``sameOrigin``.
|
||||||
|
|
||||||
|
In addition, if the CSRF cookie has not been sent to the client by use of
|
||||||
|
:ttag:`csrf_token`, you may need to ensure the client receives the cookie by
|
||||||
|
using :func:`~django.views.decorators.csrf.ensure_csrf_cookie`.
|
||||||
|
|
||||||
The decorator method
|
The decorator method
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -328,6 +332,10 @@ Utilities
|
||||||
# ...
|
# ...
|
||||||
return render(request, "a_template.html", c)
|
return render(request, "a_template.html", c)
|
||||||
|
|
||||||
|
.. function:: ensure_csrf_cookie(view)
|
||||||
|
|
||||||
|
This decorator forces a view to send the CSRF cookie.
|
||||||
|
|
||||||
Scenarios
|
Scenarios
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -381,6 +389,15 @@ path within it that needs protection. Example::
|
||||||
else:
|
else:
|
||||||
do_something_else()
|
do_something_else()
|
||||||
|
|
||||||
|
Page uses AJAX without any HTML form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A page makes a POST request via AJAX, and the page does not have an HTML form
|
||||||
|
with a :ttag:`csrf_token` that would cause the required CSRF cookie to be sent.
|
||||||
|
|
||||||
|
Solution: use :func:`~django.views.decorators.csrf.ensure_csrf_cookie` on the
|
||||||
|
view that sends the page.
|
||||||
|
|
||||||
Contrib and reusable apps
|
Contrib and reusable apps
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import warnings
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.middleware.csrf import CsrfViewMiddleware
|
from django.middleware.csrf import CsrfViewMiddleware
|
||||||
from django.views.decorators.csrf import csrf_exempt, requires_csrf_token
|
from django.views.decorators.csrf import csrf_exempt, requires_csrf_token, ensure_csrf_cookie
|
||||||
from django.core.context_processors import csrf
|
from django.core.context_processors import csrf
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template import RequestContext, Template
|
from django.template import RequestContext, Template
|
||||||
|
@ -249,3 +249,35 @@ class CsrfViewMiddlewareTest(TestCase):
|
||||||
req.META['HTTP_REFERER'] = 'https://www.example.com'
|
req.META['HTTP_REFERER'] = 'https://www.example.com'
|
||||||
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
|
||||||
self.assertEqual(None, req2)
|
self.assertEqual(None, req2)
|
||||||
|
|
||||||
|
def test_ensures_csrf_cookie_no_middleware(self):
|
||||||
|
"""
|
||||||
|
Tests that ensures_csrf_cookie decorator fulfils its promise
|
||||||
|
with no middleware
|
||||||
|
"""
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def view(request):
|
||||||
|
# Doesn't insert a token or anything
|
||||||
|
return HttpResponse(content="")
|
||||||
|
|
||||||
|
req = self._get_GET_no_csrf_cookie_request()
|
||||||
|
resp = view(req)
|
||||||
|
self.assertTrue(resp.cookies.get(settings.CSRF_COOKIE_NAME, False))
|
||||||
|
self.assertTrue('Cookie' in resp.get('Vary',''))
|
||||||
|
|
||||||
|
def test_ensures_csrf_cookie_with_middleware(self):
|
||||||
|
"""
|
||||||
|
Tests that ensures_csrf_cookie decorator fulfils its promise
|
||||||
|
with the middleware enabled.
|
||||||
|
"""
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def view(request):
|
||||||
|
# Doesn't insert a token or anything
|
||||||
|
return HttpResponse(content="")
|
||||||
|
|
||||||
|
req = self._get_GET_no_csrf_cookie_request()
|
||||||
|
CsrfViewMiddleware().process_view(req, view, (), {})
|
||||||
|
resp = view(req)
|
||||||
|
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||||
|
self.assertTrue(resp2.cookies.get(settings.CSRF_COOKIE_NAME, False))
|
||||||
|
self.assertTrue('Cookie' in resp2.get('Vary',''))
|
||||||
|
|
Loading…
Reference in New Issue