From 904e7c97f16504824513ed5e6cc8e186d8f13153 Mon Sep 17 00:00:00 2001 From: Alistair Coles Date: Mon, 13 Mar 2017 17:12:13 +0000 Subject: [PATCH] Add more doc and test for cors_expose_headers option In follow-up to the related change, mention the new cors_expose_headers option (and other proxy-server.conf options) in the CORS doc. Add a test for the cors options being loaded into the proxy server. Improve CORS comments in docs. Change-Id: I647d8f9e9cbd98de05443638628414b1e87d1a76 Related-Change: I5ca90a052f27c98a514a96ee2299bfa1b6d46334 --- doc/manpages/proxy-server.conf.5 | 9 ++++-- doc/source/cors.rst | 7 +++++ doc/source/deployment_guide.rst | 14 ++++----- etc/proxy-server.conf-sample | 9 +++++- test/unit/proxy/test_server.py | 52 +++++++++++++++++++++++++++----- 5 files changed, 73 insertions(+), 18 deletions(-) diff --git a/doc/manpages/proxy-server.conf.5 b/doc/manpages/proxy-server.conf.5 index 14b838e96a..be0ef95092 100644 --- a/doc/manpages/proxy-server.conf.5 +++ b/doc/manpages/proxy-server.conf.5 @@ -142,11 +142,14 @@ This optional suffix (default is empty) that would be appended to the swift tran id allows one to easily figure out from which cluster that X-Trans-Id belongs to. This is very useful when one is managing more than one swift cluster. .IP \fBcors_allow_origin\fR -Use a comma separated list of full URL (http://foo.bar:1234,https://foo.bar) +List of origin hosts that are allowed for CORS requests in addition to what +the container has set. Use a comma separated list of full URL (http://foo.bar:1234,https://foo.bar) .IP \fBstrict_cors_mode\fR -The default is true. +If True (default) then CORS requests are only allowed if their Origin header +matches an allowed origin. Otherwise, any Origin is allowed. .IP \fBcors_expose_headers\fR -Comma separated list of headers to expose through Access-Control-Expose-Headers +Comma separated list of headers to expose through Access-Control-Expose-Headers, +in addition to the defaults and any headers set in container metadata. .IP \fBnice_priority\fR Modify scheduling priority of server processes. Niceness values range from -20 (most favorable to the process) to 19 (least favorable to the process). diff --git a/doc/source/cors.rst b/doc/source/cors.rst index 9ef02a8374..0f7ad0147a 100644 --- a/doc/source/cors.rst +++ b/doc/source/cors.rst @@ -28,6 +28,11 @@ The supported headers are, | | Space separated. | +------------------------------------------------+------------------------------+ +In addition the the values set in container metadata, some cluster-wide values +may also be configured using the ``strict_cors_mode``, ``cors_allow_origin`` +and ``cors_expose_headers`` in ``proxy-server.conf``. See +``proxy-server.conf-sample`` for more information. + Before a browser issues an actual request it may issue a `preflight request`_. The preflight request is an OPTIONS call to verify the Origin is allowed to make the request. The sequence of events are, @@ -48,6 +53,8 @@ returns the following values for this header, * all metadata headers (``X-Container-Meta-*`` for containers and ``X-Object-Meta-*`` for objects) * headers listed in ``X-Container-Meta-Access-Control-Expose-Headers`` +* headers configured using the ``cors_expose_headers`` option in + ``proxy-server.conf`` .. note:: An OPTIONS request to a symlink object will respond with the options for diff --git a/doc/source/deployment_guide.rst b/doc/source/deployment_guide.rst index 8f24ab125f..ee580669d4 100644 --- a/doc/source/deployment_guide.rst +++ b/doc/source/deployment_guide.rst @@ -1765,14 +1765,14 @@ cert_file Path to the ssl key_file Path to the ssl .key. This should be enabled for testing purposes only. -cors_allow_origin This is a list of hosts that - are included with any CORS - request by default and - returned with the - Access-Control-Allow-Origin - header in addition to what +cors_allow_origin List of origin hosts that are allowed + for CORS requests in addition to what the container has set. -strict_cors_mode True +strict_cors_mode True If True (default) then CORS + requests are only allowed if their + Origin header matches an allowed + origin. Otherwise, any Origin is + allowed. cors_expose_headers This is a list of headers that are included in the header Access-Control-Expose-Headers diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 97208daab7..89ff1750ba 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -70,11 +70,18 @@ bind_port = 8080 # log_statsd_sample_rate_factor = 1.0 # log_statsd_metric_prefix = # +# List of origin hosts that are allowed for CORS requests in addition to what +# the container has set. # Use a comma separated list of full URL (http://foo.bar:1234,https://foo.bar) # cors_allow_origin = + +# If True (default) then CORS requests are only allowed if their Origin header +# matches an allowed origin. Otherwise, any Origin is allowed. # strict_cors_mode = True # -# Comma separated list of headers to expose through Access-Control-Expose-Headers +# Comma separated list of headers to expose through Access-Control-Expose-Headers, +# in addition to the defaults and any headers set in container metadata (see +# CORS documentation). # cors_expose_headers = # # client_timeout = 60 diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index 2a423e2187..177da4f738 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -562,18 +562,48 @@ class TestController(unittest.TestCase): @patch_policies([StoragePolicy(0, 'zero', True, object_ring=FakeRing())]) -class TestProxyServer(unittest.TestCase): +class TestProxyServerConfiguration(unittest.TestCase): + def _make_app(self, conf): + # helper function to instantiate a proxy server instance + return proxy_server.Application(conf, FakeMemcache(), + container_ring=FakeRing(), + account_ring=FakeRing()) - def test_creation(self): + def test_node_timeout(self): # later config should be extended to assert more config options - app = proxy_server.Application({'node_timeout': '3.5', - 'recoverable_node_timeout': '1.5'}, - FakeMemcache(), - container_ring=FakeRing(), - account_ring=FakeRing()) + app = self._make_app({'node_timeout': '3.5', + 'recoverable_node_timeout': '1.5'}) self.assertEqual(app.node_timeout, 3.5) self.assertEqual(app.recoverable_node_timeout, 1.5) + def test_cors_options(self): + # check defaults + app = self._make_app({}) + self.assertFalse(app.cors_allow_origin) + self.assertFalse(app.cors_expose_headers) + self.assertTrue(app.strict_cors_mode) + + # check custom configs + app = self._make_app({ + 'cors_allow_origin': '', + 'cors_expose_headers': '', + 'strict_cors_mode': 'True'}) + self.assertTrue(app.strict_cors_mode) + + app = self._make_app({ + 'cors_allow_origin': ' http://X.com,http://Y.com ,, http://Z.com', + 'cors_expose_headers': ' custom1,,, custom2,custom3,,', + 'strict_cors_mode': 'False'}) + self.assertEqual({'http://X.com', 'http://Y.com', 'http://Z.com'}, + set(app.cors_allow_origin)) + self.assertEqual({'custom1', 'custom2', 'custom3'}, + set(app.cors_expose_headers)) + self.assertFalse(app.strict_cors_mode) + + +@patch_policies([StoragePolicy(0, 'zero', True, object_ring=FakeRing())]) +class TestProxyServer(unittest.TestCase): + def test_get_object_ring(self): baseapp = proxy_server.Application({}, FakeMemcache(), @@ -5280,6 +5310,14 @@ class TestReplicatedObjectController( self.assertNotIn('access-control-expose-headers', resp.headers) self.assertNotIn('access-control-allow-origin', resp.headers) + # test proxy server cors_allow_origin option + self.app.cors_allow_origin = ['http://foo.bar'] + resp = self._get_CORS_response( + container_cors=container_cors, strict_mode=True) + self.assertEqual('http://foo.bar', + resp.headers['access-control-allow-origin']) + self.assertEqual(expected_exposed, exposed) + def test_CORS_valid_with_obj_headers(self): container_cors = {'allow_origin': 'http://foo.bar'}