Fixed #14565 - No csrf_token on 404 page.
This solution doesn't have the negative side-effects of [14356]. git-svn-id: http://code.djangoproject.com/svn/django/trunk@14377 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
144ab8877f
commit
90ac02300e
|
@ -88,18 +88,22 @@ class CsrfViewMiddleware(object):
|
|||
This middleware should be used in conjunction with the csrf_token template
|
||||
tag.
|
||||
"""
|
||||
# The _accept and _reject methods currently only exist for the sake of the
|
||||
# requires_csrf_token decorator.
|
||||
def _accept(self, request):
|
||||
# Avoid checking the request twice by adding a custom attribute to
|
||||
# request. This will be relevant when both decorator and middleware
|
||||
# are used.
|
||||
request.csrf_processing_done = True
|
||||
return None
|
||||
|
||||
def _reject(self, request, reason):
|
||||
return _get_failure_view()(request, reason=reason)
|
||||
|
||||
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||
if getattr(request, 'csrf_processing_done', False):
|
||||
return None
|
||||
|
||||
reject = lambda s: _get_failure_view()(request, reason=s)
|
||||
def accept():
|
||||
# Avoid checking the request twice by adding a custom attribute to
|
||||
# request. This will be relevant when both decorator and middleware
|
||||
# are used.
|
||||
request.csrf_processing_done = True
|
||||
return None
|
||||
|
||||
# If the user doesn't have a CSRF cookie, generate one and store it in the
|
||||
# request, so it's available to the view. We'll store it in a cookie when
|
||||
# we reach the response.
|
||||
|
@ -128,7 +132,7 @@ class CsrfViewMiddleware(object):
|
|||
# the creation of CSRF cookies, so that everything else continues to
|
||||
# work exactly the same (e.g. cookies are sent etc), but before the
|
||||
# any branches that call reject()
|
||||
return accept()
|
||||
return self._accept(request)
|
||||
|
||||
if request.is_ajax():
|
||||
# .is_ajax() is based on the presence of X-Requested-With. In
|
||||
|
@ -153,7 +157,7 @@ class CsrfViewMiddleware(object):
|
|||
# allowing the cross-domain POST request.
|
||||
#
|
||||
# So in all cases, it is safe to allow these requests through.
|
||||
return accept()
|
||||
return self._accept(request)
|
||||
|
||||
if request.is_secure():
|
||||
# Suppose user visits http://example.com/
|
||||
|
@ -179,7 +183,7 @@ class CsrfViewMiddleware(object):
|
|||
'request': request,
|
||||
}
|
||||
)
|
||||
return reject(REASON_NO_REFERER)
|
||||
return self._reject(request, REASON_NO_REFERER)
|
||||
|
||||
# The following check ensures that the referer is HTTPS,
|
||||
# the domains match and the ports match - the same origin policy.
|
||||
|
@ -192,7 +196,7 @@ class CsrfViewMiddleware(object):
|
|||
'request': request,
|
||||
}
|
||||
)
|
||||
return reject(reason)
|
||||
return self._reject(request, reason)
|
||||
|
||||
# If the user didn't already have a CSRF cookie, then fall back to
|
||||
# the Django 1.1 method (hash of session ID), so a request is not
|
||||
|
@ -212,7 +216,7 @@ class CsrfViewMiddleware(object):
|
|||
'request': request,
|
||||
}
|
||||
)
|
||||
return reject(REASON_NO_COOKIE)
|
||||
return self._reject(request, REASON_NO_COOKIE)
|
||||
else:
|
||||
csrf_token = request.META["CSRF_COOKIE"]
|
||||
|
||||
|
@ -227,7 +231,7 @@ class CsrfViewMiddleware(object):
|
|||
'request': request,
|
||||
}
|
||||
)
|
||||
return reject(REASON_NO_CSRF_COOKIE)
|
||||
return self._reject(request, REASON_NO_CSRF_COOKIE)
|
||||
else:
|
||||
logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path),
|
||||
extra={
|
||||
|
@ -235,9 +239,9 @@ class CsrfViewMiddleware(object):
|
|||
'request': request,
|
||||
}
|
||||
)
|
||||
return reject(REASON_BAD_TOKEN)
|
||||
return self._reject(request, REASON_BAD_TOKEN)
|
||||
|
||||
return accept()
|
||||
return self._accept(request)
|
||||
|
||||
def process_response(self, request, response):
|
||||
if getattr(response, 'csrf_processing_done', False):
|
||||
|
|
|
@ -14,6 +14,22 @@ CsrfViewMiddleware, but it can be used on a per view basis. Using both, or
|
|||
using the decorator multiple times, is harmless and efficient.
|
||||
"""
|
||||
|
||||
|
||||
class _EnsureCsrfToken(CsrfViewMiddleware):
|
||||
# We need this to behave just like the CsrfViewMiddleware, but not reject
|
||||
# requests.
|
||||
def _reject(self, request, reason):
|
||||
return None
|
||||
|
||||
|
||||
requires_csrf_token = decorator_from_middleware(_EnsureCsrfToken)
|
||||
requires_csrf_token.__name__ = 'requires_csrf_token'
|
||||
csrf_protect.__doc__ = """
|
||||
Use this decorator on views that need a correct csrf_token available to
|
||||
RequestContext, but without the CSRF protection that csrf_protect
|
||||
enforces.
|
||||
"""
|
||||
|
||||
def csrf_response_exempt(view_func):
|
||||
"""
|
||||
Modifies a view function so that its response is exempt
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
from django import http
|
||||
from django.views.decorators.csrf import requires_csrf_token
|
||||
from django.template import Context, RequestContext, loader
|
||||
|
||||
|
||||
# This can be called when CsrfViewMiddleware.process_view has not run, therefore
|
||||
# need @requires_csrf_token in case the template needs {% csrf_token %}.
|
||||
@requires_csrf_token
|
||||
def page_not_found(request, template_name='404.html'):
|
||||
"""
|
||||
Default 404 handler.
|
||||
|
@ -13,6 +18,8 @@ def page_not_found(request, template_name='404.html'):
|
|||
t = loader.get_template(template_name) # You need to create a 404.html template.
|
||||
return http.HttpResponseNotFound(t.render(RequestContext(request, {'request_path': request.path})))
|
||||
|
||||
|
||||
@requires_csrf_token
|
||||
def server_error(request, template_name='500.html'):
|
||||
"""
|
||||
500 error handler.
|
||||
|
@ -23,6 +30,7 @@ def server_error(request, template_name='500.html'):
|
|||
t = loader.get_template(template_name) # You need to create a 500.html template.
|
||||
return http.HttpResponseServerError(t.render(Context({})))
|
||||
|
||||
|
||||
def shortcut(request, content_type_id, object_id):
|
||||
# TODO: Remove this in Django 2.0.
|
||||
# This is a legacy view that depends on the contenttypes framework.
|
||||
|
|
|
@ -4,7 +4,7 @@ import warnings
|
|||
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, csrf_view_exempt
|
||||
from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt, requires_csrf_token
|
||||
from django.core.context_processors import csrf
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.utils.importlib import import_module
|
||||
|
@ -331,6 +331,14 @@ class CsrfMiddlewareTest(TestCase):
|
|||
resp = token_view(req)
|
||||
self._check_token_present(resp)
|
||||
|
||||
def test_get_token_for_requires_csrf_token_view(self):
|
||||
"""
|
||||
Check that get_token works for a view decorated solely with requires_csrf_token
|
||||
"""
|
||||
req = self._get_GET_csrf_cookie_request()
|
||||
resp = requires_csrf_token(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
|
||||
|
|
|
@ -9,6 +9,8 @@ from regressiontests.views.models import Author, Article, UrlArticle
|
|||
class DefaultsTests(TestCase):
|
||||
"""Test django views in django/views/defaults.py"""
|
||||
fixtures = ['testdata.json']
|
||||
non_existing_urls = ['/views/non_existing_url/', # this is in urls.py
|
||||
'/views/other_non_existing_url/'] # this NOT in urls.py
|
||||
|
||||
def test_shortcut_with_absolute_url(self):
|
||||
"Can view a shortcut for an Author object that has a get_absolute_url method"
|
||||
|
@ -49,12 +51,21 @@ class DefaultsTests(TestCase):
|
|||
|
||||
def test_page_not_found(self):
|
||||
"A 404 status is returned by the page_not_found view"
|
||||
non_existing_urls = ['/views/non_existing_url/', # this is in urls.py
|
||||
'/views/other_non_existing_url/'] # this NOT in urls.py
|
||||
for url in non_existing_urls:
|
||||
for url in self.non_existing_urls:
|
||||
response = self.client.get(url)
|
||||
self.assertEquals(response.status_code, 404)
|
||||
|
||||
def test_csrf_token_in_404(self):
|
||||
"""
|
||||
The 404 page should have the csrf_token available in the context
|
||||
"""
|
||||
# See ticket #14565
|
||||
for url in self.non_existing_urls:
|
||||
response = self.client.get(url)
|
||||
csrf_token = response.context['csrf_token']
|
||||
self.assertNotEqual(str(csrf_token), 'NOTPROVIDED')
|
||||
self.assertNotEqual(str(csrf_token), '')
|
||||
|
||||
def test_server_error(self):
|
||||
"The server_error view raises a 500 status"
|
||||
response = self.client.get('/views/server_error/')
|
||||
|
|
Loading…
Reference in New Issue