Commit 7f3eda2f authored by Luke Plant's avatar Luke Plant
Browse files

[1.3.X] Fixed #16004 - csrf_protect does not send cookie if view returns TemplateResponse

The root bug was in decorator_from_middleware, and the fix also corrects
bugs with gzip_page and other decorators.

Backport of [16276] from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.3.X@16279 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent afa09285
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -133,7 +133,7 @@ class BaseHandler(object):
                if hasattr(response, 'render') and callable(response.render):
                    for middleware_method in self._template_response_middleware:
                        response = middleware_method(request, response)
                    response.render()
                    response = response.render()

            except http.Http404, e:
                logger.warning('Not Found: %s' % request.path,
+5 −2
Original line number Diff line number Diff line
@@ -92,11 +92,14 @@ class SimpleTemplateResponse(HttpResponse):

        Returns the baked response instance.
        """
        retval = self
        if not self._is_rendered:
            self._set_content(self.rendered_content)
            for post_callback in self._post_render_callbacks:
                post_callback(self)
        return self
                newretval = post_callback(retval)
                if newretval is not None:
                    retval = newretval
        return retval

    is_rendered = property(lambda self: self._is_rendered)

+11 −4
Original line number Diff line number Diff line
@@ -97,10 +97,17 @@ def make_middleware_decorator(middleware_class):
                        if result is not None:
                            return result
                    raise
                if hasattr(response, 'render') and callable(response.render):
                    if hasattr(middleware, 'process_template_response'):
                        response = middleware.process_template_response(request, response)
                    # Defer running of process_response until after the template
                    # has been rendered:
                    if hasattr(middleware, 'process_response'):
                    result = middleware.process_response(request, response)
                    if result is not None:
                        return result
                        callback = lambda response: middleware.process_response(request, response)
                        response.add_post_render_callback(callback)
                else:
                    if hasattr(middleware, 'process_response'):
                        return middleware.process_response(request, response)
                return response
            return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view)
        return _decorator
+4 −0
Original line number Diff line number Diff line
@@ -119,6 +119,10 @@ Methods
    rendered :class:`~django.template.response.SimpleTemplateResponse`
    instance.

    If the callback returns a value that is not `None`, this will be
    used as the response instead of the original response object (and
    will be passed to the next post rendering callback etc.)

.. method:: SimpleTemplateResponse.render():

    Sets :attr:`response.content` to the result obtained by
+67 −0
Original line number Diff line number Diff line
from django.http import HttpResponse
from django.middleware.doc import XViewMiddleware
from django.template import Template, Context
from django.template.response import TemplateResponse
from django.test import TestCase, RequestFactory
from django.utils.decorators import decorator_from_middleware

@@ -19,6 +21,26 @@ class ClassXView(object):
class_xview = xview_dec(ClassXView())


class FullMiddleware(object):
    def process_request(self, request):
        request.process_request_reached = True

    def process_view(sef, request, view_func, view_args, view_kwargs):
        request.process_view_reached = True

    def process_template_response(self, request, response):
        request.process_template_response_reached = True
        return response

    def process_response(self, request, response):
        # This should never receive unrendered content.
        request.process_response_content = response.content
        request.process_response_reached = True
        return response

full_dec = decorator_from_middleware(FullMiddleware)


class DecoratorFromMiddlewareTests(TestCase):
    """
    Tests for view decorators created using
@@ -37,3 +59,48 @@ class DecoratorFromMiddlewareTests(TestCase):
        Test a middleware that implements process_view, operating on a callable class.
        """
        class_xview(self.rf.get('/'))

    def test_full_dec_normal(self):
        """
        Test that all methods of middleware are called for normal HttpResponses
        """

        @full_dec
        def normal_view(request):
            t = Template("Hello world")
            return HttpResponse(t.render(Context({})))

        request = self.rf.get('/')
        response = normal_view(request)
        self.assertTrue(getattr(request, 'process_request_reached', False))
        self.assertTrue(getattr(request, 'process_view_reached', False))
        # process_template_response must not be called for HttpResponse
        self.assertFalse(getattr(request, 'process_template_response_reached', False))
        self.assertTrue(getattr(request, 'process_response_reached', False))

    def test_full_dec_templateresponse(self):
        """
        Test that all methods of middleware are called for TemplateResponses in
        the right sequence.
        """

        @full_dec
        def template_response_view(request):
            t = Template("Hello world")
            return TemplateResponse(request, t, {})

        request = self.rf.get('/')
        response = template_response_view(request)
        self.assertTrue(getattr(request, 'process_request_reached', False))
        self.assertTrue(getattr(request, 'process_view_reached', False))
        self.assertTrue(getattr(request, 'process_template_response_reached', False))
        # response must not be rendered yet.
        self.assertFalse(response._is_rendered)
        # process_response must not be called until after response is rendered,
        # otherwise some decorators like csrf_protect and gzip_page will not
        # work correctly. See #16004
        self.assertFalse(getattr(request, 'process_response_reached', False))
        response.render()
        self.assertTrue(getattr(request, 'process_response_reached', False))
        # Check that process_response saw the rendered content
        self.assertEqual(request.process_response_content, "Hello world")