From: David\ B. <svn...@pl...> - 2012-07-31 08:39:53
|
Repository: diazo Branch: refs/heads/read_headers Date: 2012-07-31T00:20:35-07:00 Author: David\ Beitey (davidjb) <da...@da...> Commit: https://github.com/plone/diazo/commit/77b6608b7afe2bc284679538523811039e7b458e Read X-Diazo-Rules filename from request headers if explicitly allowed to do so Files changed: M docs/CHANGES.txt M docs/deployment.rst M lib/diazo/tests/test_wsgi.py M lib/diazo/wsgi.py diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index ca6e5b4..4d875b3 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -4,6 +4,10 @@ Changelog 1.0.2 (unreleased) ------------------ +* Allow Diazo middleware to read rules file location from HTTP headers. + This requires explicitly specifying ``read_headers`` as an option against + the middleware. + [davidjb] * Allow attributes (i.e. xml:id) to pass through on drop @attribute nodes [lentinj] diff --git a/docs/deployment.rst b/docs/deployment.rst index 8bbc07f..eb0f492 100644 --- a/docs/deployment.rst +++ b/docs/deployment.rst @@ -77,7 +77,10 @@ using Paste Deploy, they will always be passed as strings. The following options can be passed to ``DiazoMiddleware``: ``rules`` - Path to the rules file + Path to the rules file, which may be omitted if enabling the + ``read_headers`` option. In this case, reading the rules location is + expected to be found within the ``X-Diazo-Rules`` HTTP header coming + within the ``request``. ``theme`` Path to the theme, if not specified using a ``<theme />`` directive in the rules file. May also be a URL to a theme served over the network. @@ -97,6 +100,12 @@ The following options can be passed to ``DiazoMiddleware``: ``includemode`` Can be set to 'document', 'esi' or 'ssi' to change the way in which includes are processed +``read_headers`` + Set this to True to allow resolving options from HTTP headers. At + present, ``X-Diazo-Rules`` is the only such option, which refers to the + location of the rules file. *Warning*: beware of enabling this option if + users are able to spoof headers. This presents a potential security + concern if you do not drop or sanitize headers coming from clients. ``read_network`` Set this to True to allow resolving resources from the network. Defaults to False. @@ -352,4 +361,4 @@ Found page) with Apache as these do not pass through the filter chain. As parameters are not currently supported, path expression are unavailable. .. _plone.app.theming: http://pypi.python.org/pypi/plone.app.theming -.. _html-xslt: http://code.google.com/p/html-xslt/ \ No newline at end of file +.. _html-xslt: http://code.google.com/p/html-xslt/ diff --git a/lib/diazo/tests/test_wsgi.py b/lib/diazo/tests/test_wsgi.py index 1de07a0..f611c04 100644 --- a/lib/diazo/tests/test_wsgi.py +++ b/lib/diazo/tests/test_wsgi.py @@ -437,6 +437,32 @@ def application2(environ, start_response): self.assertTrue('<div id="content">Content content</div>' in response.body) self.assertTrue('<title>Transformed</title>' in response.body) + def test_diazo_rules_request_header(self): + from lxml import etree + + from diazo.wsgi import DiazoMiddleware + from webob import Request + + def application(environ, start_response): + status = '200 OK' + response_headers = [('Content-Type', 'text/html')] + start_response(status, response_headers) + return [HTML] + + app = DiazoMiddleware(application, {}, rules=None, read_headers=True) + + #Without headers or rules set, expect an exception + request = Request.blank('/') + self.assertRaisesRegexp(ValueError, 'No rules specified', request.get_response, app) + + #With headers set, expect theming + request = Request.blank('/') + request.headers['X-Diazo-Rules'] = testfile('simple_transform.xml') + response = request.get_response(app) + + self.assertTrue('<div id="content">Content content</div>' in response.body) + self.assertTrue('<title>Transformed</title>' in response.body) + def test_non_html_content_type(self): from lxml import etree diff --git a/lib/diazo/wsgi.py b/lib/diazo/wsgi.py index 6e8c7ff..d52f33f 100644 --- a/lib/diazo/wsgi.py +++ b/lib/diazo/wsgi.py @@ -16,6 +16,7 @@ from diazo.utils import quote_param DIAZO_OFF_HEADER = 'X-Diazo-Off' +DIAZO_RULES_HEADER = 'HTTP_X_DIAZO_RULES' def asbool(value): if isinstance(value, basestring): @@ -342,11 +343,13 @@ class DiazoMiddleware(object): """Invoke the Diazo transform as middleware """ - def __init__(self, app, global_conf, rules, + def __init__(self, app, global_conf, + rules=None, theme=None, prefix=None, includemode='document', debug=False, + read_headers=False, read_network=False, read_file=True, update_content_length=False, @@ -365,7 +368,9 @@ def __init__(self, app, global_conf, rules, ): """Create the middleware. The parameters are: - * ``rules``, the rules file + * ``rules``, the rules file, which may be omitted if + enabling the ``read_headers`` option and reading + the rules location from the ``X-Diazo-Rules`` HTTP header. * ``theme``, a URL to the theme file (may be a file:// URL) * ``debug``, set to True to recompile the theme on each request * ``prefix`` can be set to a string that will be prefixed to @@ -379,6 +384,11 @@ def __init__(self, app, global_conf, rules, "/static". * ``includemode`` can be set to 'document', 'esi' or 'ssi' to change the way in which includes are processed + * ``read_headers``, should be set to True to allow resolving + options from HTTP headers. At present, ``X-Diazo-Rules`` is + the only such option, which refers to the location of the rules file. + *Warning*: beware of enabling this option if users are able to + spoof headers. * ``read_network``, should be set to True to allow resolving resources from the network. * ``read_file``, should be set to False to disallow resolving resources @@ -418,6 +428,7 @@ def __init__(self, app, global_conf, rules, self.absolute_prefix = prefix self.includemode = includemode self.debug = asbool(debug) + self.read_headers = asbool(read_headers) self.read_network = asbool(read_network) self.read_file = asbool(read_file) self.update_content_length = asbool(update_content_length) @@ -509,6 +520,14 @@ def get_filter_middleware(self): ) def __call__(self, environ, start_response): + #Read certain options from headers but only if explicitly allowed + if self.read_headers: + if DIAZO_RULES_HEADER in environ: + self.rules = environ[DIAZO_RULES_HEADER] + + if not self.rules: + raise ValueError('No rules specified in settings or headers.') + if self.filter_xpath: filter_xpath = ';filter_xpath=' query_string = environ.get('QUERY_STRING', '') |