From 5466ec1833914019da8a9e032031732101dc760d Mon Sep 17 00:00:00 2001 From: yatinkarel Date: Mon, 5 Aug 2019 12:58:02 +0530 Subject: [PATCH] Make placement base API return version without auth Allow both GET '/placement' and '/placement/' to return version info without doing authentication. In many deployment placement is found on a prefix of '/placement' different servers will or will not append the '/'. To be most resilient, accept either as "root". A unit test is added which confirms that no auth is required for root but auth is still required elsewhere. Co-Authored-By: Chris Dent Related-Bug: #1838633 Change-Id: Iaa93ec943f4f4fbb6c1af3a809859813872e62d7 --- placement/auth.py | 4 +- placement/tests/unit/test_auth.py | 75 +++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 placement/tests/unit/test_auth.py diff --git a/placement/auth.py b/placement/auth.py index 81af1672c..df7dd7b1a 100644 --- a/placement/auth.py +++ b/placement/auth.py @@ -66,7 +66,7 @@ class PlacementKeystoneContext(Middleware): ctx = context.RequestContext.from_environ( req.environ, request_id=req_id) - if ctx.user_id is None and req.environ['PATH_INFO'] != '/': + if ctx.user_id is None and req.environ['PATH_INFO'] not in ['/', '']: LOG.debug("Neither X_USER_ID nor X_USER found in request") return webob.exc.HTTPUnauthorized() @@ -86,7 +86,7 @@ class PlacementAuthProtocol(auth_token.AuthProtocol): super(PlacementAuthProtocol, self).__init__(app, conf) def __call__(self, environ, start_response): - if environ['PATH_INFO'] == '/': + if environ['PATH_INFO'] in ['/', '']: return self._placement_app(environ, start_response) return super(PlacementAuthProtocol, self).__call__( diff --git a/placement/tests/unit/test_auth.py b/placement/tests/unit/test_auth.py new file mode 100644 index 000000000..dff3df589 --- /dev/null +++ b/placement/tests/unit/test_auth.py @@ -0,0 +1,75 @@ +# 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. +"""Unit tests for the auth middleware used by the Placement service. + +Most of the functionality of the auth middleware is tested in functional +and integration tests but sometimes it is more convenient or accurate to +use unit tests. +""" + +from keystonemiddleware import auth_token +from oslo_config import cfg +from oslo_config import fixture as config_fixture +from oslo_policy import opts as policy_opts +import testtools +import webob + +from placement import conf +from placement import deploy + + +class RootNoAuth(testtools.TestCase): + """Confirm that no auth is required for accessing root.""" + + def setUp(self): + """Establish config defaults for middlewares.""" + super(RootNoAuth, self).setUp() + config = cfg.ConfigOpts() + conf_fixture = self.useFixture(config_fixture.Config(config)) + conf.register_opts(conf_fixture.conf) + auth_token_opts = auth_token.AUTH_TOKEN_OPTS[0][1] + conf_fixture.register_opts(auth_token_opts, group='keystone_authtoken') + www_authenticate_uri = 'http://example.com/identity' + conf_fixture.config( + www_authenticate_uri=www_authenticate_uri, + group='keystone_authtoken') + # ensure that the auth_token middleware is chosen + conf_fixture.config(auth_strategy='keystone', group='api') + # register and default policy opts (referenced by deploy) + policy_opts.set_defaults(conf_fixture.conf) + self.conf = conf_fixture.conf + self.app = deploy.deploy(self.conf) + + def _test_root_req(self, req): + # set no environ on req, thus no auth + req.environ['REMOTE_ADDR'] = '127.0.0.1' + + response = req.get_response(self.app) + data = response.json_body + self.assertEqual('CURRENT', data['versions'][0]['status']) + + def test_slash_no_auth(self): + """Accessing / requires no auth.""" + req = webob.Request.blank('/', method='GET') + self._test_root_req(req) + + def test_no_slash_no_auth(self): + """Accessing '' requires no auth.""" + req = webob.Request.blank('', method='GET') + self._test_root_req(req) + + def test_auth_elsewhere(self): + """Make sure auth is happening.""" + req = webob.Request.blank('/resource_providers', method='GET') + req.environ['REMOTE_ADDR'] = '127.0.0.1' + response = req.get_response(self.app) + self.assertEqual('401 Unauthorized', response.status)