From 1e71566169d9ffb34ed4e1bdf3f264cbdbb567cb Mon Sep 17 00:00:00 2001 From: Anderson Mesquita Date: Tue, 14 Jan 2014 10:23:08 -0600 Subject: [PATCH] Assign X-Auth-Url header in a separate middleware authtoken middleware's current purpose is to set X-Auth-Url header to a value read from heat.conf. Since this value is not really related to anything the keystone middleware does and is instead read from a config file, it makes sense to move it from the authtoken middleware into its own middleware This change is the first step to grouping all X-Auth-Url related logic into one single middleware as opposed to have it scattered (or possibly repeated) in auth_token and auth_password. For example, auth_password also has some logic around it related to multi-cloud, which can be extracted and moved to auth_url middleware in a later patch, so that all handling of X-Auth-Url occurs in one place. Also, by extracting the X-Auth-Url logic, it allows cloud providers to remove auth_token or auth_password without side-effects. Closes-Bug: #1259364 Change-Id: Ieb251c18aa091391a28a90c495b61cf41436f8b9 --- etc/heat/api-paste.ini | 8 +++-- heat/common/auth_token.py | 48 ---------------------------- heat/common/auth_url.py | 49 +++++++++++++++++++++++++++++ heat/tests/test_auth_url.py | 63 +++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 50 deletions(-) delete mode 100644 heat/common/auth_token.py create mode 100644 heat/common/auth_url.py create mode 100644 heat/tests/test_auth_url.py diff --git a/etc/heat/api-paste.ini b/etc/heat/api-paste.ini index 873c219eda..fdbe745ef3 100644 --- a/etc/heat/api-paste.ini +++ b/etc/heat/api-paste.ini @@ -1,7 +1,7 @@ # heat-api pipeline [pipeline:heat-api] -pipeline = faultwrap ssl versionnegotiation authtoken context apiv1app +pipeline = faultwrap ssl versionnegotiation authurl authtoken context apiv1app # heat-api pipeline for standalone heat # ie. uses alternative auth backend that authenticates users against keystone @@ -78,9 +78,13 @@ paste.filter_factory = heat.api.aws.ec2token:EC2Token_filter_factory paste.filter_factory = heat.common.wsgi:filter_factory heat.filter_factory = heat.api.openstack:sslmiddleware_filter +# Middleware to set auth_url header appropriately +[filter:authurl] +paste.filter_factory = heat.common.auth_url:filter_factory + # Auth middleware that validates token against keystone [filter:authtoken] -paste.filter_factory = heat.common.auth_token:filter_factory +paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory # Auth middleware that validates username/password against keystone [filter:authpassword] diff --git a/heat/common/auth_token.py b/heat/common/auth_token.py deleted file mode 100644 index ad2bc6ccac..0000000000 --- a/heat/common/auth_token.py +++ /dev/null @@ -1,48 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010-2012 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 logging -from keystoneclient.middleware import auth_token - -LOG = logging.getLogger(__name__) - - -class AuthProtocol(auth_token.AuthProtocol): - """ - Subclass of keystoneclient auth_token middleware which also - sets the 'X-Auth-Url' header to the value specified in the config. - """ - def _build_user_headers(self, token_info): - rval = super(AuthProtocol, self)._build_user_headers(token_info) - rval['X-Auth-Url'] = self.auth_uri - return rval - - -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 auth_filter(app): - return AuthProtocol(app, conf) - return auth_filter - - -def app_factory(global_conf, **local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) diff --git a/heat/common/auth_url.py b/heat/common/auth_url.py new file mode 100644 index 0000000000..24e7c7c42a --- /dev/null +++ b/heat/common/auth_url.py @@ -0,0 +1,49 @@ +# Copyright 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 oslo.config import cfg + +from heat.common import wsgi +from heat.openstack.common import importutils + + +class AuthUrlFilter(wsgi.Middleware): + + def __init__(self, app, conf): + super(AuthUrlFilter, self).__init__(app) + self.conf = conf + self.auth_url = self._get_auth_url() + + def _get_auth_url(self): + if 'auth_uri' in self.conf: + return self.conf['auth_uri'] + else: + # Import auth_token to have keystone_authtoken settings setup. + auth_token_module = 'keystoneclient.middleware.auth_token' + importutils.import_module(auth_token_module) + return cfg.CONF.keystone_authtoken.auth_uri + + def process_request(self, req): + req.headers['X-Auth-Url'] = self.auth_url + return None + + +def filter_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + + def auth_url_filter(app): + return AuthUrlFilter(app, conf) + return auth_url_filter diff --git a/heat/tests/test_auth_url.py b/heat/tests/test_auth_url.py new file mode 100644 index 0000000000..d7c219943a --- /dev/null +++ b/heat/tests/test_auth_url.py @@ -0,0 +1,63 @@ +# Copyright 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 mock +import webob + +from heat.common import auth_url +from heat.tests.common import HeatTestCase + + +class FakeApp(object): + """This represents a WSGI app protected by our auth middleware.""" + + def __call__(self, environ, start_response): + """Assert that headers are correctly set up when finally called.""" + resp = webob.Response() + resp.body = 'SUCCESS' + return resp(environ, start_response) + + +class AuthUrlFilterTest(HeatTestCase): + + def setUp(self): + super(AuthUrlFilterTest, self).setUp() + self.app = FakeApp() + self.config = {'auth_uri': 'foobar'} + + @mock.patch.object(auth_url.cfg, 'CONF') + def test_adds_default_auth_url_from_keystone_authtoken(self, mock_cfg): + self.config = {} + mock_cfg.keystone_authtoken.auth_uri = 'foobar' + mock_cfg.auth_password.multi_cloud = False + self.middleware = auth_url.AuthUrlFilter(self.app, self.config) + req = webob.Request.blank('/tenant_id/') + self.middleware(req) + self.assertIn('X-Auth-Url', req.headers) + self.assertEqual('foobar', req.headers['X-Auth-Url']) + + def test_overwrites_auth_url_from_headers_with_local_config(self): + self.middleware = auth_url.AuthUrlFilter(self.app, self.config) + req = webob.Request.blank('/tenant_id/') + req.headers['X-Auth-Url'] = 'should_be_overwritten' + self.middleware(req) + self.assertEqual('foobar', req.headers['X-Auth-Url']) + + def test_reads_auth_url_from_local_config(self): + self.middleware = auth_url.AuthUrlFilter(self.app, self.config) + req = webob.Request.blank('/tenant_id/') + self.middleware(req) + self.assertIn('X-Auth-Url', req.headers) + self.assertEqual('foobar', req.headers['X-Auth-Url'])