Repro bug 1845530: versioned discovery is authed

This recreates the referenced bug, demonstrating that requests for
versioned discovery endpoints (/v2, /v2.1) are being piped through
authentication.

Change-Id: Iaef1229f542e4e824c6c5c73335bc601bed08c04
Related-Bug: #1845530
This commit is contained in:
Eric Fried 2019-09-26 15:10:26 -05:00 committed by melanie witt
parent 7520676b28
commit 49a9f45644
3 changed files with 76 additions and 33 deletions
nova/tests

@ -874,7 +874,7 @@ class OSAPIFixture(fixtures.Fixture):
def __init__(self, api_version='v2', def __init__(self, api_version='v2',
project_id='6f70656e737461636b20342065766572', project_id='6f70656e737461636b20342065766572',
use_project_id_in_urls=False): use_project_id_in_urls=False, stub_keystone=True):
"""Constructor """Constructor
:param api_version: the API version that we're interested in :param api_version: the API version that we're interested in
@ -883,11 +883,14 @@ class OSAPIFixture(fixtures.Fixture):
:param project_id: the project id to use on the API. :param project_id: the project id to use on the API.
:param use_project_id_in_urls: If True, act like the "endpoint" in the :param use_project_id_in_urls: If True, act like the "endpoint" in the
"service catalog" has the legacy format including the project_id. "service catalog" has the legacy format including the project_id.
:param stub_keystone: If True, stub keystonemiddleware and
NovaKeystoneContext to simulate (but not perform) real auth.
""" """
super(OSAPIFixture, self).__init__() super(OSAPIFixture, self).__init__()
self.api_version = api_version self.api_version = api_version
self.project_id = project_id self.project_id = project_id
self.use_project_id_in_urls = use_project_id_in_urls self.use_project_id_in_urls = use_project_id_in_urls
self.stub_keystone = stub_keystone
def setUp(self): def setUp(self):
super(OSAPIFixture, self).setUp() super(OSAPIFixture, self).setUp()
@ -903,22 +906,8 @@ class OSAPIFixture(fixtures.Fixture):
} }
self.useFixture(ConfPatcher(**conf_overrides)) self.useFixture(ConfPatcher(**conf_overrides))
# Stub out authentication middleware if self.stub_keystone:
# TODO(efried): Use keystonemiddleware.fixtures.AuthTokenFixture self._stub_keystone()
self.useFixture(fixtures.MockPatch(
'keystonemiddleware.auth_token.filter_factory',
return_value=lambda _app: _app))
# Stub out context middleware
def fake_ctx(env, **kwargs):
user_id = env['HTTP_X_AUTH_USER']
project_id = env['HTTP_X_AUTH_PROJECT_ID']
is_admin = user_id == 'admin'
return context.RequestContext(
user_id, project_id, is_admin=is_admin, **kwargs)
self.useFixture(fixtures.MonkeyPatch(
'nova.api.auth.NovaKeystoneContext._create_context', fake_ctx))
# Turn off manipulation of socket_options in TCPKeepAliveAdapter # Turn off manipulation of socket_options in TCPKeepAliveAdapter
# to keep wsgi-intercept happy. Replace it with the method # to keep wsgi-intercept happy. Replace it with the method
@ -950,6 +939,24 @@ class OSAPIFixture(fixtures.Fixture):
# the fixture. # the fixture.
self.app = app self.app = app
def _stub_keystone(self):
# Stub out authentication middleware
# TODO(efried): Use keystonemiddleware.fixtures.AuthTokenFixture
self.useFixture(fixtures.MockPatch(
'keystonemiddleware.auth_token.filter_factory',
return_value=lambda _app: _app))
# Stub out context middleware
def fake_ctx(env, **kwargs):
user_id = env['HTTP_X_AUTH_USER']
project_id = env['HTTP_X_AUTH_PROJECT_ID']
is_admin = user_id == 'admin'
return context.RequestContext(
user_id, project_id, is_admin=is_admin, **kwargs)
self.useFixture(fixtures.MonkeyPatch(
'nova.api.auth.NovaKeystoneContext._create_context', fake_ctx))
class OSMetadataServer(fixtures.Fixture): class OSMetadataServer(fixtures.Fixture):
"""Create an OS Metadata API server as a fixture. """Create an OS Metadata API server as a fixture.

