From eb4b29d243150d2f348d163ecc2fb552675891bf Mon Sep 17 00:00:00 2001 From: Donagh McCabe Date: Mon, 25 Mar 2013 18:48:25 +0000 Subject: [PATCH] Add crossdomain.xml middleware Allows client-side technologies such as Flash, Java and Silverlight running on web pages served elsewhere to interact with the Swift API. Bug #1159960 Change-Id: I7d0533a0aaf189ac452abbd983469acb064fdca4 --- doc/source/crossdomain.xml | 55 ++++++++++++ doc/source/index.rst | 1 + setup.py | 1 + swift/common/middleware/crossdomain.py | 90 +++++++++++++++++++ .../common/middleware/test_crossdomain.py | 85 ++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 doc/source/crossdomain.xml create mode 100644 swift/common/middleware/crossdomain.py create mode 100644 test/unit/common/middleware/test_crossdomain.py diff --git a/doc/source/crossdomain.xml b/doc/source/crossdomain.xml new file mode 100644 index 0000000000..3bdb43d908 --- /dev/null +++ b/doc/source/crossdomain.xml @@ -0,0 +1,55 @@ +======================== +Cross-domain Policy File +======================== + +A cross-domain policy file allows web pages hosted elsewhere to use client +side technologies such as Flash, Java and Silverlight to interact +with the Swift API. + +See http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html for +a description of the purpose and structure of the cross-domain policy +file. The cross-domain policy file is installed in the root of a web +server (i.e., the path is /crossdomain.xml). + +The crossdomain middleware responds to a path of /crossdomain.xml with an +XML document such as:: + + + + + + + +You should use a policy appropriate to your site. The examples and the +default policy are provided to indicate how to syntactically construct +a cross domain policy file -- they are not recommendations. + +------------- +Configuration +------------- + +To enable this middleware, add it to the pipeline in your proxy-server.conf +file. It should be added before any authentication (e.g., tempauth or +keystone) middleware. In this example ellipsis (...) indicate other +middleware you may have chosen to use:: + + [pipeline:main] + pipeline = ... crossdomain ... authtoken ... proxy-server + +And add a filter section, such as:: + + [filter:crossdomain] + use = egg:swift#crossdomain + cross_domain_policy = + + +For continuation lines, put some whitespace before the continuation +text. Ensure you put a completely blank line to terminate the +cross_domain_policy value. + +The cross_domain_policy name/value is optional. If omited, the policy +defaults as if you had specified:: + + cross_domain_policy = + + diff --git a/doc/source/index.rst b/doc/source/index.rst index 89b6102b92..55a84b8e10 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -54,6 +54,7 @@ Overview and Concepts overview_container_sync overview_expiring_objects cors + crossdomain associated_projects Developer Documentation diff --git a/setup.py b/setup.py index 0702feb4b4..229dc97e18 100644 --- a/setup.py +++ b/setup.py @@ -89,6 +89,7 @@ setup( ], 'paste.filter_factory': [ 'healthcheck=swift.common.middleware.healthcheck:filter_factory', + 'crossdomain=swift.common.middleware.crossdomain:filter_factory', 'memcache=swift.common.middleware.memcache:filter_factory', 'ratelimit=swift.common.middleware.ratelimit:filter_factory', 'cname_lookup=swift.common.middleware.cname_lookup:filter_factory', diff --git a/swift/common/middleware/crossdomain.py b/swift/common/middleware/crossdomain.py new file mode 100644 index 0000000000..ea9b759e09 --- /dev/null +++ b/swift/common/middleware/crossdomain.py @@ -0,0 +1,90 @@ +# Copyright (c) 2013 OpenStack Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from swift.common.swob import Request, Response + + +class CrossDomainMiddleware(object): + + """ + Cross domain middleware used to respond to requests for cross domain + policy information. + + If the path is /crossdomain.xml it will respond with an xml cross domain + policy document. This allows web pages hosted elsewhere to use client + side technologies such as Flash, Java and Silverlight to interact + with the Swift API. + + To enable this middleware, add it to the pipeline in your proxy-server.conf + file. It should be added before any authentication (e.g., tempauth or + keystone) middleware. In this example ellipsis (...) indicate other + middleware you may have chosen to use:: + + [pipeline:main] + pipeline = ... crossdomain ... authtoken ... proxy-server + + And add a filter section, such as:: + + [filter:crossdomain] + use = egg:swift#crossdomain + cross_domain_policy = + + + For continuation lines, put some whitespace before the continuation + text. Ensure you put a completely blank line to terminate the + cross_domain_policy value. + + The cross_domain_policy name/value is optional. If omited, the policy + defaults as if you had specified:: + + cross_domain_policy = + + + """ + + def __init__(self, app, conf, *args, **kwargs): + self.app = app + self.conf = conf + default_domain_policy = '' + self.cross_domain_policy = self.conf.get('cross_domain_policy', + default_domain_policy) + + def GET(self, req): + """Returns a 200 response with cross domain policy information """ + body = '\n' \ + '\n' \ + '\n' \ + '%s\n' \ + '' % self.cross_domain_policy + return Response(request=req, body=body, + content_type="application/xml") + + def __call__(self, env, start_response): + req = Request(env) + if req.path == '/crossdomain.xml' and req.method == 'GET': + return self.GET(req)(env, start_response) + else: + return self.app(env, start_response) + + +def filter_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + + def crossdomain_filter(app): + return CrossDomainMiddleware(app, conf) + return crossdomain_filter diff --git a/test/unit/common/middleware/test_crossdomain.py b/test/unit/common/middleware/test_crossdomain.py new file mode 100644 index 0000000000..3031981896 --- /dev/null +++ b/test/unit/common/middleware/test_crossdomain.py @@ -0,0 +1,85 @@ +# Copyright (c) 2013 OpenStack Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from swift.common.swob import Request, Response + +from swift.common.middleware import crossdomain + + +class FakeApp(object): + + def __call__(self, env, start_response): + return "FAKE APP" + + +def start_response(*args): + pass + + +class TestCrossDomain(unittest.TestCase): + + def setUp(self): + self.app = crossdomain.CrossDomainMiddleware(FakeApp(), {}) + + # GET of /crossdomain.xml (default) + def test_crossdomain_default(self): + expectedResponse = '\n' \ + '\n' \ + '\n' \ + '\n' \ + '' + + req = Request.blank('/crossdomain.xml', + environ={'REQUEST_METHOD': 'GET'}) + resp = self.app(req.environ, start_response) + self.assertEquals(resp, [expectedResponse]) + + # GET of /crossdomain.xml (custom) + def test_crossdomain_custom(self): + conf = {'cross_domain_policy': '\n'} + self.app = crossdomain.CrossDomainMiddleware(FakeApp(), conf) + expectedResponse = '\n' \ + '\n' \ + '\n' \ + '\n' \ + '\n' \ + '' + + req = Request.blank('/crossdomain.xml', + environ={'REQUEST_METHOD': 'GET'}) + resp = self.app(req.environ, start_response) + self.assertEquals(resp, [expectedResponse]) + + # GET to a different resource should be passed on + def test_crossdomain_pass(self): + req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'}) + resp = self.app(req.environ, start_response) + self.assertEquals(resp, 'FAKE APP') + + # Only GET is allowed on the /crossdomain.xml resource + def test_crossdomain_get_only(self): + for method in ['HEAD', 'PUT', 'POST', 'COPY', 'OPTIONS']: + req = Request.blank('/crossdomain.xml', + environ={'REQUEST_METHOD': method}) + resp = self.app(req.environ, start_response) + self.assertEquals(resp, 'FAKE APP') + + +if __name__ == '__main__': + unittest.main()