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 <cdent@anticdent.org>
Related-Bug: #1838633
Change-Id: Iaa93ec943f4f4fbb6c1af3a809859813872e62d7
This commit is contained in:
yatinkarel 2019-08-05 12:58:02 +05:30 committed by Matt Riedemann
parent b12cba973d
commit 5466ec1833
2 changed files with 77 additions and 2 deletions

View File

@ -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__(

View File

@ -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)