From 61074353864293d3ea51e51e09cf4deedee12963 Mon Sep 17 00:00:00 2001 From: Bouke Haarsma Date: Sat, 2 Nov 2013 09:30:39 +0100 Subject: [PATCH] Fixed #21324 -- Translate CSRF failure view Thanks to Claude Paroz for the original patch. --- django/middleware/csrf.py | 5 +- django/views/csrf.py | 45 +++++++++++------- .../locale/nl/LC_MESSAGES/django.mo | Bin 0 -> 515 bytes .../locale/nl/LC_MESSAGES/django.po | 25 ++++++++++ tests/view_tests/tests/test_csrf.py | 33 +++++++++++++ 5 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 tests/view_tests/locale/nl/LC_MESSAGES/django.mo create mode 100644 tests/view_tests/locale/nl/LC_MESSAGES/django.po create mode 100644 tests/view_tests/tests/test_csrf.py diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 1089153538..46b1891615 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -92,8 +92,7 @@ class CsrfViewMiddleware(object): return None def _reject(self, request, reason): - logger.warning('Forbidden (%s): %s', - reason, request.path, + logger.warning('Forbidden (%s): %s', reason, request.path, extra={ 'status_code': 403, 'request': request, @@ -184,7 +183,7 @@ class CsrfViewMiddleware(object): return response # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was - # never called, probaby because a request middleware returned a response + # never called, probably because a request middleware returned a response # (for example, contrib.auth redirecting to a login page). if request.META.get("CSRF_COOKIE") is None: return response diff --git a/django/views/csrf.py b/django/views/csrf.py index c95d19d56d..f942917e4b 100644 --- a/django/views/csrf.py +++ b/django/views/csrf.py @@ -1,11 +1,16 @@ +from django.conf import settings from django.http import HttpResponseForbidden from django.template import Context, Template -from django.conf import settings +from django.utils.translation import ugettext as _ # We include the template inline since we need to be able to reliably display # this error message, especially for the sake of developers, and there isn't any # other way of making it available independent of what is in the settings file. +# Only the text appearing with DEBUG=False is translated. Normal translation +# tags cannot be used with this inline templates as makemessages would not be +# able to discover the strings. + CSRF_FAILURE_TEMPLATE = """ @@ -30,17 +35,11 @@ CSRF_FAILURE_TEMPLATE = """
-

Forbidden (403)

-

CSRF verification failed. Request aborted.

+

{{ title }} (403)

+

{{ main }}

{% if no_referer %} -

You are seeing this message because this HTTPS site requires a 'Referer - header' to be sent by your Web browser, but none was sent. This header is - required for security reasons, to ensure that your browser is not being - hijacked by third parties.

- -

If you have configured your browser to disable 'Referer' headers, please - re-enable them, at least for this site, or for HTTPS connections, or for - 'same-origin' requests.

+

{{ no_referer1 }}

+

{{ no_referer2 }}

{% endif %}
{% if DEBUG %} @@ -84,21 +83,35 @@ CSRF_FAILURE_TEMPLATE = """ {% else %}
-

More information is available with DEBUG=True.

+

{{ more }}

{% endif %} """ + def csrf_failure(request, reason=""): """ Default view used when request fails CSRF protection """ from django.middleware.csrf import REASON_NO_REFERER t = Template(CSRF_FAILURE_TEMPLATE) - c = Context({'DEBUG': settings.DEBUG, - 'reason': reason, - 'no_referer': reason == REASON_NO_REFERER - }) + c = Context({ + 'title': _("Forbidden"), + 'main': _("CSRF verification failed. Request aborted."), + 'reason': reason, + 'no_referer': reason == REASON_NO_REFERER, + 'no_referer1': _( + "You are seeing this message because this HTTPS site requires a " + "'Referer header' to be sent by your Web browser, but none was " + "sent. This header is required for security reasons, to ensure " + "that your browser is not being hijacked by third parties."), + 'no_referer2': _( + "If you have configured your browser to disable 'Referer' headers, " + "please re-enable them, at least for this site, or for HTTPS " + "connections, or for 'same-origin' requests."), + 'DEBUG': settings.DEBUG, + 'more': _("More information is available with DEBUG=True."), + }) return HttpResponseForbidden(t.render(c), content_type='text/html') diff --git a/tests/view_tests/locale/nl/LC_MESSAGES/django.mo b/tests/view_tests/locale/nl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..90066d70ecfea30c963789239a707ad531d60059 GIT binary patch literal 515 zcmYL_!A{#i5Qbe^B=C`Q4|4{Mu>(=TP=uQ}D5{-Aj>DOCJTcpt-E_S{EziKUJQdH- z2kEKfNRZKfn$_r^+1dZ^!KXhC$4BNfbIR;83Dd?MbI1s0pLu4w9~|d}GcvDy-{bs` zGaqM%d5dk!<@ILDr$%~RMHmO|+G{ulJm zG3Y{Y9tPbbpDQfHj2>k>{9PCL=yx&bK+p+K1N2W=mx#t!{z);_38?rK(zsiZCbf5JD1-Zf5Pumq~J6OHZ5X%1wu9y!}+*`EE}uON!t4 zz2app{j$faT1a&P^Lt?$vR`NU0ME9%c9#VijMZG1QdJ?G71FvJt8r^J!Gmn-_0oF4 WCO&Cef>=~k7`-IrIb17rYxaMTYm1ct literal 0 HcmV?d00001 diff --git a/tests/view_tests/locale/nl/LC_MESSAGES/django.po b/tests/view_tests/locale/nl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..e85f08ee80 --- /dev/null +++ b/tests/view_tests/locale/nl/LC_MESSAGES/django.po @@ -0,0 +1,25 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-15 19:15+0200\n" +"PO-Revision-Date: 2010-05-12 12:41-0300\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: views/csrf.py:98 +msgid "Forbidden" +msgstr "Verboden" + +#: views/csrf.py:99 +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF-verificatie mislukt. Verzoek afgebroken." diff --git a/tests/view_tests/tests/test_csrf.py b/tests/view_tests/tests/test_csrf.py new file mode 100644 index 0000000000..8e5c7f950a --- /dev/null +++ b/tests/view_tests/tests/test_csrf.py @@ -0,0 +1,33 @@ +from django.test import TestCase, override_settings, Client +from django.utils.translation import override + + +class CsrfViewTests(TestCase): + urls = "view_tests.urls" + + @override_settings( + USE_I18N=True, + MIDDLEWARE_CLASSES=( + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + ), + ) + def test_translation(self): + """ + Test that an invalid request is rejected with a localized error message. + """ + self.client = Client(enforce_csrf_checks=True) + + response = self.client.post('/', HTTP_HOST='www.example.com') + self.assertContains(response, "Forbidden", status_code=403) + self.assertContains(response, + "CSRF verification failed. Request aborted.", + status_code=403) + + with self.settings(LANGUAGE_CODE='nl'), override('en-us'): + response = self.client.post('/', HTTP_HOST='www.example.com') + self.assertContains(response, "Verboden", status_code=403) + self.assertContains(response, + "CSRF-verificatie mislukt. Verzoek afgebroken.", + status_code=403)