Deprecate admin_token_auth
Deprecate the admin_token_auth middleware in favor of using keystone-manage bootstrap Change-Id: Ib4ca153af2855911f9261081e7e442dfbc28f652 bp: deprecated-as-of-mitaka bp: bootstrap
This commit is contained in:
parent
6982c50b48
commit
5286b4a297
|
@ -13,6 +13,8 @@ use = egg:keystone#build_auth_context
|
|||
use = egg:keystone#token_auth
|
||||
|
||||
[filter:admin_token_auth]
|
||||
# This is deprecated in the M release and will be removed in the O release.
|
||||
# Use `keystone-manage bootstrap` and remove this from the pipelines below.
|
||||
use = egg:keystone#admin_token_auth
|
||||
|
||||
[filter:json_body]
|
||||
|
@ -52,17 +54,17 @@ use = egg:keystone#admin_service
|
|||
[pipeline:public_api]
|
||||
# The last item in this pipeline must be public_service or an equivalent
|
||||
# application. It cannot be a filter.
|
||||
pipeline = cors sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension public_service
|
||||
pipeline = cors sizelimit url_normalize request_id build_auth_context token_auth json_body ec2_extension public_service
|
||||
|
||||
[pipeline:admin_api]
|
||||
# The last item in this pipeline must be admin_service or an equivalent
|
||||
# application. It cannot be a filter.
|
||||
pipeline = cors sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension s3_extension admin_service
|
||||
pipeline = cors sizelimit url_normalize request_id build_auth_context token_auth json_body ec2_extension s3_extension admin_service
|
||||
|
||||
[pipeline:api_v3]
|
||||
# The last item in this pipeline must be service_v3 or an equivalent
|
||||
# application. It cannot be a filter.
|
||||
pipeline = cors sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension_v3 s3_extension service_v3
|
||||
pipeline = cors sizelimit url_normalize request_id build_auth_context token_auth json_body ec2_extension_v3 s3_extension service_v3
|
||||
|
||||
[app:public_version_service]
|
||||
use = egg:keystone#public_version_service
|
||||
|
|
|
@ -13,14 +13,17 @@
|
|||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_log import versionutils
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from keystone.common import wsgi
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
# Header used to transmit the auth token
|
||||
AUTH_TOKEN_HEADER = 'X-Auth-Token'
|
||||
|
@ -56,6 +59,16 @@ class AdminTokenAuthMiddleware(wsgi.Middleware):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, application):
|
||||
super(AdminTokenAuthMiddleware, self).__init__(application)
|
||||
msg = _("Remove admin_token_auth from the paste-ini file, the "
|
||||
"admin_token_auth middleware has been deprecated in favor of "
|
||||
"using keystone-manage bootstrap and real users after "
|
||||
"bootstrap process. Update the [pipeline:api_v3], "
|
||||
"[pipeline:admin_api], and [pipeline:public_api] sections "
|
||||
"accordingly, as it will be removed in the O release.")
|
||||
versionutils.report_deprecated_feature(LOG, msg)
|
||||
|
||||
def process_request(self, request):
|
||||
token = request.headers.get(AUTH_TOKEN_HEADER)
|
||||
context = request.environ.get(CONTEXT_ENV, {})
|
||||
|
|
|
@ -748,6 +748,17 @@ class TestCase(BaseTestCase):
|
|||
setattr(self, attrname, user_copy)
|
||||
fixtures_to_cleanup.append(attrname)
|
||||
|
||||
for role_assignment in fixtures.ROLE_ASSIGNMENTS:
|
||||
role_id = role_assignment['role_id']
|
||||
user = role_assignment['user']
|
||||
tenant_id = role_assignment['tenant_id']
|
||||
user_id = getattr(self, 'user_%s' % user)['id']
|
||||
try:
|
||||
self.assignment_api.add_role_to_user_and_project(
|
||||
user_id, tenant_id, role_id)
|
||||
except exception.Conflict:
|
||||
pass
|
||||
|
||||
self.addCleanup(self.cleanup_instance(*fixtures_to_cleanup))
|
||||
|
||||
def _paste_config(self, config):
|
||||
|
|
|
@ -55,6 +55,15 @@ TENANTS = [
|
|||
|
||||
# NOTE(ja): a role of keystone_admin is done in setUp
|
||||
USERS = [
|
||||
# NOTE(morganfainberg): Admin user for replacing admin_token_auth
|
||||
{
|
||||
'id': 'reqadmin',
|
||||
'name': 'REQ_ADMIN',
|
||||
'domain_id': DEFAULT_DOMAIN_ID,
|
||||
'password': 'password',
|
||||
'tenants': [],
|
||||
'enabled': True
|
||||
},
|
||||
{
|
||||
'id': 'foo',
|
||||
'name': 'FOO',
|
||||
|
@ -124,6 +133,15 @@ ROLES = [
|
|||
}
|
||||
]
|
||||
|
||||
# NOTE(morganfainberg): Admin assignment for replacing admin_token_auth
|
||||
ROLE_ASSIGNMENTS = [
|
||||
{
|
||||
'user': 'reqadmin',
|
||||
'tenant_id': 'service',
|
||||
'role_id': 'admin'
|
||||
},
|
||||
]
|
||||
|
||||
DOMAINS = [{'description':
|
||||
(u'Owns users and tenants (i.e. projects)'
|
||||
' available on Identity API v2.'),
|
||||
|
|
|
@ -216,6 +216,17 @@ class RestfulTestCase(unit.TestCase):
|
|||
r = self.public_request(method='POST', path='/v2.0/tokens', body=body)
|
||||
return self._get_token_id(r)
|
||||
|
||||
def get_admin_token(self):
|
||||
return self._get_token({
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': self.user_reqadmin['name'],
|
||||
'password': self.user_reqadmin['password']
|
||||
},
|
||||
'tenantId': 'service'
|
||||
}
|
||||
})
|
||||
|
||||
def get_unscoped_token(self):
|
||||
"""Convenience method so that we can test authenticated requests."""
|
||||
return self._get_token({
|
||||
|
|
|
@ -1513,7 +1513,7 @@ class TestFernetTokenProviderV2(RestfulTestCase):
|
|||
self.admin_request(
|
||||
method='GET',
|
||||
path=path,
|
||||
token=CONF.admin_token,
|
||||
token=self.get_admin_token(),
|
||||
expected_status=http_client.OK)
|
||||
|
||||
def test_rescoped_tokens_maintain_original_expiration(self):
|
||||
|
|
|
@ -344,6 +344,15 @@ class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase,
|
|||
self.default_domain_user_id, self.project_id,
|
||||
self.role_id)
|
||||
|
||||
# Create "req_admin" user for simulating a real user instead of the
|
||||
# admin_token_auth middleware
|
||||
self.user_reqadmin = unit.create_user(self.identity_api,
|
||||
DEFAULT_DOMAIN_ID)
|
||||
self.assignment_api.add_role_to_user_and_project(
|
||||
self.user_reqadmin['id'],
|
||||
self.default_domain_project_id,
|
||||
self.role_id)
|
||||
|
||||
self.region = unit.new_region_ref()
|
||||
self.region_id = self.region['id']
|
||||
self.catalog_api.create_region(self.region)
|
||||
|
@ -375,6 +384,34 @@ class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase,
|
|||
|
||||
return project
|
||||
|
||||
def get_admin_token(self):
|
||||
"""Convenience method so that we can test authenticated requests."""
|
||||
r = self.admin_request(
|
||||
method='POST',
|
||||
path='/v3/auth/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'identity': {
|
||||
'methods': ['password'],
|
||||
'password': {
|
||||
'user': {
|
||||
'name': self.user_reqadmin['name'],
|
||||
'password': self.user_reqadmin['password'],
|
||||
'domain': {
|
||||
'id': self.user_reqadmin['domain_id']
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'scope': {
|
||||
'project': {
|
||||
'id': self.default_domain_project_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return r.headers.get('X-Subject-Token')
|
||||
|
||||
def get_unscoped_token(self):
|
||||
"""Convenience method so that we can test authenticated requests."""
|
||||
r = self.admin_request(
|
||||
|
@ -1285,6 +1322,37 @@ class VersionTestCase(RestfulTestCase):
|
|||
pass
|
||||
|
||||
|
||||
# NOTE(morganfainberg): To be removed when admin_token_auth is removed. This
|
||||
# has been split out to allow testing admin_token auth without enabling it
|
||||
# for other tests.
|
||||
class AuthContextMiddlewareAdminTokenTestCase(RestfulTestCase):
|
||||
EXTENSION_TO_ADD = 'admin_token_auth'
|
||||
|
||||
# NOTE(morganfainberg): This is knowingly copied from below for simplicity
|
||||
# during the deprecation cycle.
|
||||
def _middleware_request(self, token, extra_environ=None):
|
||||
|
||||
def application(environ, start_response):
|
||||
body = 'body'
|
||||
headers = [('Content-Type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body)))]
|
||||
start_response('200 OK', headers)
|
||||
return [body]
|
||||
|
||||
app = webtest.TestApp(middleware.AuthContextMiddleware(application),
|
||||
extra_environ=extra_environ)
|
||||
resp = app.get('/', headers={middleware.AUTH_TOKEN_HEADER: token})
|
||||
self.assertEqual('body', resp.text) # just to make sure it worked
|
||||
return resp.request
|
||||
|
||||
def test_admin_token_auth_context(self):
|
||||
# test to make sure AuthContextMiddleware does not attempt to build
|
||||
# auth context if the incoming auth token is the special admin token
|
||||
req = self._middleware_request(CONF.admin_token)
|
||||
auth_context = req.environ.get(authorization.AUTH_CONTEXT_ENV)
|
||||
self.assertDictEqual({}, auth_context)
|
||||
|
||||
|
||||
# NOTE(gyee): test AuthContextMiddleware here instead of test_middleware.py
|
||||
# because we need the token
|
||||
class AuthContextMiddlewareTestCase(RestfulTestCase):
|
||||
|
@ -1324,13 +1392,6 @@ class AuthContextMiddlewareTestCase(RestfulTestCase):
|
|||
self.assertEqual(overridden_context,
|
||||
req.environ.get(authorization.AUTH_CONTEXT_ENV))
|
||||
|
||||
def test_admin_token_auth_context(self):
|
||||
# test to make sure AuthContextMiddleware does not attempt to build
|
||||
# auth context if the incoming auth token is the special admin token
|
||||
req = self._middleware_request(CONF.admin_token)
|
||||
auth_context = req.environ.get(authorization.AUTH_CONTEXT_ENV)
|
||||
self.assertDictEqual({}, auth_context)
|
||||
|
||||
def test_unscoped_token_auth_context(self):
|
||||
unscoped_token = self.get_unscoped_token()
|
||||
req = self._middleware_request(unscoped_token)
|
||||
|
|
|
@ -160,7 +160,7 @@ class TokenAPITests(object):
|
|||
# 5) Validate token using v2 API.
|
||||
self.admin_request(
|
||||
path='/v2.0/tokens/%s' % v3_token,
|
||||
token=CONF.admin_token,
|
||||
token=self.get_admin_token(),
|
||||
method='GET')
|
||||
|
||||
def test_v3_v2_intermix_domain_scoped_token_failed(self):
|
||||
|
@ -179,7 +179,7 @@ class TokenAPITests(object):
|
|||
self.admin_request(
|
||||
method='GET',
|
||||
path='/v2.0/tokens/%s' % v3_token,
|
||||
token=CONF.admin_token,
|
||||
token=self.get_admin_token(),
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
def test_v3_v2_intermix_non_default_project_succeed(self):
|
||||
|
@ -193,7 +193,7 @@ class TokenAPITests(object):
|
|||
self.admin_request(
|
||||
method='GET',
|
||||
path='/v2.0/tokens/%s' % v3_token,
|
||||
token=CONF.admin_token)
|
||||
token=self.get_admin_token())
|
||||
|
||||
def test_v3_v2_intermix_non_default_user_succeed(self):
|
||||
self.assignment_api.create_grant(
|
||||
|
@ -211,7 +211,7 @@ class TokenAPITests(object):
|
|||
self.admin_request(
|
||||
method='GET',
|
||||
path='/v2.0/tokens/%s' % v3_token,
|
||||
token=CONF.admin_token)
|
||||
token=self.get_admin_token())
|
||||
|
||||
def test_v3_v2_intermix_domain_scope_failed(self):
|
||||
self.assignment_api.create_grant(
|
||||
|
@ -227,7 +227,7 @@ class TokenAPITests(object):
|
|||
# v2 cannot reference projects outside the default domain
|
||||
self.admin_request(
|
||||
path='/v2.0/tokens/%s' % v3_token,
|
||||
token=CONF.admin_token,
|
||||
token=self.get_admin_token(),
|
||||
method='GET',
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
|
@ -242,7 +242,7 @@ class TokenAPITests(object):
|
|||
# now validate the v3 token with v2 API
|
||||
r = self.admin_request(
|
||||
path='/v2.0/tokens/%s' % v3_token,
|
||||
token=CONF.admin_token,
|
||||
token=self.get_admin_token(),
|
||||
method='GET')
|
||||
v2_token_data = r.result
|
||||
|
||||
|
@ -268,7 +268,7 @@ class TokenAPITests(object):
|
|||
r = self.admin_request(
|
||||
method='GET',
|
||||
path='/v2.0/tokens/%s' % v3_token,
|
||||
token=CONF.admin_token)
|
||||
token=self.get_admin_token())
|
||||
v2_token_data = r.result
|
||||
|
||||
self.assertEqual(v2_token_data['access']['user']['id'],
|
||||
|
@ -881,7 +881,7 @@ class TestPKITokenAPIs(test_v3.RestfulTestCase, TokenAPITests, TokenDataTests):
|
|||
token = cms.cms_hash_token(token)
|
||||
path = '/v2.0/tokens/%s' % (token)
|
||||
resp = self.admin_request(path=path,
|
||||
token=CONF.admin_token,
|
||||
token=self.get_admin_token(),
|
||||
method='GET')
|
||||
v2_token = resp.result
|
||||
self.assertEqual(v2_token['access']['user']['id'],
|
||||
|
|
|
@ -266,7 +266,7 @@ class CredentialTestCase(CredentialBaseTestCase):
|
|||
r = self.post(
|
||||
'/credentials',
|
||||
body={'credential': ref},
|
||||
token=CONF.admin_token)
|
||||
token=self.get_admin_token())
|
||||
self.assertValidCredentialResponse(r, ref)
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,36 @@ from keystone.tests.unit import test_v3
|
|||
CONF = cfg.CONF
|
||||
|
||||
|
||||
# NOTE(morganfainberg): To be removed when admin_token_auth middleware is
|
||||
# removed. This was moved to it's own testcase so it can setup the
|
||||
# admin_token_auth pipeline without impacting other tests.
|
||||
class IdentityTestCaseStaticAdminToken(test_v3.RestfulTestCase):
|
||||
EXTENSION_TO_ADD = 'admin_token_auth'
|
||||
|
||||
def test_list_users_with_static_admin_token_and_multiple_backends(self):
|
||||
# domain-specific operations with the bootstrap ADMIN token is
|
||||
# disallowed when domain-specific drivers are enabled
|
||||
self.config_fixture.config(group='identity',
|
||||
domain_specific_drivers_enabled=True)
|
||||
self.get('/users', token=CONF.admin_token,
|
||||
expected_status=exception.Unauthorized.code)
|
||||
|
||||
def test_create_user_with_admin_token_and_no_domain(self):
|
||||
"""Call ``POST /users`` with admin token but no domain id.
|
||||
|
||||
It should not be possible to use the admin token to create a user
|
||||
while not explicitly passing the domain in the request body.
|
||||
|
||||
"""
|
||||
# Passing a valid domain id to new_user_ref() since domain_id is
|
||||
# not an optional parameter.
|
||||
ref = unit.new_user_ref(domain_id=self.domain_id)
|
||||
# Delete the domain id before sending the request.
|
||||
del ref['domain_id']
|
||||
self.post('/users', body={'user': ref}, token=CONF.admin_token,
|
||||
expected_status=http_client.BAD_REQUEST)
|
||||
|
||||
|
||||
class IdentityTestCase(test_v3.RestfulTestCase):
|
||||
"""Test users and groups."""
|
||||
|
||||
|
@ -109,24 +139,9 @@ class IdentityTestCase(test_v3.RestfulTestCase):
|
|||
def test_create_user_with_admin_token_and_domain(self):
|
||||
"""Call ``POST /users`` with admin token and domain id."""
|
||||
ref = unit.new_user_ref(domain_id=self.domain_id)
|
||||
self.post('/users', body={'user': ref}, token=CONF.admin_token,
|
||||
self.post('/users', body={'user': ref}, token=self.get_admin_token(),
|
||||
expected_status=http_client.CREATED)
|
||||
|
||||
def test_create_user_with_admin_token_and_no_domain(self):
|
||||
"""Call ``POST /users`` with admin token but no domain id.
|
||||
|
||||
It should not be possible to use the admin token to create a user
|
||||
while not explicitly passing the domain in the request body.
|
||||
|
||||
"""
|
||||
# Passing a valid domain id to new_user_ref() since domain_id is
|
||||
# not an optional parameter.
|
||||
ref = unit.new_user_ref(domain_id=self.domain_id)
|
||||
# Delete the domain id before sending the request.
|
||||
del ref['domain_id']
|
||||
self.post('/users', body={'user': ref}, token=CONF.admin_token,
|
||||
expected_status=http_client.BAD_REQUEST)
|
||||
|
||||
def test_user_management_normalized_keys(self):
|
||||
"""Illustrate the inconsistent handling of hyphens in keys.
|
||||
|
||||
|
@ -250,14 +265,6 @@ class IdentityTestCase(test_v3.RestfulTestCase):
|
|||
# which should fail
|
||||
r = self.get('/users', expected_status=exception.Unauthorized.code)
|
||||
|
||||
def test_list_users_with_static_admin_token_and_multiple_backends(self):
|
||||
# domain-specific operations with the bootstrap ADMIN token is
|
||||
# disallowed when domain-specific drivers are enabled
|
||||
self.config_fixture.config(group='identity',
|
||||
domain_specific_drivers_enabled=True)
|
||||
self.get('/users', token=CONF.admin_token,
|
||||
expected_status=exception.Unauthorized.code)
|
||||
|
||||
def test_list_users_no_default_project(self):
|
||||
"""Call ``GET /users`` making sure no default_project_id."""
|
||||
user = unit.new_user_ref(self.domain_id)
|
||||
|
|
|
@ -268,7 +268,7 @@ class TestTrustOperations(test_v3.RestfulTestCase):
|
|||
# now validate the v3 token with v2 API
|
||||
path = '/v2.0/tokens/%s' % (token)
|
||||
self.admin_request(
|
||||
path=path, token=CONF.admin_token,
|
||||
path=path, token=self.get_admin_token(),
|
||||
method='GET', expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
def test_v3_v2_intermix_trustor_not_in_default_domain_failed(self):
|
||||
|
@ -303,7 +303,7 @@ class TestTrustOperations(test_v3.RestfulTestCase):
|
|||
# now validate the v3 token with v2 API
|
||||
path = '/v2.0/tokens/%s' % (token)
|
||||
self.admin_request(
|
||||
path=path, token=CONF.admin_token,
|
||||
path=path, token=self.get_admin_token(),
|
||||
method='GET', expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
def test_v3_v2_intermix_project_not_in_default_domain_failed(self):
|
||||
|
@ -343,7 +343,7 @@ class TestTrustOperations(test_v3.RestfulTestCase):
|
|||
# ensure the token is invalid against v2
|
||||
path = '/v2.0/tokens/%s' % (token)
|
||||
self.admin_request(
|
||||
path=path, token=CONF.admin_token,
|
||||
path=path, token=self.get_admin_token(),
|
||||
method='GET', expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
def test_exercise_trust_scoped_token_without_impersonation(self):
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
---
|
||||
upgrade:
|
||||
- >
|
||||
The ``keystone-paste.ini`` file must be updated to remove middleware filters
|
||||
and their use in ``[pipeline:api_v3]``, ``[pipeline:public_api]``,
|
||||
and ``[pipeline:admin_api]``. Remove ``[filter:admin_token_auth]``. See the
|
||||
sample `keystone-paste.ini
|
||||
<https://git.openstack.org/cgit/openstack/keystone/tree/etc/keystone-paste.ini>`_
|
||||
file for guidance.
|
||||
deprecations:
|
||||
- >
|
||||
[`blueprint deprecated-as-of-mitaka <https://blueprints.launchpad.net/keystone/+spec/deprecated-as-of-mitaka>`_]
|
||||
|
@ -19,3 +27,13 @@ deprecations:
|
|||
As of the Mitaka release, the auth plugin ``keystone.auth.plugins.saml2.Saml2``
|
||||
has been deprecated. It is recommended to use ``keystone.auth.plugins.mapped.Mapped``
|
||||
instead. The ``saml2`` plugin will be removed in the 'O' release.
|
||||
- >
|
||||
[`blueprint deprecated-as-of-mitaka <https://blueprints.launchpad.net/keystone/+spec/deprecated-as-of-mitaka>`_]
|
||||
As of the Mitaka release, the simple_cert_extension is deprecated since it
|
||||
is only used in support of the PKI and PKIz token formats. It will be
|
||||
removed in the 'O' release.
|
||||
- >
|
||||
[`blueprint deprecated-as-of-mitaka <https://blueprints.launchpad.net/keystone/+spec/deprecated-as-of-mitaka>`_]
|
||||
As of the Mitaka release, the use of ``admin_token_auth`` is deprecated in favor
|
||||
of using the ``keystone-manage bootstrap`` CLI. It will be removed in the
|
||||
'O' release.
|
||||
|
|
Loading…
Reference in New Issue