Page.dummy_request() now takes an optional request object argument.

When building a dummy request, you can now pass in the original request object
to add additional information to the dummy. Currently, that includes the
following headers:
REMOTE_ADDR
HTTP_X_FORWARDED_FOR
HTTP_COOKIE
HTTP_USER_AGENT

More may be added later.

This changes ensures that middleware which work on the client IP aren't flumuxed
by its absense, and also makes it possible for previews to be rendered as the
logged in user (they had previously been rendered using an AnnonymousUser).

Because the user's logged in state is now detectable in a Page previews, the
Wagtail userbar now hides itself explicitly during previews, rather than relying
on the fact that previews used to be built with AnonymousUser.
pull/2829/head
Robert Rollins 2016-05-02 13:58:57 -07:00 zatwierdzone przez Matt Westcott
rodzic 14919f3b41
commit cbabc3d9c7
6 zmienionych plików z 52 dodań i 7 usunięć

Wyświetl plik

@ -22,6 +22,7 @@ Changelog
* `wagtailforms.models.AbstractEmailForm` now supports multiple email recipients (Serafeim Papastefanos)
* Added the ``include_block`` template tag for improved StreamField template inclusion (Matt Westcott)
* Added ability to delete users through Settings -> Users (Vincent Audebert; thanks also to Ludolf Takens and Tobias Schmidt for alternative implementations)
* Page previews now pass additional HTTP headers, to simulate the page being viewed by the logged-in user and avoid clashes with middleware (Robert Rollins)
* Fix: Email templates and document uploader now support custom `STATICFILES_STORAGE` (Jonny Scholes)
* Fix: Removed alignment options (deprecated in HTML and not rendered by Wagtail) from `TableBlock` context menu (Moritz Pfeiffer)
* Fix: Fixed incorrect CSS path on ModelAdmin's "choose a parent page" view

Wyświetl plik

@ -37,6 +37,7 @@ Minor features
* ``wagtailforms.models.AbstractEmailForm`` now supports multiple email recipients (Serafeim Papastefanos)
* Added the ``include_block`` template tag for improved StreamField template inclusion. See :doc:`/topics/streamfield` for documentation (Matt Westcott)
* Added ability to delete users through Settings -> Users (Vincent Audebert; thanks also to Ludolf Takens and Tobias Schmidt for alternative implementations)
* Page previews now pass additional HTTP headers, to simulate the page being viewed by the logged-in user and avoid clashes with middleware (Robert Rollins)
Bug fixes

Wyświetl plik

@ -40,6 +40,11 @@ def wagtailuserbar(context, position='bottom-right'):
if not request.user.has_perm('wagtailadmin.access_admin'):
return ''
# Don't render if this is a preview. Since some routes can render the userbar without going through Page.serve(),
# request.is_preview might not be defined.
if getattr(request, 'is_preview', False):
return ''
# Only render if the context contains a variable referencing a saved page
page = get_page_instance(context)
if page is None:

Wyświetl plik

