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:
Morgan Fainberg 2016-02-02 13:52:49 -08:00 committed by Samuel de Medeiros Queiroz
parent 6982c50b48
commit 5286b4a297
12 changed files with 189 additions and 48 deletions

View File

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

View File

@ -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, {})

View File

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

View File

@ -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.'),

View File

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

View File

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

View File

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

View File

@ -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'],

View File

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

View File

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

View File

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

View File

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