Fixed #25695 -- Added template_name parameter to csrf_failure() view.

This commit is contained in:
Raphael Michel 2015-11-07 14:18:06 +01:00 committed by Tim Graham
parent 20d2778597
commit 16945f0e9c
4 changed files with 55 additions and 5 deletions

View File

@ -1,6 +1,6 @@
from django.conf import settings from django.conf import settings
from django.http import HttpResponseForbidden from django.http import HttpResponseForbidden
from django.template import Context, Engine from django.template import Context, Engine, TemplateDoesNotExist, loader
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.version import get_docs_version from django.utils.version import get_docs_version
@ -95,14 +95,14 @@ CSRF_FAILURE_TEMPLATE = """
</body> </body>
</html> </html>
""" """
CSRF_FAILURE_TEMPLATE_NAME = "403_csrf.html"
def csrf_failure(request, reason=""): def csrf_failure(request, reason="", template_name=CSRF_FAILURE_TEMPLATE_NAME):
""" """
Default view used when request fails CSRF protection Default view used when request fails CSRF protection
""" """
from django.middleware.csrf import REASON_NO_REFERER, REASON_NO_CSRF_COOKIE from django.middleware.csrf import REASON_NO_REFERER, REASON_NO_CSRF_COOKIE
t = Engine().from_string(CSRF_FAILURE_TEMPLATE)
c = Context({ c = Context({
'title': _("Forbidden"), 'title': _("Forbidden"),
'main': _("CSRF verification failed. Request aborted."), 'main': _("CSRF verification failed. Request aborted."),
@ -131,4 +131,13 @@ def csrf_failure(request, reason=""):
'docs_version': get_docs_version(), 'docs_version': get_docs_version(),
'more': _("More information is available with DEBUG=True."), 'more': _("More information is available with DEBUG=True."),
}) })
try:
t = loader.get_template(template_name)
except TemplateDoesNotExist:
if template_name == CSRF_FAILURE_TEMPLATE_NAME:
# If the default template doesn't exist, use the string template.
t = Engine().from_string(CSRF_FAILURE_TEMPLATE)
else:
# Raise if a developer-specified template doesn't exist.
raise
return HttpResponseForbidden(t.render(c), content_type='text/html') return HttpResponseForbidden(t.render(c), content_type='text/html')

View File

@ -385,6 +385,15 @@ where ``reason`` is a short message (intended for developers or logging, not for
end users) indicating the reason the request was rejected. See end users) indicating the reason the request was rejected. See
:doc:`/ref/csrf`. :doc:`/ref/csrf`.
``django.views.csrf.csrf_failure()`` accepts an additional ``template_name``
parameter that defaults to ``'403_csrf.html'``. If a template with that name
exists, it will be used to render the page.
.. versionchanged:: 1.10
The ``template_name`` parameter and the behavior of searching for a template
called ``403_csrf.html`` were added to ``csrf_failure()``.
.. setting:: CSRF_HEADER_NAME .. setting:: CSRF_HEADER_NAME
CSRF_HEADER_NAME CSRF_HEADER_NAME

View File

@ -114,7 +114,9 @@ Cache
CSRF CSRF
^^^^ ^^^^
* ... * The default :setting:`CSRF_FAILURE_VIEW`, ``views.csrf.csrf_failure()`` now
accepts an optional ``template_name`` parameter, defaulting to
``'403_csrf.html'``, to control the template used to render the page.
Database backends Database backends
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^

View File

@ -1,5 +1,9 @@
from django.test import Client, SimpleTestCase, override_settings from django.template import TemplateDoesNotExist
from django.test import (
Client, RequestFactory, SimpleTestCase, override_settings,
)
from django.utils.translation import override from django.utils.translation import override
from django.views.csrf import CSRF_FAILURE_TEMPLATE_NAME, csrf_failure
@override_settings(ROOT_URLCONF="view_tests.urls") @override_settings(ROOT_URLCONF="view_tests.urls")
@ -70,3 +74,29 @@ class CsrfViewTests(SimpleTestCase):
""" """
response = self.client.post('/') response = self.client.post('/')
self.assertContains(response, "Forbidden", status_code=403) self.assertContains(response, "Forbidden", status_code=403)
@override_settings(TEMPLATES=[{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'loaders': [
('django.template.loaders.locmem.Loader', {
CSRF_FAILURE_TEMPLATE_NAME: 'Test template for CSRF failure'
}),
],
},
}])
def test_custom_template(self):
"""
A custom CSRF_FAILURE_TEMPLATE_NAME is used.
"""
response = self.client.post('/')
self.assertContains(response, "Test template for CSRF failure", status_code=403)
def test_custom_template_does_not_exist(self):
"""
An exception is raised if a nonexistent template is supplied.
"""
factory = RequestFactory()
request = factory.post('/')
with self.assertRaises(TemplateDoesNotExist):
csrf_failure(request, template_name="nonexistent.html")