Made CSRF middleware skip post-processing for 'csrf_exempt' decorated views.
This commit also decomposes the decorator into two decorators which can be used separately, adds some tests, updates docs and fixes some code comments. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9815 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
fffade6633
commit
9a2e338107
|
@ -65,9 +65,12 @@ class CsrfResponseMiddleware(object):
|
||||||
session.
|
session.
|
||||||
"""
|
"""
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
|
if getattr(response, 'csrf_exempt', False):
|
||||||
|
return response
|
||||||
|
|
||||||
csrf_token = None
|
csrf_token = None
|
||||||
try:
|
try:
|
||||||
# This covers a corner case in which the outgoing request
|
# This covers a corner case in which the outgoing response
|
||||||
# both contains a form and sets a session cookie. This
|
# both contains a form and sets a session cookie. This
|
||||||
# really should not be needed, since it is best if views
|
# really should not be needed, since it is best if views
|
||||||
# that create a new session (login pages) also do a
|
# that create a new session (login pages) also do a
|
||||||
|
@ -123,13 +126,35 @@ class CsrfMiddleware(CsrfViewMiddleware, CsrfResponseMiddleware):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def csrf_response_exempt(view_func):
|
||||||
|
"""
|
||||||
|
Modifies a view function so that its response is exempt
|
||||||
|
from the post-processing of the CSRF middleware.
|
||||||
|
"""
|
||||||
|
def wrapped_view(*args, **kwargs):
|
||||||
|
resp = view_func(*args, **kwargs)
|
||||||
|
resp.csrf_exempt = True
|
||||||
|
return resp
|
||||||
|
return wraps(view_func)(wrapped_view)
|
||||||
|
|
||||||
|
def csrf_view_exempt(view_func):
|
||||||
|
"""
|
||||||
|
Marks a view function as being exempt from CSRF view protection.
|
||||||
|
"""
|
||||||
|
# We could just do view_func.csrf_exempt = True, but decorators
|
||||||
|
# are nicer if they don't have side-effects, so we return a new
|
||||||
|
# function.
|
||||||
|
def wrapped_view(*args, **kwargs):
|
||||||
|
return view_func(*args, **kwargs)
|
||||||
|
wrapped_view.csrf_exempt = True
|
||||||
|
return wraps(view_func)(wrapped_view)
|
||||||
|
|
||||||
def csrf_exempt(view_func):
|
def csrf_exempt(view_func):
|
||||||
"""
|
"""
|
||||||
Marks a view function as being exempt from the CSRF checks
|
Marks a view function as being exempt from the CSRF checks
|
||||||
|
and post processing.
|
||||||
|
|
||||||
|
This is the same as using both the csrf_exempt_view and
|
||||||
|
csrf_exempt_response decorators.
|
||||||
"""
|
"""
|
||||||
def wrapped_view(*args, **kwargs):
|
return csrf_response_exempt(csrf_view_exempt(view_func))
|
||||||
return view_func(*args, **kwargs)
|
|
||||||
# We could just do view.csrf_exempt = True, but decorators are
|
|
||||||
# nicer if they don't have side-effects.
|
|
||||||
wrapped_view.csrf_exempt = True
|
|
||||||
return wraps(view_func)(wrapped_view)
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
"""
|
"""
|
||||||
req = self._get_GET_no_session_request()
|
req = self._get_GET_no_session_request()
|
||||||
resp = self._get_post_form_response()
|
resp = self._get_post_form_response()
|
||||||
resp_content = resp.content
|
resp_content = resp.content # needed because process_response modifies resp
|
||||||
resp2 = CsrfMiddleware().process_response(req, resp)
|
resp2 = CsrfMiddleware().process_response(req, resp)
|
||||||
self.assertEquals(resp_content, resp2.content)
|
self.assertEquals(resp_content, resp2.content)
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
"""
|
"""
|
||||||
req = self._get_GET_session_request()
|
req = self._get_GET_session_request()
|
||||||
resp = self._get_post_form_response()
|
resp = self._get_post_form_response()
|
||||||
resp_content = resp.content
|
resp_content = resp.content # needed because process_response modifies resp
|
||||||
resp2 = CsrfMiddleware().process_response(req, resp)
|
resp2 = CsrfMiddleware().process_response(req, resp)
|
||||||
self.assertNotEqual(resp_content, resp2.content)
|
self.assertNotEqual(resp_content, resp2.content)
|
||||||
self._check_token_present(resp2)
|
self._check_token_present(resp2)
|
||||||
|
@ -84,11 +84,21 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
"""
|
"""
|
||||||
req = self._get_GET_no_session_request() # no session in request
|
req = self._get_GET_no_session_request() # no session in request
|
||||||
resp = self._get_new_session_response() # but new session started
|
resp = self._get_new_session_response() # but new session started
|
||||||
resp_content = resp.content
|
resp_content = resp.content # needed because process_response modifies resp
|
||||||
resp2 = CsrfMiddleware().process_response(req, resp)
|
resp2 = CsrfMiddleware().process_response(req, resp)
|
||||||
self.assertNotEqual(resp_content, resp2.content)
|
self.assertNotEqual(resp_content, resp2.content)
|
||||||
self._check_token_present(resp2)
|
self._check_token_present(resp2)
|
||||||
|
|
||||||
|
def test_process_response_exempt_view(self):
|
||||||
|
"""
|
||||||
|
Check that no post processing is done for an exempt view
|
||||||
|
"""
|
||||||
|
req = self._get_POST_session_request()
|
||||||
|
resp = csrf_exempt(self.get_view())(req)
|
||||||
|
resp_content = resp.content
|
||||||
|
resp2 = CsrfMiddleware().process_response(req, resp)
|
||||||
|
self.assertEquals(resp_content, resp2.content)
|
||||||
|
|
||||||
# Check the request processing
|
# Check the request processing
|
||||||
def test_process_request_no_session(self):
|
def test_process_request_no_session(self):
|
||||||
"""
|
"""
|
||||||
|
@ -126,7 +136,7 @@ class CsrfMiddlewareTest(TestCase):
|
||||||
|
|
||||||
def test_ajax_exemption(self):
|
def test_ajax_exemption(self):
|
||||||
"""
|
"""
|
||||||
Check the AJAX requests are automatically exempted.
|
Check that AJAX requests are automatically exempted.
|
||||||
"""
|
"""
|
||||||
req = self._get_POST_session_request()
|
req = self._get_POST_session_request()
|
||||||
req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
||||||
|
|
|
@ -29,6 +29,16 @@ list. It also must process the response before things like compression
|
||||||
happen to the response, so it must come after GZipMiddleware in the
|
happen to the response, so it must come after GZipMiddleware in the
|
||||||
list.
|
list.
|
||||||
|
|
||||||
|
The ``CsrfMiddleware`` class is actually composed of two middleware:
|
||||||
|
``CsrfViewMiddleware`` which performs the checks on incoming requests,
|
||||||
|
and ``CsrfResponseMiddleware`` which performs post-processing of the
|
||||||
|
result. This allows the individual components to be used and/or
|
||||||
|
replaced instead of using ``CsrfMiddleware``.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.1
|
||||||
|
(previous versions of Django did not provide these two components
|
||||||
|
of ``CsrfMiddleware`` as described above)
|
||||||
|
|
||||||
Exceptions
|
Exceptions
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -44,9 +54,16 @@ the ``django.contrib.csrf.middleware`` module. For example::
|
||||||
return HttpResponse('Hello world')
|
return HttpResponse('Hello world')
|
||||||
my_view = csrf_exempt(my_view)
|
my_view = csrf_exempt(my_view)
|
||||||
|
|
||||||
You don't have to worry about doing this for most AJAX views. Any request sent
|
Like the middleware itself, the ``csrf_exempt`` decorator is composed
|
||||||
with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the next
|
of two parts: a ``csrf_view_exempt`` decorator and a
|
||||||
section.)
|
``csrf_response_exempt`` decorator, found in the same module. These
|
||||||
|
disable the view protection mechanism (``CsrfViewMiddleware``) and the
|
||||||
|
response post-processing (``CsrfResponseMiddleware``) respectively.
|
||||||
|
They can be used individually if required.
|
||||||
|
|
||||||
|
You don't have to worry about doing this for most AJAX views. Any
|
||||||
|
request sent with "X-Requested-With: XMLHttpRequest" is automatically
|
||||||
|
exempt. (See the next section.)
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
============
|
============
|
||||||
|
@ -58,10 +75,12 @@ CsrfMiddleware does two things:
|
||||||
a hash of the session ID plus a secret. If there is no session ID set,
|
a hash of the session ID plus a secret. If there is no session ID set,
|
||||||
this modification of the response isn't done, so there is very little
|
this modification of the response isn't done, so there is very little
|
||||||
performance penalty for those requests that don't have a session.
|
performance penalty for those requests that don't have a session.
|
||||||
|
(This is done by ``CsrfResponseMiddleware``).
|
||||||
|
|
||||||
2. On all incoming POST requests that have the session cookie set, it
|
2. On all incoming POST requests that have the session cookie set, it
|
||||||
checks that the 'csrfmiddlewaretoken' is present and correct. If it
|
checks that the 'csrfmiddlewaretoken' is present and correct. If it
|
||||||
isn't, the user will get a 403 error.
|
isn't, the user will get a 403 error. (This is done by
|
||||||
|
``CsrfViewMiddleware``)
|
||||||
|
|
||||||
This ensures that only forms that have originated from your Web site
|
This ensures that only forms that have originated from your Web site
|
||||||
can be used to POST data back.
|
can be used to POST data back.
|
||||||
|
@ -87,14 +106,6 @@ be added by using ``XMLHttpRequest``, and browsers already implement a
|
||||||
same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you
|
same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you
|
||||||
don't trust content within the same domain or subdomains.)
|
don't trust content within the same domain or subdomains.)
|
||||||
|
|
||||||
The above two functions of ``CsrfMiddleware`` are split between two
|
|
||||||
classes: ``CsrfResponseMiddleware`` and ``CsrfViewMiddleware``
|
|
||||||
respectively. This allows the individual components to be used and/or
|
|
||||||
replaced instead of using ``CsrfMiddleware``.
|
|
||||||
|
|
||||||
.. versionchanged:: 1.1
|
|
||||||
(previous versions of Django did not provide these two components
|
|
||||||
of ``CsrfMiddleware`` as described above)
|
|
||||||
|
|
||||||
.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
|
.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue