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
This commit is contained in:
Donagh McCabe 2013-03-25 18:48:25 +00:00
parent 7f534fac38
commit eb4b29d243
5 changed files with 232 additions and 0 deletions

View File

@ -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::
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd" >
<cross-domain-policy>
<allow-access-from domain="*" secure="false" />
</cross-domain-policy>
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 = <allow-access-from domain="*.example.com" />
<allow-access-from domain="www.example.com" secure="false" />
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 = <allow-access-from domain="*" secure="false" />

View File

@ -54,6 +54,7 @@ Overview and Concepts
overview_container_sync
overview_expiring_objects
cors
crossdomain
associated_projects
Developer Documentation

View File

@ -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',

View File

@ -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 = <allow-access-from domain="*.example.com" />
<allow-access-from domain="www.example.com" secure="false" />
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 = <allow-access-from domain="*" secure="false" />
"""
def __init__(self, app, conf, *args, **kwargs):
self.app = app
self.conf = conf
default_domain_policy = '<allow-access-from domain="*"' \
' secure="false" />'
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 = '<?xml version="1.0"?>\n' \
'<!DOCTYPE cross-domain-policy SYSTEM ' \
'"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd" >\n' \
'<cross-domain-policy>\n' \
'%s\n' \
'</cross-domain-policy>' % 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

View File

@ -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 = '<?xml version="1.0"?>\n' \
'<!DOCTYPE cross-domain-policy SYSTEM ' \
'"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd" >\n' \
'<cross-domain-policy>\n' \
'<allow-access-from domain="*" secure="false" />\n' \
'</cross-domain-policy>'
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': '<dummy 1>\n<dummy 2>'}
self.app = crossdomain.CrossDomainMiddleware(FakeApp(), conf)
expectedResponse = '<?xml version="1.0"?>\n' \
'<!DOCTYPE cross-domain-policy SYSTEM ' \
'"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd" >\n' \
'<cross-domain-policy>\n' \
'<dummy 1>\n' \
'<dummy 2>\n' \
'</cross-domain-policy>'
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()