Ensure tokens are added to both Trustor and Trustee indexes
Tokens are now added to both the Trustor and Trustee user-token-index so that bulk token revocations (e.g. password change) of the trustee will work as expected. This is a backport of the basic code that was used in the Icehouse-vintage Dogpile Token KVS backend that resolves this issue by merging the handling of memcache and KVS backends into the same logic. Change-Id: I3e19e4a8fc1e11cef6db51d364e80061e97befa7 Closes-Bug: #1260080
This commit is contained in:
parent
c22f2edeb7
commit
b6f0e26da0
|
@ -25,6 +25,7 @@ from keystone import exception
|
||||||
from keystone.openstack.common import timeutils
|
from keystone.openstack.common import timeutils
|
||||||
from keystone import tests
|
from keystone import tests
|
||||||
from keystone.tests import default_fixtures
|
from keystone.tests import default_fixtures
|
||||||
|
from keystone.token import provider
|
||||||
|
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
@ -2645,7 +2646,8 @@ class TokenTests(object):
|
||||||
self.token_api.delete_token, token_id)
|
self.token_api.delete_token, token_id)
|
||||||
|
|
||||||
def create_token_sample_data(self, tenant_id=None, trust_id=None,
|
def create_token_sample_data(self, tenant_id=None, trust_id=None,
|
||||||
user_id="testuserid"):
|
user_id='testuserid',
|
||||||
|
trustee_user_id='testuserid2'):
|
||||||
token_id = self._create_token_id()
|
token_id = self._create_token_id()
|
||||||
data = {'id': token_id, 'a': 'b',
|
data = {'id': token_id, 'a': 'b',
|
||||||
'user': {'id': user_id}}
|
'user': {'id': user_id}}
|
||||||
|
@ -2655,6 +2657,15 @@ class TokenTests(object):
|
||||||
data['tenant'] = None
|
data['tenant'] = None
|
||||||
if trust_id is not None:
|
if trust_id is not None:
|
||||||
data['trust_id'] = trust_id
|
data['trust_id'] = trust_id
|
||||||
|
data.setdefault('access', {}).setdefault('trust', {})
|
||||||
|
# Testuserid2 is used here since a trustee will be different in
|
||||||
|
# the cases of impersonation and therefore should not match the
|
||||||
|
# token's user_id.
|
||||||
|
data['access']['trust']['trustee_user_id'] = trustee_user_id
|
||||||
|
data['token_version'] = provider.V2
|
||||||
|
# Issue token stores a copy of all token data at token['token_data'].
|
||||||
|
# This emulates that assumption as part of the test.
|
||||||
|
data['token_data'] = copy.deepcopy(data)
|
||||||
new_token = self.token_api.create_token(token_id, data)
|
new_token = self.token_api.create_token(token_id, data)
|
||||||
return new_token['id']
|
return new_token['id']
|
||||||
|
|
||||||
|
@ -2907,6 +2918,39 @@ class TokenTests(object):
|
||||||
for t in self.token_api.list_revoked_tokens():
|
for t in self.token_api.list_revoked_tokens():
|
||||||
self.assertIn('expires', t)
|
self.assertIn('expires', t)
|
||||||
|
|
||||||
|
def test_token_in_trustee_and_trustor_token_list(self):
|
||||||
|
self.opt_in_group('trust',
|
||||||
|
enabled=True)
|
||||||
|
trustor = self.user_foo
|
||||||
|
trustee = self.user_two
|
||||||
|
trust_id = uuid.uuid4().hex
|
||||||
|
trust_info = {'trustor_user_id': trustor['id'],
|
||||||
|
'trustee_user_id': trustee['id'],
|
||||||
|
'project_id': self.tenant_bar['id'],
|
||||||
|
'expires_at': timeutils.
|
||||||
|
parse_isotime('2031-02-18T18:10:00Z'),
|
||||||
|
'impersonation': True}
|
||||||
|
self.trust_api.create_trust(trust_id, trust_info,
|
||||||
|
roles=[{'id': 'member'},
|
||||||
|
{'id': 'other'},
|
||||||
|
{'id': 'browser'}])
|
||||||
|
|
||||||
|
token_id = self.create_token_sample_data(
|
||||||
|
tenant_id=self.tenant_bar['id'],
|
||||||
|
trust_id=trust_id,
|
||||||
|
user_id=trustor['id'],
|
||||||
|
trustee_user_id=trustee['id'])
|
||||||
|
|
||||||
|
# Ensure the token id exists in both the trustor and trustee token
|
||||||
|
# lists
|
||||||
|
|
||||||
|
self.assertIn(token_id,
|
||||||
|
self.token_api.list_tokens(self.user_two['id'],
|
||||||
|
trust_id=trust_id))
|
||||||
|
self.assertIn(token_id,
|
||||||
|
self.token_api.list_tokens(self.user_foo['id'],
|
||||||
|
trust_id=trust_id))
|
||||||
|
|
||||||
|
|
||||||
class TokenCacheInvalidation(object):
|
class TokenCacheInvalidation(object):
|
||||||
def _create_test_data(self):
|
def _create_test_data(self):
|
||||||
|
|
|
@ -70,6 +70,7 @@ class KvsToken(tests.TestCase, test_backend.TokenTests):
|
||||||
identity.CONF.identity.driver = (
|
identity.CONF.identity.driver = (
|
||||||
'keystone.identity.backends.kvs.Identity')
|
'keystone.identity.backends.kvs.Identity')
|
||||||
self.load_backends()
|
self.load_backends()
|
||||||
|
self.load_fixtures(default_fixtures)
|
||||||
|
|
||||||
|
|
||||||
class KvsTrust(tests.TestCase, test_backend.TrustTests):
|
class KvsTrust(tests.TestCase, test_backend.TrustTests):
|
||||||
|
|
|
@ -26,6 +26,7 @@ from keystone import exception
|
||||||
from keystone.openstack.common import jsonutils
|
from keystone.openstack.common import jsonutils
|
||||||
from keystone.openstack.common import timeutils
|
from keystone.openstack.common import timeutils
|
||||||
from keystone import tests
|
from keystone import tests
|
||||||
|
from keystone.tests import default_fixtures
|
||||||
from keystone.tests import test_backend
|
from keystone.tests import test_backend
|
||||||
from keystone.tests import test_utils
|
from keystone.tests import test_utils
|
||||||
from keystone import token
|
from keystone import token
|
||||||
|
@ -115,6 +116,7 @@ class MemcacheToken(tests.TestCase, test_backend.TokenTests):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(MemcacheToken, self).setUp()
|
super(MemcacheToken, self).setUp()
|
||||||
self.load_backends()
|
self.load_backends()
|
||||||
|
self.load_fixtures(default_fixtures)
|
||||||
fake_client = MemcacheClient()
|
fake_client = MemcacheClient()
|
||||||
self.token_man = token.Manager()
|
self.token_man = token.Manager()
|
||||||
self.token_man.driver = token_memcache.Token(client=fake_client)
|
self.token_man.driver = token_memcache.Token(client=fake_client)
|
||||||
|
|
|
@ -150,5 +150,7 @@ class Token(kvs.Base, token.Driver):
|
||||||
def flush_expired_tokens(self):
|
def flush_expired_tokens(self):
|
||||||
now = timeutils.utcnow()
|
now = timeutils.utcnow()
|
||||||
for token, token_ref in self.db.items():
|
for token, token_ref in self.db.items():
|
||||||
|
if not token.startswith('revoked-token-'):
|
||||||
|
continue
|
||||||
if self.is_expired(now, token_ref):
|
if self.is_expired(now, token_ref):
|
||||||
self.db.delete(token)
|
self.db.delete(token)
|
||||||
|
|
|
@ -83,12 +83,33 @@ class Token(token.Driver):
|
||||||
expires_ts = utils.unixtime(data_copy['expires'])
|
expires_ts = utils.unixtime(data_copy['expires'])
|
||||||
kwargs['time'] = expires_ts
|
kwargs['time'] = expires_ts
|
||||||
self.client.set(ptk, data_copy, **kwargs)
|
self.client.set(ptk, data_copy, **kwargs)
|
||||||
if 'id' in data['user']:
|
user_id = data['user']['id']
|
||||||
user_id = data['user']['id']
|
user_key = self._prefix_user_id(user_id)
|
||||||
user_key = self._prefix_user_id(user_id)
|
# Append the new token_id to the token-index-list stored in the
|
||||||
# Append the new token_id to the token-index-list stored in the
|
# user-key within memcache.
|
||||||
# user-key within memcache.
|
self._update_user_list_with_cas(user_key, token_id, data_copy)
|
||||||
self._update_user_list_with_cas(user_key, token_id, data_copy)
|
if CONF.trust.enabled and data.get('trust_id'):
|
||||||
|
# NOTE(morganfainberg): If trusts are enabled and this is a trust
|
||||||
|
# scoped token, we add the token to the trustee list as well. This
|
||||||
|
# allows password changes of the trustee to also expire the token.
|
||||||
|
# There is no harm in placing the token in multiple lists, as
|
||||||
|
# _list_tokens is smart enough to handle almost any case of
|
||||||
|
# valid/invalid/expired for a given token.
|
||||||
|
token_data = data_copy['token_data']
|
||||||
|
if data_copy['token_version'] == token.provider.V2:
|
||||||
|
trustee_user_id = token_data['access']['trust'][
|
||||||
|
'trustee_user_id']
|
||||||
|
elif data_copy['token_version'] == token.provider.V3:
|
||||||
|
trustee_user_id = token_data['OS-TRUST:trust'][
|
||||||
|
'trustee_user_id']
|
||||||
|
else:
|
||||||
|
raise token.provider.UnsupportedTokenVersionException(
|
||||||
|
_('Unknown token version %s') %
|
||||||
|
data_copy.get('token_version'))
|
||||||
|
|
||||||
|
trustee_key = self._prefix_user_id(trustee_user_id)
|
||||||
|
self._update_user_list_with_cas(trustee_key, token_id, data_copy)
|
||||||
|
|
||||||
return copy.deepcopy(data_copy)
|
return copy.deepcopy(data_copy)
|
||||||
|
|
||||||
def _convert_user_index_from_json(self, token_list, user_key):
|
def _convert_user_index_from_json(self, token_list, user_key):
|
||||||
|
|
Loading…
Reference in New Issue