From eda3be3af48b004b2b5ebfeaf7d4e27e6591d8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Tue, 17 May 2016 16:11:24 +0200 Subject: [PATCH 1/2] Add a Vary header for Authorization. Fixes Kinto/kinto#593 --- cliquet/__init__.py | 1 + cliquet/initialization.py | 14 ++++++++++++++ cliquet/tests/test_views_hello.py | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/cliquet/__init__.py b/cliquet/__init__.py index 7849e59e..f35cc58a 100644 --- a/cliquet/__init__.py +++ b/cliquet/__init__.py @@ -51,6 +51,7 @@ 'cliquet.initialization.setup_permission', 'cliquet.initialization.setup_cache', 'cliquet.initialization.setup_requests_scheme', + 'cliquet.initialization.setup_vary_headers', 'cliquet.initialization.setup_version_redirection', 'cliquet.initialization.setup_deprecation', 'cliquet.initialization.setup_authentication', diff --git a/cliquet/initialization.py b/cliquet/initialization.py index ac2da1cc..61273a31 100644 --- a/cliquet/initialization.py +++ b/cliquet/initialization.py @@ -140,6 +140,20 @@ def on_new_request(event): config.add_subscriber(on_new_request, NewRequest) +def setup_vary_headers(config): + """Add Vary headers to each response.""" + settings = config.get_settings() + + vary = aslist(settings.get('vary', 'Authorization')) + + def on_new_request(event): + def vary_callback(request, response): + response.vary = vary + event.request.add_response_callback(vary_callback) + + config.add_subscriber(on_new_request, NewRequest) + + def setup_deprecation(config): config.add_tween("cliquet.initialization._end_of_life_tween_factory") diff --git a/cliquet/tests/test_views_hello.py b/cliquet/tests/test_views_hello.py index b175c5e0..69544929 100644 --- a/cliquet/tests/test_views_hello.py +++ b/cliquet/tests/test_views_hello.py @@ -63,6 +63,11 @@ def test_if_user_authenticated_userid_is_provided(self): self.assertTrue(userid.startswith('basicauth:'), '"%s" does not start with "basicauth:"' % userid) + def test_vary_header_is_present(self): + response = self.app.get('/', headers=self.headers) + self.assertIn('Vary', response.headers) + self.assertIn('Authorization', response.headers['Vary']) + def test_return_http_api_version_when_set(self): with mock.patch.dict( self.app.app.registry.settings, From 42e528c69cc7c9b8fe0e2ff6fba645bf93a47fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Tue, 17 May 2016 17:27:06 +0200 Subject: [PATCH 2/2] Make sure Vary is in the Exposed CORS headers. --- cliquet/__init__.py | 2 +- cliquet/tests/resource/test_views_cors.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cliquet/__init__.py b/cliquet/__init__.py index f35cc58a..c5ae304f 100644 --- a/cliquet/__init__.py +++ b/cliquet/__init__.py @@ -102,7 +102,7 @@ class Service(CorniceService): patching the default cornice service (which would impact other uses of it) """ default_cors_headers = ('Backoff', 'Retry-After', 'Alert', - 'Content-Length') + 'Content-Length', 'Vary') def error_handler(self, error): return errors.json_error_handler(error) diff --git a/cliquet/tests/resource/test_views_cors.py b/cliquet/tests/resource/test_views_cors.py index 2194c8eb..163492f4 100644 --- a/cliquet/tests/resource/test_views_cors.py +++ b/cliquet/tests/resource/test_views_cors.py @@ -147,11 +147,11 @@ def test_collection_get_exposes_every_possible_header(self): self.assert_expose_headers('GET', self.collection_url, [ 'Alert', 'Backoff', 'ETag', 'Last-Modified', 'Next-Page', 'Retry-After', 'Total-Records', 'Content-Length', - 'Cache-Control', 'Expires', 'Pragma']) + 'Cache-Control', 'Expires', 'Pragma', 'Vary']) def test_hello_endpoint_exposes_only_minimal_set_of_headers(self): self.assert_expose_headers('GET', '/', [ - 'Alert', 'Backoff', 'Retry-After', 'Content-Length']) + 'Alert', 'Backoff', 'Retry-After', 'Content-Length', 'Vary']) def test_record_get_exposes_only_used_headers(self): body = {'data': MINIMALIST_RECORD} @@ -163,22 +163,26 @@ def test_record_get_exposes_only_used_headers(self): self.assert_expose_headers('GET', record_url, [ 'Alert', 'Backoff', 'ETag', 'Retry-After', 'Last-Modified', 'Content-Length', - 'Cache-Control', 'Expires', 'Pragma']) + 'Cache-Control', 'Expires', 'Pragma', 'Vary']) def test_record_post_exposes_only_minimal_set_of_headers(self): body = {'data': MINIMALIST_RECORD} - self.assert_expose_headers('POST_JSON', '/mushrooms', [ - 'Alert', 'Backoff', 'Retry-After', 'Content-Length'], body=body) + self.assert_expose_headers('POST_JSON', '/mushrooms', + ['Alert', 'Backoff', 'Retry-After', + 'Content-Length', 'Vary'], + body=body) def test_present_on_bad_id_400_errors(self): body = {'data': {'name': 'Amanite'}} self.assert_expose_headers('PUT_JSON', '/mushrooms/wrong=ids', [ - 'Alert', 'Backoff', 'Retry-After', 'Content-Length'], + 'Alert', 'Backoff', 'Retry-After', 'Content-Length', 'Vary'], body=body, status=400) def test_present_on_unknown_url(self): - self.assert_expose_headers('PUT_JSON', '/unknown', [ - 'Alert', 'Backoff', 'Retry-After', 'Content-Length'], status=404) + self.assert_expose_headers('PUT_JSON', '/unknown', + ['Alert', 'Backoff', 'Retry-After', + 'Content-Length', 'Vary'], + status=404) class CORSMaxAgeTest(BaseWebTest, unittest.TestCase):