Remove token bind capabilities

Token bind operations were deprecated in Pike with UUID tokens and
staged for removal in Rocky.

  https://review.openstack.org/#/c/428388/

This change does keep a configuration option around since it was not
officially deprecated with the rest of the token bind functionality.
The option is being officially deprecated in this commit and
additional context about the change was added to the help text for the
option.

bp removed-as-of-rocky

Change-Id: I7a42408893c782bcc20fb40ebba5f2d8af9da6a5
This commit is contained in:
Lance Bragstad 2018-06-22 19:31:31 +00:00
parent 5621786f75
commit eaa5435416
8 changed files with 12 additions and 415 deletions

View File

@ -49,11 +49,6 @@ class Base(base.AuthMethodHandler):
raise exception.Unauthorized(msg)
response_data['user_id'] = user_ref['id']
auth_type = (request.auth_type or '').lower()
if 'kerberos' in CONF.token.bind and auth_type == 'negotiate':
response_data.setdefault('bind', {})['kerberos'] = user_ref['name']
return base.AuthHandlerResponse(status=True, response_body=None,
response_data=response_data)

View File

@ -18,7 +18,6 @@ import six
from keystone.auth.plugins import base
from keystone.auth.plugins import mapped
from keystone.common import provider_api
from keystone.common import wsgi
import keystone.conf
from keystone import exception
from keystone.i18n import _
@ -103,8 +102,6 @@ def token_authenticate(request, token_ref):
raise exception.ForbiddenAction(
action=_('rescope a scoped token'))
wsgi.validate_token_bind(request.context_dict, token_ref)
# New tokens maintain the audit_id of the original token in the
# chain (if possible) as the second element in the audit data
# structure. Look for the last element in the audit data structure

View File