@ -12,33 +12,64 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ddt
import fixtures
import webob
from nova.api.openstack import api_version_request as avr from nova.api.openstack import api_version_request as avr
from nova.tests.functional.api_sample_tests import api_sample_base from nova.tests.functional.api_sample_tests import api_sample_base
@ddt.ddt
class VersionsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21): class VersionsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
"""Validate that proper version documents can be fetched without auth."""
# Here we want to avoid stubbing keystone middleware. That will cause
# "real" keystone middleware to run (and fail) if it's in the pipeline.
# (The point of this test is to prove we do version discovery through
# pipelines that *don't* authenticate.)
STUB_KEYSTONE = False
sample_dir = 'versions' sample_dir = 'versions'
_use_project_id = False _use_project_id = False
# NOTE(gmann): Setting empty scenario for 'version' API testing # NOTE(gmann): Setting empty scenario for 'version' API testing
# as those does not send request on particular endpoint and running # as those does not send request on particular endpoint and running
# its tests alone is enough. # its tests alone is enough.
scenarios = [] scenarios = []
max_api_version = avr.max_api_version().get_string() max_api_version = {'max_api_version': avr.max_api_version().get_string()}
def test_versions_get(self): def setUp(self):
response = self._do_get('', strip_version=True) super(VersionsSampleJsonTest, self).setUp()
self._verify_response('versions-get-resp', # Version documents are supposed to be available without auth, so make
{'max_api_version': self.max_api_version}, # the auth middleware "fail" authentication.
self.useFixture(fixtures.MockPatch(
# [api]auth_strategy is set to noauth2 by the ConfFixture
'nova.api.openstack.auth.NoAuthMiddlewareBase.base_call',
return_value=webob.Response(status=401)))
def _get(self, url):
return self._do_get(
url,
# Since we're explicitly getting discovery endpoints, strip the
# automatic /v2[.1] added by the fixture.
strip_version=True)
@ddt.data('', '/')
def test_versions_get_base(self, url):
response = self._get(url)
self._verify_response('versions-get-resp', self.max_api_version,
response, 200, update_links=False) response, 200, update_links=False)
def test_versions_get_v2(self): @ddt.data(('/v2', 'v2-version-get-resp', {}),
response = self._do_get('/v2', strip_version=True) ('/v2/', 'v2-version-get-resp', {}),
self._verify_response('v2-version-get-resp', {}, ('/v2.1', 'v21-version-get-resp', max_api_version),
response, 200, update_links=False) ('/v2.1/', 'v21-version-get-resp', max_api_version))
@ddt.unpack
def test_versions_get_v21(self): def test_versions_get_versioned(self, url, tplname, subs):
response = self._do_get('/v2.1', strip_version=True) response = self._get(url)
self._verify_response('v21-version-get-resp', # TODO(efried): This is bug 1845530 whereby we try to authenticate at
{'max_api_version': self.max_api_version}, # the versioned discovery endpoint.
response, 200, update_links=False) self.assertEqual(401, response.status_code)
# TODO(efried): Uncomment when bug 1845530 is resolved
# self._verify_response(tplname, subs, response, 200,
# update_links=False)

@ -362,6 +362,10 @@ class _IntegratedTestBase(test.TestCase, InstanceHelperMixin):
# This indicates whether to include the project ID in the URL for API # This indicates whether to include the project ID in the URL for API
# requests through OSAPIFixture. Overridden by subclasses. # requests through OSAPIFixture. Overridden by subclasses.
_use_project_id = False _use_project_id = False
# Override this in subclasses to avoid stubbing keystonemiddleware and
# NovaKeystoneContext, thus making those middlewares behave as they would
# in real life (i.e. try to do real authentication).
STUB_KEYSTONE = True
def setUp(self): def setUp(self):
super(_IntegratedTestBase, self).setUp() super(_IntegratedTestBase, self).setUp()
@ -398,7 +402,8 @@ class _IntegratedTestBase(test.TestCase, InstanceHelperMixin):
self.api_fixture = self.useFixture( self.api_fixture = self.useFixture(
nova_fixtures.OSAPIFixture( nova_fixtures.OSAPIFixture(
api_version=self.api_major_version, api_version=self.api_major_version,
use_project_id_in_urls=self._use_project_id)) use_project_id_in_urls=self._use_project_id,
stub_keystone=self.STUB_KEYSTONE))
# if the class needs to run as admin, make the api endpoint # if the class needs to run as admin, make the api endpoint
# the admin, otherwise it's safer to run as non admin user. # the admin, otherwise it's safer to run as non admin user.