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.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.version import get_docs_version
@ -95,14 +95,14 @@ CSRF_FAILURE_TEMPLATE = """
</body>
</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
"""
from django.middleware.csrf import REASON_NO_REFERER, REASON_NO_CSRF_COOKIE
t = Engine().from_string(CSRF_FAILURE_TEMPLATE)
c = Context({
'title': _("Forbidden"),
'main': _("CSRF verification failed. Request aborted."),
@ -131,4 +131,13 @@ def csrf_failure(request, reason=""):
'docs_version': get_docs_version(),
'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')

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
: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
CSRF_HEADER_NAME

View File

@ -114,7 +114,9 @@ Cache
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
^^^^^^^^^^^^^^^^^

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.views.csrf import CSRF_FAILURE_TEMPLATE_NAME, csrf_failure
@override_settings(ROOT_URLCONF="view_tests.urls")
@ -70,3 +74,29 @@ class CsrfViewTests(SimpleTestCase):
"""
response = self.client.post('/')
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")