@ -41,7 +41,6 @@ from keystone.common import utils
import keystone.conf
from keystone import exception
from keystone.i18n import _
from keystone.models import token_model
CONF = keystone.conf.CONF
@ -57,68 +56,6 @@ JSON_ENCODE_CONTENT_TYPES = set(['application/json',
'application/json-home'])
def validate_token_bind(context, token_ref):
bind_mode = CONF.token.enforce_token_bind
if bind_mode == 'disabled':
return
if not isinstance(token_ref, token_model.KeystoneToken):
raise exception.UnexpectedError(_('token reference must be a '
'KeystoneToken type, got: %s') %
type(token_ref))
bind = token_ref.bind
# permissive and strict modes don't require there to be a bind
permissive = bind_mode in ('permissive', 'strict')
if not bind:
if permissive:
# no bind provided and none required
return
else:
msg = _('No bind information present in token.')
LOG.info(msg)
raise exception.Unauthorized(msg)
# get the named mode if bind_mode is not one of the known
name = None if permissive or bind_mode == 'required' else bind_mode
if name and name not in bind:
msg = (_('Named bind mode %(name)s not in bind information') %
{'name': name})
LOG.info(msg)
raise exception.Unauthorized(msg)
for bind_type, identifier in bind.items():
if bind_type == 'kerberos':
if (context['environment'].get('AUTH_TYPE', '').lower() !=
'negotiate'):
msg = _('Kerberos credentials required and not present')
LOG.info(msg)
raise exception.Unauthorized(msg)
if context['environment'].get('REMOTE_USER') != identifier:
msg = _('Kerberos credentials do not match those in bind')
LOG.info(msg)
raise exception.Unauthorized(msg)
LOG.info('Kerberos bind authentication successful')
elif bind_mode == 'permissive':
LOG.debug(("Ignoring unknown bind (due to permissive mode): "
"{%(bind_type)s: %(identifier)s}"), {
'bind_type': bind_type,
'identifier': identifier})
else:
msg = _('Could not verify unknown bind: {%(bind_type)s: '
'%(identifier)s}') % {
'bind_type': bind_type,
'identifier': identifier}
LOG.info(msg)
raise exception.Unauthorized(msg)
def best_match_language(req):
"""Determine the best available locale.

View File

@ -17,31 +17,18 @@ from oslo_log import versionutils
from keystone.conf import utils
bind = cfg.ListOpt(
'bind',
default=[],
help=utils.fmt("""
This is a list of external authentication mechanisms which should add token
binding metadata to tokens, such as `kerberos` or `x509`. Binding metadata is
enforced according to the `[token] enforce_token_bind` option.
"""))
enforce_token_bind = cfg.StrOpt(
'enforce_token_bind',
default='permissive',
deprecated_since=versionutils.deprecated.PIKE,
deprecated_for_removal=True,
help=utils.fmt("""
This controls the token binding enforcement policy on tokens presented to
keystone with token binding metadata (as specified by the `[token] bind`
option). `disabled` completely bypasses token binding validation. `permissive`
and `strict` do not require tokens to have binding metadata (but will validate
it if present), whereas `required` will always demand tokens to having binding
metadata. `permissive` will allow unsupported binding metadata to pass through
without validation (usually to be validated at another time by another
component), whereas `strict` and `required` will demand that the included
binding metadata be supported by keystone.
This is a list of external authentication mechanisms which should add token
binding metadata to tokens, such as `kerberos` or `x509`. Note that this option
is deprecated as keystone no longer supports binding metadata to tokens
directly. This option is silently ignored and will be removed in the future.
This option no longer has any impact on the behavior of tokens and can be
removed.
"""))
expiration = cfg.IntOpt(
@ -141,8 +128,6 @@ Defaults to two days.
GROUP_NAME = __name__.split('.')[-1]
ALL_OPTS = [
bind,
enforce_token_bind,
expiration,
provider,
caching,

View File

@ -39,10 +39,7 @@ class AuthContextMiddleware(provider_api.ProviderAPIMixin,
kwargs_to_fetch_token = True
def __init__(self, app):
bind = CONF.token.enforce_token_bind
super(AuthContextMiddleware, self).__init__(app,
log=LOG,
enforce_token_bind=bind)
super(AuthContextMiddleware, self).__init__(app, log=LOG)
def fetch_token(self, token, **kwargs):
try:

View File

@ -1,198 +0,0 @@
# Copyright 2013 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.
import copy
import uuid
from keystone.common import wsgi
from keystone import exception
from keystone.models import token_model
from keystone.tests import unit
from keystone.tests.unit import test_token_provider
KERBEROS_BIND = 'USER@REALM'
ANY = 'any'
class BindTest(unit.TestCase):
"""Test binding tokens to a Principal.
Even though everything in this file references kerberos the same concepts
will apply to all future binding mechanisms.
"""
def setUp(self):
super(BindTest, self).setUp()
self.TOKEN_BIND_KERB = copy.deepcopy(
test_token_provider.SAMPLE_V3_TOKEN)
self.TOKEN_BIND_KERB['token']['bind'] = {'kerberos': KERBEROS_BIND}
self.TOKEN_BIND_UNKNOWN = copy.deepcopy(
test_token_provider.SAMPLE_V3_TOKEN)
self.TOKEN_BIND_UNKNOWN['token']['bind'] = {'FOO': 'BAR'}
self.TOKEN_BIND_NONE = copy.deepcopy(
test_token_provider.SAMPLE_V3_TOKEN)
self.ALL_TOKENS = [self.TOKEN_BIND_KERB, self.TOKEN_BIND_UNKNOWN,
self.TOKEN_BIND_NONE]
def assert_kerberos_bind(self, tokens, bind_level,
use_kerberos=True, success=True):
if not isinstance(tokens, dict):
for token in tokens:
self.assert_kerberos_bind(token, bind_level,
use_kerberos=use_kerberos,
success=success)
elif use_kerberos == ANY:
for val in (True, False):
self.assert_kerberos_bind(tokens, bind_level,
use_kerberos=val, success=success)
else:
context = {'environment': {}}
self.config_fixture.config(group='token',
enforce_token_bind=bind_level)
if use_kerberos:
context['environment']['REMOTE_USER'] = KERBEROS_BIND
context['environment']['AUTH_TYPE'] = 'Negotiate'
# NOTE(morganfainberg): This assumes a V3 token.
token_ref = token_model.KeystoneToken(
token_id=uuid.uuid4().hex,
token_data=tokens)
if not success:
self.assertRaises(exception.Unauthorized,
wsgi.validate_token_bind,
context, token_ref)
else:
wsgi.validate_token_bind(context, token_ref)
# DISABLED
def test_bind_disabled_with_kerb_user(self):
self.assert_kerberos_bind(self.ALL_TOKENS,
bind_level='disabled',
use_kerberos=ANY,
success=True)
# PERMISSIVE
def test_bind_permissive_with_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='permissive',
use_kerberos=True,
success=True)
def test_bind_permissive_with_regular_token(self):
self.assert_kerberos_bind(self.TOKEN_BIND_NONE,
bind_level='permissive',
use_kerberos=ANY,
success=True)
def test_bind_permissive_without_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='permissive',
use_kerberos=False,
success=False)
def test_bind_permissive_with_unknown_bind(self):
self.assert_kerberos_bind(self.TOKEN_BIND_UNKNOWN,
bind_level='permissive',
use_kerberos=ANY,
success=True)
# STRICT
def test_bind_strict_with_regular_token(self):
self.assert_kerberos_bind(self.TOKEN_BIND_NONE,
bind_level='strict',
use_kerberos=ANY,
success=True)
def test_bind_strict_with_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='strict',
use_kerberos=True,
success=True)
def test_bind_strict_without_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='strict',
use_kerberos=False,
success=False)
def test_bind_strict_with_unknown_bind(self):
self.assert_kerberos_bind(self.TOKEN_BIND_UNKNOWN,
bind_level='strict',
use_kerberos=ANY,
success=False)
# REQUIRED
def test_bind_required_with_regular_token(self):
self.assert_kerberos_bind(self.TOKEN_BIND_NONE,
bind_level='required',
use_kerberos=ANY,
success=False)
def test_bind_required_with_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='required',
use_kerberos=True,
success=True)
def test_bind_required_without_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='required',
use_kerberos=False,
success=False)
def test_bind_required_with_unknown_bind(self):
self.assert_kerberos_bind(self.TOKEN_BIND_UNKNOWN,
bind_level='required',
use_kerberos=ANY,
success=False)
# NAMED
def test_bind_named_with_regular_token(self):
self.assert_kerberos_bind(self.TOKEN_BIND_NONE,
bind_level='kerberos',
use_kerberos=ANY,
success=False)
def test_bind_named_with_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='kerberos',
use_kerberos=True,
success=True)
def test_bind_named_without_kerb_user(self):
self.assert_kerberos_bind(self.TOKEN_BIND_KERB,
bind_level='kerberos',
use_kerberos=False,
success=False)
def test_bind_named_with_unknown_bind(self):
self.assert_kerberos_bind(self.TOKEN_BIND_UNKNOWN,
bind_level='kerberos',
use_kerberos=ANY,
success=False)
def test_bind_named_with_unknown_scheme(self):
self.assert_kerberos_bind(self.ALL_TOKENS,
bind_level='unknown',
use_kerberos=ANY,
success=False)

View File

@ -2335,55 +2335,6 @@ class TokenAPITests(object):
auth_info,
auth_context)
def test_bind_not_set_with_remote_user(self):
self.config_fixture.config(group='token', bind=[])
auth_data = self.build_authentication_request()
remote_user = self.default_domain_user['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'AUTH_TYPE': 'Negotiate'})
r = self.v3_create_token(auth_data)
token = self.assertValidUnscopedTokenResponse(r)
self.assertNotIn('bind', token)
def test_verify_with_bound_token(self):
self.config_fixture.config(group='token', bind='kerberos')
auth_data = self.build_authentication_request(
project_id=self.project['id'])
remote_user = self.default_domain_user['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'AUTH_TYPE': 'Negotiate'})
token = self.get_requested_token(auth_data)
headers = {'X-Subject-Token': token}
r = self.get('/auth/tokens', headers=headers, token=token)
token = self.assertValidProjectScopedTokenResponse(r)
self.assertEqual(self.default_domain_user['name'],
token['bind']['kerberos'])
def test_auth_with_bind_token(self):
self.config_fixture.config(group='token', bind=['kerberos'])
auth_data = self.build_authentication_request()
remote_user = self.default_domain_user['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'AUTH_TYPE': 'Negotiate'})
r = self.v3_create_token(auth_data)
# the unscoped token should have bind information in it
token = self.assertValidUnscopedTokenResponse(r)
self.assertEqual(remote_user, token['bind']['kerberos'])
token = r.headers.get('X-Subject-Token')
# using unscoped token with remote user succeeds
auth_params = {'token': token, 'project_id': self.project_id}
auth_data = self.build_authentication_request(**auth_params)
r = self.v3_create_token(auth_data)
token = self.assertValidProjectScopedTokenResponse(r)
# the bind information should be carried over from the original token
self.assertEqual(remote_user, token['bind']['kerberos'])
def test_fetch_expired_allow_expired(self):
self.config_fixture.config(group='token',
expiration=10,
@ -2582,28 +2533,6 @@ class TestFernetTokenAPIs(test_v3.RestfulTestCase, TokenAPITests,
self._validate_token(tampered_token,
expected_status=http_client.NOT_FOUND)
def test_verify_with_bound_token(self):
self.config_fixture.config(group='token', bind='kerberos')
auth_data = self.build_authentication_request(
project_id=self.project['id'])
remote_user = self.default_domain_user['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'AUTH_TYPE': 'Negotiate'})
# Bind not current supported by Fernet, see bug 1433311.
self.v3_create_token(auth_data,
expected_status=http_client.NOT_IMPLEMENTED)
def test_auth_with_bind_token(self):
self.config_fixture.config(group='token', bind=['kerberos'])
auth_data = self.build_authentication_request()
remote_user = self.default_domain_user['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'AUTH_TYPE': 'Negotiate'})
# Bind not current supported by Fernet, see bug 1433311.
self.v3_create_token(auth_data,
expected_status=http_client.NOT_IMPLEMENTED)
def test_trust_scoped_token_is_invalid_after_disabling_trustor(self):
# NOTE(amakarov): have to override this test for non-persistent tokens
# as TokenNotFound exception makes no sense for those.
@ -3638,32 +3567,6 @@ class AuthExternalDomainBehavior(object):
api.authenticate(request, auth_info, auth_context)
self.assertEqual(self.user['id'], auth_context['user_id'])
def test_project_id_scoped_with_remote_user(self):
self.config_fixture.config(group='token', bind=['kerberos'])
auth_data = self.build_authentication_request(
project_id=self.project['id'],
kerberos=self.kerberos)
remote_user = self.user['name']
remote_domain = self.domain['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'REMOTE_DOMAIN': remote_domain,
'AUTH_TYPE': 'Negotiate'})
r = self.v3_create_token(auth_data)
token = self.assertValidProjectScopedTokenResponse(r)
self.assertEqual(self.user['name'], token['bind']['kerberos'])
def test_unscoped_bind_with_remote_user(self):
self.config_fixture.config(group='token', bind=['kerberos'])
auth_data = self.build_authentication_request(kerberos=self.kerberos)
remote_user = self.user['name']
remote_domain = self.domain['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'REMOTE_DOMAIN': remote_domain,
'AUTH_TYPE': 'Negotiate'})
r = self.v3_create_token(auth_data)
token = self.assertValidUnscopedTokenResponse(r)
self.assertEqual(self.user['name'], token['bind']['kerberos'])
class TestAuthExternalDefaultDomain(object):
content_type = 'json'
@ -3697,30 +3600,6 @@ class TestAuthExternalDefaultDomain(object):
self.assertEqual(self.default_domain_user['id'],
auth_context['user_id'])
def test_project_id_scoped_with_remote_user(self):
self.config_fixture.config(group='token', bind=['kerberos'])
auth_data = self.build_authentication_request(
project_id=self.default_domain_project['id'],
kerberos=self.kerberos)
remote_user = self.default_domain_user['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'AUTH_TYPE': 'Negotiate'})
r = self.v3_create_token(auth_data)
token = self.assertValidProjectScopedTokenResponse(r)
self.assertEqual(self.default_domain_user['name'],
token['bind']['kerberos'])
def test_unscoped_bind_with_remote_user(self):
self.config_fixture.config(group='token', bind=['kerberos'])
auth_data = self.build_authentication_request(kerberos=self.kerberos)
remote_user = self.default_domain_user['name']
self.public_app.extra_environ.update({'REMOTE_USER': remote_user,
'AUTH_TYPE': 'Negotiate'})
r = self.v3_create_token(auth_data)
token = self.assertValidUnscopedTokenResponse(r)
self.assertEqual(self.default_domain_user['name'],
token['bind']['kerberos'])
class TestAuthJSONExternal(test_v3.RestfulTestCase):
content_type = 'json'

View File

@ -9,3 +9,8 @@ other:
Removed support for direct import of authentication drivers. If you're
using full path names for authentication methods in configuration, please
update your configuration to use the corresponding namespaces.
- >
[`blueprint removed-as-of-rocky <https://blueprints.launchpad.net/keystone/+spec/removed-as-of-rocky>`_]
Removed support for token bind operations, which were supported by the
``uuid``, ``pki``, and ``pkiz`` token providers. Support for this
feature was deprecated in Pike.