api: Remove '[api] auth_strategy', NoAuthMiddlware

Also remove associated tests.

Change-Id: I098f1d4b61fabb10c4da3de02f10337b2ad6c544
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2025-06-09 09:45:27 +01:00
parent 4baa108c04
commit d238306aa6
13 changed files with 24 additions and 369 deletions

View File

@@ -37,7 +37,6 @@ configuration file, for example:
[service_user]
send_service_user_token = true
auth_url = $AUTH_URL
auth_strategy = keystone
auth_type = password
project_domain_name = $PROJECT_DOMAIN_NAME
project_name = service

View File

@@ -101,8 +101,7 @@ following to a :file:`nova-api.conf` file:
.. note::
This does not include configuration options that are not metadata-specific
but are nonetheless required, such as
:oslo.config:option:`api.auth_strategy`.
but are nonetheless required.
Configuring the application to use the ``DynamicJSON`` vendordata provider is
more involved and is not covered here.
@@ -138,8 +137,7 @@ file:
.. note::
This does not include configuration options that are not metadata-specific
but are nonetheless required, such as
:oslo.config:option:`api.auth_strategy`.
but are nonetheless required.
For information about configuring the neutron side of the metadata service,
refer to the :neutron-doc:`neutron configuration guide

View File

@@ -31,16 +31,10 @@ use = call:nova.api.openstack.urlmap:urlmap_factory
[composite:openstack_compute_api_v21]
use = call:nova.api.auth:pipeline_factory_v21
keystone = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler authtoken keystonecontext osapi_compute_app_v21
# DEPRECATED: The [api]auth_strategy conf option is deprecated and will be
# removed in a subsequent release, whereupon this pipeline will be unreachable.
noauth2 = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler noauth2 osapi_compute_app_v21
[composite:openstack_compute_api_v21_legacy_v2_compatible]
use = call:nova.api.auth:pipeline_factory_v21
keystone = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21
# DEPRECATED: The [api]auth_strategy conf option is deprecated and will be
# removed in a subsequent release, whereupon this pipeline will be unreachable.
noauth2 = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler noauth2 legacy_v2_compatible osapi_compute_app_v21
[filter:request_log]
paste.filter_factory = nova.api.openstack.requestlog:RequestLog.factory
@@ -51,11 +45,6 @@ paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory
[filter:faultwrap]
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
# DEPRECATED: NoAuthMiddleware will be removed in a subsequent release,
# whereupon this filter will cease to function.
[filter:noauth2]
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory
[filter:osprofiler]
paste.filter_factory = nova.profiler:WsgiMiddleware.factory

View File

@@ -17,7 +17,6 @@ Common Auth Middleware.
"""
from oslo_log import log as logging
from oslo_log import versionutils
from oslo_serialization import jsonutils
import webob.dec
import webob.exc
@@ -43,16 +42,7 @@ def _load_pipeline(loader, pipeline):
def pipeline_factory_v21(loader, global_conf, **local_conf):
"""A paste pipeline replica that keys off of auth_strategy."""
auth_strategy = CONF.api.auth_strategy
if auth_strategy == 'noauth2':
versionutils.report_deprecated_feature(
LOG,
"'[api]auth_strategy=noauth2' is deprecated as of the 21.0.0 "
"Ussuri release and will be removed in a future release. Please "
"remove any 'noauth2' entries from api-paste.ini; only the "
"'keystone' pipeline is supported."
)
return _load_pipeline(loader, local_conf[auth_strategy].split())
return _load_pipeline(loader, local_conf['keystone'].split())
class InjectContext(wsgi.Middleware):

View File

@@ -1,85 +0,0 @@
# Copyright 2013 IBM Corp.
# Copyright 2010 OpenStack Foundation
# All Rights Reserved.
#
# 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_middleware import request_id
import webob.dec
import webob.exc
from nova.api.openstack import wsgi
from nova.api import wsgi as base_wsgi
import nova.conf
from nova import context
CONF = nova.conf.CONF
class NoAuthMiddlewareBase(base_wsgi.Middleware):
"""Return a fake token if one isn't specified."""
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')
if project_id_in_path:
os_url = '/'.join([req.url.rstrip('/'), project_id])
else:
os_url = req.url.rstrip('/')
res = webob.Response()
# NOTE(vish): This is expecting and returning Auth(1.1), whereas
# keystone uses 2.0 auth. We should probably allow
# 2.0 auth here as well.
res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id)
res.headers['X-Server-Management-Url'] = os_url
res.content_type = 'text/plain'
res.status = '204'
return res
token = req.headers['X-Auth-Token']
user_id, _sep, project_id = token.partition(':')
project_id = project_id or user_id
remote_address = getattr(req, 'remote_addr', '127.0.0.1')
is_admin = always_admin or (user_id == 'admin')
ctx = context.RequestContext(
user_id, project_id, is_admin=is_admin,
remote_address=remote_address,
request_id=req.environ.get(request_id.ENV_REQUEST_ID))
req.environ['nova.context'] = ctx
return self.application
class NoAuthMiddleware(NoAuthMiddlewareBase):
"""Return a fake token if one isn't specified.
noauth2 provides admin privs if 'admin' is provided as the user id.
"""
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
return self.base_call(req, True, always_admin=False)
class NoAuthMiddlewareV2_18(NoAuthMiddlewareBase):
"""Return a fake token if one isn't specified.
This provides a version of the middleware which does not add
project_id into server management urls.
"""
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
return self.base_call(req, False, always_admin=False)

View File

@@ -21,27 +21,6 @@ api_group = cfg.OptGroup('api',
Options under this group are used to define Nova API.
""")
auth_opts = [
cfg.StrOpt("auth_strategy",
default="keystone",
choices=[
("keystone", "Use keystone for authentication."),
("noauth2", "Designed for testing only, as it does no actual "
"credential checking. 'noauth2' provides administrative "
"credentials only if 'admin' is specified as the username."),
],
deprecated_for_removal=True,
deprecated_since='21.0.0',
deprecated_reason="""
The only non-default choice, ``noauth2``, is for internal development and
testing purposes only and should not be used in deployments. This option and
its middleware, NoAuthMiddleware[V2_18], will be removed in a future release.
""",
help="""
Determine the strategy to use for authentication.
"""),
]
metadata_opts = [
cfg.StrOpt("config_drive_skip_versions",
default=("1.0 2007-01-19 2007-03-01 2007-08-29 2007-10-10 "
@@ -411,13 +390,14 @@ issues you are seeing to the Nova team so we can improve our schemas.
),
]
API_OPTS = (auth_opts +
metadata_opts +
file_opts +
osapi_opts +
os_network_opts +
enable_inst_pw_opts +
validation_opts)
API_OPTS = (
metadata_opts +
file_opts +
osapi_opts +
os_network_opts +
enable_inst_pw_opts +
validation_opts
)
def register_opts(conf):

View File

@@ -10,8 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from .api_paste import ApiPasteNoProjectId # noqa: F401, H304
from .api_paste import ApiPasteV21Fixture # noqa: F401, H304
from .cast_as_call import CastAsCallFixture # noqa: F401, H304
from .cinder import CinderFixture # noqa: F401, H304
from .conf import ConfFixture # noqa: F401, H304, F403

View File

@@ -1,57 +0,0 @@
# Copyright 2015 NEC Corporation. All rights reserved.
#
# 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 os
import fixtures
import nova.conf
from nova.conf import paths
CONF = nova.conf.CONF
class ApiPasteV21Fixture(fixtures.Fixture):
def _replace_line(self, target_file, line):
# TODO(johnthetubaguy) should really point the tests at /v2.1
target_file.write(line.replace(
"/v2: openstack_compute_api_v21_legacy_v2_compatible",
"/v2: openstack_compute_api_v21"))
def setUp(self):
super(ApiPasteV21Fixture, self).setUp()
CONF.set_default('api_paste_config',
paths.state_path_def('etc/nova/api-paste.ini'),
group='wsgi')
tmp_api_paste_dir = self.useFixture(fixtures.TempDir())
tmp_api_paste_file_name = os.path.join(tmp_api_paste_dir.path,
'fake_api_paste.ini')
with open(CONF.wsgi.api_paste_config, 'r') as orig_api_paste:
with open(tmp_api_paste_file_name, 'w') as tmp_file:
for line in orig_api_paste:
self._replace_line(tmp_file, line)
CONF.set_override('api_paste_config', tmp_api_paste_file_name,
group='wsgi')
class ApiPasteNoProjectId(ApiPasteV21Fixture):
def _replace_line(self, target_file, line):
line = line.replace(
"paste.filter_factory = nova.api.openstack.auth:"
"NoAuthMiddleware.factory",
"paste.filter_factory = nova.api.openstack.auth:"
"NoAuthMiddlewareV2_18.factory")
target_file.write(line)

View File

@@ -18,7 +18,6 @@ import testscenarios
import nova.conf
from nova.tests import fixtures
from nova.tests.fixtures import api_paste as api_paste_fixture
from nova.tests.functional import api_samples_test_base
CONF = nova.conf.CONF
@@ -71,17 +70,14 @@ class ApiSampleTestBaseV21(testscenarios.WithScenarios,
scenarios = [
# test v2 with the v2.1 compatibility stack
('v2', {
'api_major_version': 'v2'}),
('v2', {'api_major_version': 'v2'}),
# test v2.1 base microversion
('v2_1', {
'api_major_version': 'v2.1'}),
('v2_1', {'api_major_version': 'v2.1'}),
# test v2.18 code without project id
('v2_1_noproject_id', {
'api_major_version': 'v2.1',
'USE_PROJECT_ID': False,
'_additional_fixtures': [
api_paste_fixture.ApiPasteNoProjectId]})
(
'v2_1_noproject_id',
{'api_major_version': 'v2.1', 'USE_PROJECT_ID': False},
),
]
def setUp(self):

View File

@@ -12,9 +12,8 @@
# 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 ddt
import fixtures
import webob
from nova.api.openstack import api_version_request as avr
from nova.tests.functional.api_sample_tests import api_sample_base
@@ -39,15 +38,6 @@ class VersionsSampleJsonTest(api_sample_base.ApiSampleTestBaseV21):
scenarios = []
max_api_version = {'max_api_version': avr.max_api_version().get_string()}
def setUp(self):
super(VersionsSampleJsonTest, self).setUp()
# Version documents are supposed to be available without auth, so make
# 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,

View File

@@ -1,80 +0,0 @@
# Copyright 2013 IBM Corp.
# Copyright 2010 OpenStack Foundation
# All Rights Reserved.
#
# 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 testscenarios
from nova.api import openstack as openstack_api
from nova.api.openstack import auth
from nova.api.openstack import compute
from nova.api.openstack import urlmap
from nova import test
from nova.tests.unit.api.openstack import fakes
class TestNoAuthMiddleware(testscenarios.WithScenarios, test.NoDBTestCase):
scenarios = [
('project_id', {
'expected_url': 'http://localhost/v2.1/user1_project',
'auth_middleware': auth.NoAuthMiddleware}),
('no_project_id', {
'expected_url': 'http://localhost/v2.1',
'auth_middleware': auth.NoAuthMiddlewareV2_18}),
]
def setUp(self):
super(TestNoAuthMiddleware, self).setUp()
fakes.stub_out_networking(self)
api_v21 = openstack_api.FaultWrapper(
self.auth_middleware(
compute.APIRouterV21()
)
)
self.wsgi_app = urlmap.URLMap()
self.wsgi_app['/v2.1'] = api_v21
self.req_url = '/v2.1'
def test_authorize_user(self):
req = fakes.HTTPRequest.blank(self.req_url, base_url='')
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
req.headers['X-Auth-Project-Id'] = 'user1_project'
result = req.get_response(self.wsgi_app)
self.assertEqual(result.status, '204 No Content')
self.assertEqual(result.headers['X-Server-Management-Url'],
self.expected_url)
def test_authorize_user_trailing_slash(self):
# make sure it works with trailing slash on the request
self.req_url = self.req_url + '/'
req = fakes.HTTPRequest.blank(self.req_url, base_url='')
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
req.headers['X-Auth-Project-Id'] = 'user1_project'
result = req.get_response(self.wsgi_app)
self.assertEqual(result.status, '204 No Content')
self.assertEqual(result.headers['X-Server-Management-Url'],
self.expected_url)
def test_auth_token_no_empty_headers(self):
req = fakes.HTTPRequest.blank(self.req_url, base_url='')
req.headers['X-Auth-User'] = 'user1'
req.headers['X-Auth-Key'] = 'user1_key'
req.headers['X-Auth-Project-Id'] = 'user1_project'
result = req.get_response(self.wsgi_app)
self.assertEqual(result.status, '204 No Content')
self.assertNotIn('X-CDN-Management-Url', result.headers)
self.assertNotIn('X-Storage-Url', result.headers)

View File

@@ -1,50 +0,0 @@
# Copyright (c) 2018 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_middleware import request_id
from oslo_serialization import jsonutils
import webob
import webob.exc
import nova.api.openstack.auth
import nova.conf
from nova import test
CONF = nova.conf.CONF
class NoAuthMiddleware(test.NoDBTestCase):
def setUp(self):
super(NoAuthMiddleware, self).setUp()
@webob.dec.wsgify()
def fake_app(req):
self.context = req.environ['nova.context']
return webob.Response()
self.context = None
self.middleware = nova.api.openstack.auth.NoAuthMiddleware(fake_app)
self.request = webob.Request.blank('/')
self.request.headers['X_TENANT_ID'] = 'testtenantid'
self.request.headers['X_AUTH_TOKEN'] = 'testauthtoken'
self.request.headers['X_SERVICE_CATALOG'] = jsonutils.dumps({})
def test_request_id_extracted_from_env(self):
req_id = 'dummy-request-id'
self.request.headers['X_PROJECT_ID'] = 'testtenantid'
self.request.headers['X_USER_ID'] = 'testuserid'
self.request.environ[request_id.ENV_REQUEST_ID] = req_id
self.request.get_response(self.middleware)
self.assertEqual(req_id, self.context.request_id)

View File

@@ -133,28 +133,15 @@ class TestPipeLineFactory(test.NoDBTestCase):
def get_app(self, name):
return TestPipeLineFactory.FakeApp(name)
def _test_pipeline(self, pipeline, app):
for p in pipeline.split()[:-1]:
self.assertEqual(app.name, p)
self.assertIsInstance(app, TestPipeLineFactory.FakeFilter)
app = app.obj
self.assertEqual(app.name, pipeline.split()[-1])
self.assertIsInstance(app, TestPipeLineFactory.FakeApp)
@mock.patch('oslo_log.versionutils.report_deprecated_feature',
new=mock.NonCallableMock())
def test_pipeline_factory_v21(self):
fake_pipeline = 'test1 test2 test3'
CONF.set_override('auth_strategy', 'keystone', group='api')
app = nova.api.auth.pipeline_factory_v21(
TestPipeLineFactory.FakeLoader(), None, keystone=fake_pipeline)
self._test_pipeline(fake_pipeline, app)
@mock.patch('oslo_log.versionutils.report_deprecated_feature')
def test_pipeline_factory_v21_noauth2(self, mock_report_deprecated):
fake_pipeline = 'test1 test2 test3'
CONF.set_override('auth_strategy', 'noauth2', group='api')
app = nova.api.auth.pipeline_factory_v21(
TestPipeLineFactory.FakeLoader(), None, noauth2=fake_pipeline)
self._test_pipeline(fake_pipeline, app)
self.assertTrue(mock_report_deprecated.called)
for p in fake_pipeline.split()[:-1]:
self.assertEqual(app.name, p)
self.assertIsInstance(app, TestPipeLineFactory.FakeFilter)
app = app.obj
self.assertEqual(app.name, fake_pipeline.split()[-1])
self.assertIsInstance(app, TestPipeLineFactory.FakeApp)