@ -496,7 +496,7 @@ def delete(request, page_id):
def view_draft(request, page_id):
page = get_object_or_404(Page, id=page_id).get_latest_revision_as_page()
return page.serve_preview(page.dummy_request(), page.default_preview_mode)
return page.serve_preview(page.dummy_request(request), page.default_preview_mode)
def preview_on_edit(request, page_id):
@ -516,7 +516,7 @@ def preview_on_edit(request, page_id):
page.full_clean()
preview_mode = request.GET.get('mode', page.default_preview_mode)
response = page.serve_preview(page.dummy_request(), preview_mode)
response = page.serve_preview(page.dummy_request(request), preview_mode)
response['X-Wagtail-Preview'] = 'ok'
return response
@ -576,7 +576,7 @@ def preview_on_create(request, content_type_app_name, content_type_model_name, p
page.path = Page._get_children_path_interval(parent_page.path)[1]
preview_mode = request.GET.get('mode', page.default_preview_mode)
response = page.serve_preview(page.dummy_request(), preview_mode)
response = page.serve_preview(page.dummy_request(request), preview_mode)
response['X-Wagtail-Preview'] = 'ok'
return response
@ -1013,4 +1013,4 @@ def revisions_view(request, page_id, revision_id):
revision = get_object_or_404(page.revisions, id=revision_id)
revision_page = revision.as_page_object()
return revision_page.serve_preview(page.dummy_request(), page.default_preview_mode)
return revision_page.serve_preview(page.dummy_request(request), page.default_preview_mode)

Wyświetl plik

@ -1168,12 +1168,15 @@ class Page(six.with_metaclass(PageBase, MP_Node, index.Indexed, ClusterableModel
user_perms = UserPagePermissionsProxy(user)
return user_perms.for_page(self)
def dummy_request(self):
def dummy_request(self, original_request=None, **meta):
"""
Construct a HttpRequest object that is, as far as possible, representative of ones that would
receive this page as a response. Used for previewing / moderation and any other place where we
want to display a view of this page in the admin interface without going through the regular
page routing logic.
If you pass in a real request object as original_request, additional information (e.g. client IP, cookies)
will be included in the dummy request.
"""
url = self.full_url
if url:
@ -1195,14 +1198,30 @@ class Page(six.with_metaclass(PageBase, MP_Node, index.Indexed, ClusterableModel
path = '/'
port = 80
request = WSGIRequest({
dummy_values = {
'REQUEST_METHOD': 'GET',
'PATH_INFO': path,
'SERVER_NAME': hostname,
'SERVER_PORT': port,
'HTTP_HOST': hostname,
'wsgi.input': StringIO(),
})
}
# Add important values from the original request object, if it was provided.
if original_request:
if original_request.META.get('REMOTE_ADDR'):
dummy_values['REMOTE_ADDR'] = original_request.META['REMOTE_ADDR']
if original_request.META.get('HTTP_X_FORWARDED_FOR'):
dummy_values['HTTP_X_FORWARDED_FOR'] = original_request.META['HTTP_X_FORWARDED_FOR']
if original_request.META.get('HTTP_COOKIE'):
dummy_values['HTTP_COOKIE'] = original_request.META['HTTP_COOKIE']
if original_request.META.get('HTTP_USER_AGENT'):
dummy_values['HTTP_USER_AGENT'] = original_request.META['HTTP_USER_AGENT']
# Add additional custom metadata sent by the caller.
dummy_values.update(**meta)
request = WSGIRequest(dummy_values)
# Apply middleware to the request - see http://www.mellowmorning.com/2011/04/18/mock-django-request-for-testing/
handler = BaseHandler()

Wyświetl plik

@ -10,6 +10,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.http import Http404, HttpRequest
from django.test import Client, TestCase
from django.test.client import RequestFactory
from django.test.utils import override_settings
from wagtail.tests.testapp.models import (
@ -1156,6 +1157,24 @@ class TestDummyRequest(TestCase):
self.assertEqual(request.path, '/events/')
self.assertEqual(request.META['HTTP_HOST'], 'localhost')
def test_dummy_request_for_accessible_page_with_original_request(self):
event_index = Page.objects.get(url_path='/home/events/')
original_headers = {
'REMOTE_ADDR': '192.168.0.1',
'HTTP_X_FORWARDED_FOR': '192.168.0.2,192.168.0.3',
'HTTP_COOKIE': "test=1;blah=2",
'HTTP_USER_AGENT': "Test Agent",
}
factory = RequestFactory(**original_headers)
original_request = factory.get('/home/events/')
request = event_index.dummy_request(original_request)
# request should have the all the special headers we set in original_request
self.assertEqual(request.META['REMOTE_ADDR'], original_request.META['REMOTE_ADDR'])
self.assertEqual(request.META['HTTP_X_FORWARDED_FOR'], original_request.META['HTTP_X_FORWARDED_FOR'])
self.assertEqual(request.META['HTTP_COOKIE'], original_request.META['HTTP_COOKIE'])
self.assertEqual(request.META['HTTP_USER_AGENT'], original_request.META['HTTP_USER_AGENT'])
@override_settings(ALLOWED_HOSTS=['production.example.com'])
def test_dummy_request_for_inaccessible_page_should_use_valid_host(self):
root_page = Page.objects.get(url_path='/')