middleware: Use a proper error code on CSRF failure.

This allows us to reliably parse the error in code, rather than
attempt to parse the error text.  Because the error text gets
translated into the user's language, this error-handling path
wasn't functioning at all for users using Zulip in any of the
seven non-English languages for which we had a translation for
this string.

Together with 709c3b50f which fixed a similar issue in a
different error-handling path, this fixes #5598.
This commit is contained in:
Greg Price 2017-07-24 18:30:13 -07:00 committed by Tim Abbott
parent 388acbbfcb
commit 192ec7c0f6
4 changed files with 19 additions and 4 deletions

View File

@ -192,7 +192,7 @@ function test_with_mock_ajax(test_params) {
test_with_mock_ajax({ test_with_mock_ajax({
xhr: { xhr: {
status: 403, status: 403,
responseText: '{"msg": "CSRF Error: whatever"}', responseText: '{"msg": "CSRF Fehler: etwas", "code": "CSRF_FAILED"}',
}, },
run_code: function () { run_code: function () {

View File

@ -30,7 +30,7 @@ function call(args, idempotent) {
if (xhr.status === 403) { if (xhr.status === 403) {
try { try {
if (JSON.parse(xhr.responseText).msg.indexOf("CSRF Error:") !== -1) { if (JSON.parse(xhr.responseText).code === 'CSRF_FAILED') {
reload.initiate({immediate: true, reload.initiate({immediate: true,
save_pointer: true, save_pointer: true,
save_narrow: true, save_narrow: true,

View File

@ -36,6 +36,7 @@ class ErrorCode(AbstractEnum):
BAD_NARROW = () BAD_NARROW = ()
UNAUTHORIZED_PRINCIPAL = () UNAUTHORIZED_PRINCIPAL = ()
BAD_EVENT_QUEUE_ID = () BAD_EVENT_QUEUE_ID = ()
CSRF_FAILED = ()
class JsonableError(Exception): class JsonableError(Exception):
'''A standardized error format we can turn into a nice JSON HTTP response. '''A standardized error format we can turn into a nice JSON HTTP response.

View File

@ -9,7 +9,7 @@ from django.utils.translation import ugettext as _
from django.utils.deprecation import MiddlewareMixin from django.utils.deprecation import MiddlewareMixin
from zerver.lib.response import json_error, json_response_from_error from zerver.lib.response import json_error, json_response_from_error
from zerver.lib.request import JsonableError from zerver.lib.exceptions import JsonableError, ErrorCode
from django.db import connection from django.db import connection
from django.http import HttpRequest, HttpResponse, StreamingHttpResponse from django.http import HttpRequest, HttpResponse, StreamingHttpResponse
from zerver.lib.utils import statsd, get_subdomain from zerver.lib.utils import statsd, get_subdomain
@ -304,10 +304,24 @@ class TagRequests(MiddlewareMixin):
else: else:
request.error_format = "HTML" request.error_format = "HTML"
class CsrfFailureError(JsonableError):
http_status_code = 403
code = ErrorCode.CSRF_FAILED
data_fields = ['reason']
def __init__(self, reason):
# type: (Text) -> None
self.reason = reason # type: Text
@staticmethod
def msg_format():
# type: () -> None
return _("CSRF Error: {reason}")
def csrf_failure(request, reason=""): def csrf_failure(request, reason=""):
# type: (HttpRequest, Optional[Text]) -> HttpResponse # type: (HttpRequest, Optional[Text]) -> HttpResponse
if request.error_format == "JSON": if request.error_format == "JSON":
return json_error(_("CSRF Error: %s") % (reason,), status=403) return json_response_from_error(CsrfFailureError(reason))
else: else:
return html_csrf_failure(request, reason) return html_csrf_failure(request, reason)