Loading django/views/csrf.py +12 −3 Original line number Diff line number Diff line 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 Loading Loading @@ -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."), Loading Loading @@ -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') docs/ref/settings.txt +9 −0 Original line number Diff line number Diff line Loading @@ -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 Loading docs/releases/1.10.txt +3 −1 Original line number Diff line number Diff line Loading @@ -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 ^^^^^^^^^^^^^^^^^ Loading tests/view_tests/tests/test_csrf.py +31 −1 Original line number Diff line number Diff line 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") Loading Loading @@ -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") Loading
django/views/csrf.py +12 −3 Original line number Diff line number Diff line 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 Loading Loading @@ -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."), Loading Loading @@ -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')
docs/ref/settings.txt +9 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
docs/releases/1.10.txt +3 −1 Original line number Diff line number Diff line Loading @@ -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 ^^^^^^^^^^^^^^^^^ Loading
tests/view_tests/tests/test_csrf.py +31 −1 Original line number Diff line number Diff line 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") Loading Loading @@ -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")