diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini index be00f3873535..1a87f0c5a346 100644 --- a/etc/nova/api-paste.ini +++ b/etc/nova/api-paste.ini @@ -22,6 +22,7 @@ use = egg:Paste#urlmap [composite:ec2cloud] use = call:nova.api.auth:pipeline_factory noauth = ec2faultwrap logrequest ec2noauth cloudrequest validator ec2executor +noauth2 = ec2faultwrap logrequest ec2noauth cloudrequest validator ec2executor keystone = ec2faultwrap logrequest ec2keystoneauth cloudrequest validator ec2executor [filter:ec2faultwrap] @@ -67,17 +68,20 @@ use = call:nova.api.openstack.urlmap:urlmap_factory [composite:openstack_compute_api_v2] use = call:nova.api.auth:pipeline_factory noauth = compute_req_id faultwrap sizelimit noauth ratelimit osapi_compute_app_v2 +noauth2 = compute_req_id faultwrap sizelimit noauth2 ratelimit osapi_compute_app_v2 keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2 keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2 [composite:openstack_compute_api_v21] use = call:nova.api.auth:pipeline_factory_v21 noauth = compute_req_id faultwrap sizelimit noauth osapi_compute_app_v21 +noauth2 = compute_req_id faultwrap sizelimit noauth2 osapi_compute_app_v21 keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21 [composite:openstack_compute_api_v3] use = call:nova.api.auth:pipeline_factory_v21 noauth = request_id faultwrap sizelimit noauth_v3 osapi_compute_app_v3 +noauth2 = request_id faultwrap sizelimit noauth_v3 osapi_compute_app_v3 keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v3 [filter:request_id] @@ -90,6 +94,9 @@ paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory paste.filter_factory = nova.api.openstack:FaultWrapper.factory [filter:noauth] +paste.filter_factory = nova.api.openstack.auth:NoAuthMiddlewareOld.factory + +[filter:noauth2] paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory [filter:noauth_v3] diff --git a/nova/api/auth.py b/nova/api/auth.py index 33613e107b4e..d0c4b1ed799f 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -25,6 +25,7 @@ import webob.exc from nova import context from nova.i18n import _ +from nova.openstack.common import versionutils from nova import wsgi @@ -36,7 +37,13 @@ auth_opts = [ 'is removed from v3 api.'), cfg.StrOpt('auth_strategy', default='keystone', - help='The strategy to use for auth: noauth or keystone.'), + help=''' +The strategy to use for auth: keystone, noauth (deprecated), or +noauth2. Both noauth and noauth2 are designed for testing only, as +they do no actual credential checking. noauth provides administrative +credentials regardless of the passed in user, noauth2 only does if +'admin' is specified as the username. +'''), cfg.BoolOpt('use_forwarded_for', default=False, help='Treat X-Forwarded-For as the canonical remote address. ' @@ -60,6 +67,12 @@ def _load_pipeline(loader, pipeline): def pipeline_factory(loader, global_conf, **local_conf): """A paste pipeline replica that keys off of auth_strategy.""" + # TODO(sdague): remove deprecated noauth in Liberty + if CONF.auth_strategy == 'noauth': + versionutils.report_deprecated_feature( + LOG, + ('The noauth middleware will be removed in Liberty.' + ' noauth2 should be used instead.')) pipeline = local_conf[CONF.auth_strategy] if not CONF.api_rate_limit: limit_name = CONF.auth_strategy + '_nolimit' diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 469bf2181deb..6f53a9d26145 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -29,7 +29,7 @@ CONF.import_opt('use_forwarded_for', 'nova.api.auth') class NoAuthMiddlewareBase(base_wsgi.Middleware): """Return a fake token if one isn't specified.""" - def base_call(self, req, project_id_in_path): + def base_call(self, req, project_id_in_path, always_admin=True): if 'X-Auth-Token' not in req.headers: user_id = req.headers.get('X-Auth-User', 'admin') project_id = req.headers.get('X-Auth-Project-Id', 'admin') @@ -53,9 +53,10 @@ class NoAuthMiddlewareBase(base_wsgi.Middleware): remote_address = getattr(req, 'remote_address', '127.0.0.1') if CONF.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) + is_admin = always_admin or (user_id == 'admin') ctx = context.RequestContext(user_id, project_id, - is_admin=True, + is_admin=is_admin, remote_address=remote_address) req.environ['nova.context'] = ctx @@ -63,7 +64,27 @@ class NoAuthMiddlewareBase(base_wsgi.Middleware): class NoAuthMiddleware(NoAuthMiddlewareBase): - """Return a fake token if one isn't specified.""" + """Return a fake token if one isn't specified. + + noauth2 is a variation on noauth that only provides admin privs if + 'admin' is provided as the user id. We will deprecate the + NoAuthMiddlewareOld for future removal so we don't need to + maintain both code paths. + + """ + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + return self.base_call(req, True, always_admin=False) + + +# TODO(sdague): remove in Liberty +class NoAuthMiddlewareOld(NoAuthMiddlewareBase): + """Return a fake token if one isn't specified. + + This is the Deprecated version of noauth, and should be removed in + the Liberty cycle. + + """ @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): return self.base_call(req, True) diff --git a/nova/tests/fixtures.py b/nova/tests/fixtures.py index f1ae8ea32a2c..f1941ec35bc5 100644 --- a/nova/tests/fixtures.py +++ b/nova/tests/fixtures.py @@ -331,6 +331,8 @@ class OSAPIFixture(fixtures.Fixture): 'host': osapi.host, 'port': osapi.port, 'api_version': self.api_version}) self.api = client.TestOpenStackClient('fake', 'fake', self.auth_url) + self.admin_api = client.TestOpenStackClient( + 'admin', 'admin', self.auth_url) class PoisonFunctions(fixtures.Fixture): diff --git a/nova/tests/functional/integrated_helpers.py b/nova/tests/functional/integrated_helpers.py index e74d5888ec09..f9e8f8087487 100644 --- a/nova/tests/functional/integrated_helpers.py +++ b/nova/tests/functional/integrated_helpers.py @@ -88,6 +88,7 @@ class _IntegratedTestBase(test.TestCase): self.api_fixture = self.useFixture( nova_fixtures.OSAPIFixture(self._api_version)) self.api = self.api_fixture.api + self.admin_api = self.api_fixture.admin_api self.useFixture(cast_as_call.CastAsCall(self.stubs)) diff --git a/nova/tests/functional/wsgi/test_flavor_manage.py b/nova/tests/functional/wsgi/test_flavor_manage.py index b5c76af437ea..8f13872c1ff7 100644 --- a/nova/tests/functional/wsgi/test_flavor_manage.py +++ b/nova/tests/functional/wsgi/test_flavor_manage.py @@ -70,7 +70,7 @@ class FlavorManageFullstack(test.TestCase): """ def setUp(self): super(FlavorManageFullstack, self).setUp() - self.api = self.useFixture(nova_fixtures.OSAPIFixture()).api + self.api = self.useFixture(nova_fixtures.OSAPIFixture()).admin_api def assertFlavorDbEqual(self, flav, flavdb): # a mapping of the REST params to the db fields diff --git a/nova/tests/unit/api/test_auth.py b/nova/tests/unit/api/test_auth.py index 19215d18fdf9..76ef16d3e91e 100644 --- a/nova/tests/unit/api/test_auth.py +++ b/nova/tests/unit/api/test_auth.py @@ -152,18 +152,34 @@ class TestPipeLineFactory(test.NoDBTestCase): self.assertEqual(app.name, pipeline.split()[-1]) self.assertIsInstance(app, TestPipeLineFactory.FakeApp) - def test_pipeline_factory(self): + def test_pipeline_factory_noauthold(self): fake_pipeline = 'test1 test2 test3' + CONF.set_override('auth_strategy', 'noauth') app = nova.api.auth.pipeline_factory( TestPipeLineFactory.FakeLoader(), None, noauth=fake_pipeline) self._test_pipeline(fake_pipeline, app) - def test_pipeline_factory_v21(self): + def test_pipeline_factory_v21_noauthold(self): fake_pipeline = 'test1 test2 test3' + CONF.set_override('auth_strategy', 'noauth') app = nova.api.auth.pipeline_factory_v21( TestPipeLineFactory.FakeLoader(), None, noauth=fake_pipeline) self._test_pipeline(fake_pipeline, app) + def test_pipeline_factory(self): + fake_pipeline = 'test1 test2 test3' + CONF.set_override('auth_strategy', 'noauth2') + app = nova.api.auth.pipeline_factory( + TestPipeLineFactory.FakeLoader(), None, noauth2=fake_pipeline) + self._test_pipeline(fake_pipeline, app) + + def test_pipeline_factory_v21(self): + fake_pipeline = 'test1 test2 test3' + CONF.set_override('auth_strategy', 'noauth2') + app = nova.api.auth.pipeline_factory_v21( + TestPipeLineFactory.FakeLoader(), None, noauth2=fake_pipeline) + self._test_pipeline(fake_pipeline, app) + def test_pipeline_factory_with_rate_limits(self): CONF.set_override('api_rate_limit', True) CONF.set_override('auth_strategy', 'keystone') diff --git a/nova/wsgi.py b/nova/wsgi.py index ba3f0c939f80..1b8a649b08e0 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -531,6 +531,6 @@ class Loader(object): LOG.debug("Loading app %(name)s from %(path)s", {'name': name, 'path': self.config_path}) return deploy.loadapp("config:%s" % self.config_path, name=name) - except LookupError as err: - LOG.error(err) + except LookupError: + LOG.exception(_LE("Couldn't lookup app: %s"), name) raise exception.PasteAppNotFound(name=name, path=self.config_path)