From f9c44f02b86d87f928a894ca4cf353202b01d3ff Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Fri, 8 Jul 2011 14:22:14 -0500 Subject: Renamed exthandler to urlrewritefilter to better illustrate it's purpose --- etc/keystone.conf | 8 +-- keystone/middleware/exthandler.py | 82 ---------------------------- keystone/middleware/url.py | 83 +++++++++++++++++++++++++++++ keystone/test/unit/test_exthandler.py | 78 --------------------------- keystone/test/unit/test_keystone.py | 2 +- keystone/test/unit/test_urlrewritefilter.py | 78 +++++++++++++++++++++++++++ setup.py | 2 +- 7 files changed, 167 insertions(+), 166 deletions(-) delete mode 100644 keystone/middleware/exthandler.py create mode 100644 keystone/middleware/url.py delete mode 100644 keystone/test/unit/test_exthandler.py create mode 100644 keystone/test/unit/test_urlrewritefilter.py diff --git a/etc/keystone.conf b/etc/keystone.conf index 425b41ee..a404435e 100755 --- a/etc/keystone.conf +++ b/etc/keystone.conf @@ -64,12 +64,12 @@ sql_idle_timeout = 30 [pipeline:admin] pipeline = - exthandler + urlrewritefilter admin_service [pipeline:keystone-legacy-auth] pipeline = - exthandler + urlrewritefilter legacy_auth public_service @@ -79,8 +79,8 @@ paste.app_factory = keystone.server:app_factory [app:admin_service] paste.app_factory = keystone.server:admin_app_factory -[filter:exthandler] -paste.filter_factory = keystone.middleware.exthandler:filter_factory +[filter:urlrewritefilter] +paste.filter_factory = keystone.middleware.url:filter_factory [filter:legacy_auth] paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/middleware/exthandler.py b/keystone/middleware/exthandler.py deleted file mode 100644 index e0cc7b39..00000000 --- a/keystone/middleware/exthandler.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010 OpenStack, LLC. -# -# 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. - - -""" -Auth Middleware that accepts URL query extension. - -This module can be installed as a filter in front of your service to -detect extension in the resource URI (e.g., foo/resource.xml) to -specify HTTP response body type. If an extension is specified, it -overwrites the Accept header in the request, if present. - -""" - -CONTENT_TYPES = {'json': 'application/json', 'xml': 'application/xml'} -DEFAULT_CONTENT_TYPE = CONTENT_TYPES['json'] - -class UrlExtensionFilter(object): - - def __init__(self, app, conf): - # app is the next app in WSGI chain - eventually the OpenStack service - self.app = app - self.conf = conf - - def __call__(self, env, start_response): - (env['PATH_INFO'], env['HTTP_ACCEPT']) = self.override_accept_header( - env.get('PATH_INFO'), env.get('HTTP_ACCEPT')) - - env['PATH_INFO'] = self.remove_trailing_slash(env.get('PATH_INFO')) - - return self.app(env, start_response) - - def override_accept_header(self, path_info, http_accept): - """Looks for an (.json/.xml) extension on the URL, removes it, and - overrides the Accept header if an extension was found""" - # try to split the extension from the rest of the path - parts = path_info.rsplit('.', 1) - if len(parts) > 1: - (path, ext) = parts - else: - (path, ext) = (parts[0], None) - - if ext in CONTENT_TYPES: - # Use the content type specified by the extension - return (path, CONTENT_TYPES[ext]) - elif http_accept is None: - # No extension or Accept header specified, use default - return (path_info, DEFAULT_CONTENT_TYPE) - else: - # Return what we were given - return (path_info, http_accept) - - def remove_trailing_slash(self, path_info): - """Removes a trailing slash from the given path, if any""" - if path_info[-1] == '/': - return path_info[:-1] - else: - return path_info - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - return UrlExtensionFilter(app, conf) - return ext_filter diff --git a/keystone/middleware/url.py b/keystone/middleware/url.py new file mode 100644 index 00000000..fd97a6a7 --- /dev/null +++ b/keystone/middleware/url.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright (c) 2010 OpenStack, LLC. +# +# 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. + + +""" +Auth Middleware that accepts URL query extension. + +This module can be installed as a filter in front of your service to +detect extension in the resource URI (e.g., foo/resource.xml) to +specify HTTP response body type. If an extension is specified, it +overwrites the Accept header in the request, if present. + +""" + +CONTENT_TYPES = {'json': 'application/json', 'xml': 'application/xml'} +DEFAULT_CONTENT_TYPE = CONTENT_TYPES['json'] + +class UrlRewriteFilter(object): + """Middleware filter to handle URL rewriting""" + + def __init__(self, app, conf): + # app is the next app in WSGI chain - eventually the OpenStack service + self.app = app + self.conf = conf + + def __call__(self, env, start_response): + (env['PATH_INFO'], env['HTTP_ACCEPT']) = self.override_accept_header( + env.get('PATH_INFO'), env.get('HTTP_ACCEPT')) + + env['PATH_INFO'] = self.remove_trailing_slash(env.get('PATH_INFO')) + + return self.app(env, start_response) + + def override_accept_header(self, path_info, http_accept): + """Looks for an (.json/.xml) extension on the URL, removes it, and + overrides the Accept header if an extension was found""" + # try to split the extension from the rest of the path + parts = path_info.rsplit('.', 1) + if len(parts) > 1: + (path, ext) = parts + else: + (path, ext) = (parts[0], None) + + if ext in CONTENT_TYPES: + # Use the content type specified by the extension + return (path, CONTENT_TYPES[ext]) + elif http_accept is None: + # No extension or Accept header specified, use default + return (path_info, DEFAULT_CONTENT_TYPE) + else: + # Return what we were given + return (path_info, http_accept) + + def remove_trailing_slash(self, path_info): + """Removes a trailing slash from the given path, if any""" + if path_info[-1] == '/': + return path_info[:-1] + else: + return path_info + +def filter_factory(global_conf, **local_conf): + """Returns a WSGI filter app for use with paste.deploy.""" + conf = global_conf.copy() + conf.update(local_conf) + + def ext_filter(app): + return UrlRewriteFilter(app, conf) + return ext_filter diff --git a/keystone/test/unit/test_exthandler.py b/keystone/test/unit/test_exthandler.py deleted file mode 100644 index b3616875..00000000 --- a/keystone/test/unit/test_exthandler.py +++ /dev/null @@ -1,78 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 OpenStack, LLC. -# -# 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 keystone.middleware.exthandler import UrlExtensionFilter - - -class MockWsgiApp(object): - - def __init__(self): - pass - - def __call__(self, env, start_response): - pass - - -def _start_response(): - pass - - -class UrlExtensionFilterTest(unittest.TestCase): - - def setUp(self): - self.filter = UrlExtensionFilter(MockWsgiApp(), {}) - - def test_remove_trailing_slash(self): - env = {'PATH_INFO': '/v2.0/'} - self.filter(env, _start_response) - self.assertEqual('/v2.0', env['PATH_INFO']) - - def test_remove_trailing_slash_from_empty_path(self): - env = {'PATH_INFO': '/'} - self.filter(env, _start_response) - self.assertEqual('', env['PATH_INFO']) - - def test_no_extension(self): - env = {'PATH_INFO': '/v2.0/someresource'} - self.filter(env, _start_response) - self.assertEqual('/v2.0/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - def test_xml_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.xml'} - self.filter(env, _start_response) - self.assertEqual('/v2.0/someresource', env['PATH_INFO']) - self.assertEqual('application/xml', env['HTTP_ACCEPT']) - - def test_json_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.json'} - self.filter(env, _start_response) - self.assertEqual('/v2.0/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - def test_extension_overrides_header(self): - env = { - 'PATH_INFO': '/v2.0/someresource.json', - 'HTTP_ACCEPT': 'application/xml'} - self.filter(env, _start_response) - self.assertEqual('/v2.0/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_keystone.py b/keystone/test/unit/test_keystone.py index 05fe1864..5909c08f 100755 --- a/keystone/test/unit/test_keystone.py +++ b/keystone/test/unit/test_keystone.py @@ -9,7 +9,7 @@ TEST_FILES = [ #'test_authn_v2.py', # this is largely failing 'test_common.py', # this doesn't actually contain tests 'test_endpoints.py', - 'test_exthandler.py', + 'test_urlrewritefilter.py', 'test_groups.py', 'test_keystone.py', # not sure why this is referencing itself 'test_roles.py', diff --git a/keystone/test/unit/test_urlrewritefilter.py b/keystone/test/unit/test_urlrewritefilter.py new file mode 100644 index 00000000..e22c362f --- /dev/null +++ b/keystone/test/unit/test_urlrewritefilter.py @@ -0,0 +1,78 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2010-2011 OpenStack, LLC. +# +# 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 keystone.middleware.url import UrlRewriteFilter + + +class MockWsgiApp(object): + + def __init__(self): + pass + + def __call__(self, env, start_response): + pass + + +def _start_response(): + pass + + +class UrlExtensionFilterTest(unittest.TestCase): + + def setUp(self): + self.filter = UrlRewriteFilter(MockWsgiApp(), {}) + + def test_remove_trailing_slash(self): + env = {'PATH_INFO': '/v2.0/'} + self.filter(env, _start_response) + self.assertEqual('/v2.0', env['PATH_INFO']) + + def test_remove_trailing_slash_from_empty_path(self): + env = {'PATH_INFO': '/'} + self.filter(env, _start_response) + self.assertEqual('', env['PATH_INFO']) + + def test_no_extension(self): + env = {'PATH_INFO': '/v2.0/someresource'} + self.filter(env, _start_response) + self.assertEqual('/v2.0/someresource', env['PATH_INFO']) + self.assertEqual('application/json', env['HTTP_ACCEPT']) + + def test_xml_extension(self): + env = {'PATH_INFO': '/v2.0/someresource.xml'} + self.filter(env, _start_response) + self.assertEqual('/v2.0/someresource', env['PATH_INFO']) + self.assertEqual('application/xml', env['HTTP_ACCEPT']) + + def test_json_extension(self): + env = {'PATH_INFO': '/v2.0/someresource.json'} + self.filter(env, _start_response) + self.assertEqual('/v2.0/someresource', env['PATH_INFO']) + self.assertEqual('application/json', env['HTTP_ACCEPT']) + + def test_extension_overrides_header(self): + env = { + 'PATH_INFO': '/v2.0/someresource.json', + 'HTTP_ACCEPT': 'application/xml'} + self.filter(env, _start_response) + self.assertEqual('/v2.0/someresource', env['PATH_INFO']) + self.assertEqual('application/json', env['HTTP_ACCEPT']) + + +if __name__ == '__main__': + unittest.main() diff --git a/setup.py b/setup.py index 6e7276a4..56444937 100755 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ setup( entry_points={ 'paste.app_factory': ['main=identity:app_factory'], 'paste.filter_factory': [ - 'extfilter=keystone.middleware.exthandler:filter_factory', + 'extfilter=keystone.middleware.url:filter_factory', 'remoteauth=keystone.middleware.remoteauth:remoteauth_factory', 'tokenauth=keystone.auth_protocols.auth_token:filter_factory', 'swiftauth=keystone.middleware.swift_auth:filter_factory', -- cgit