diff --git a/keystone/tests/test_backend.py b/keystone/tests/test_backend.py index e0e81ca13a..1e926c855b 100644 --- a/keystone/tests/test_backend.py +++ b/keystone/tests/test_backend.py @@ -25,6 +25,7 @@ from keystone import exception from keystone.openstack.common import timeutils from keystone import tests from keystone.tests import default_fixtures +from keystone.token import provider CONF = config.CONF @@ -2645,7 +2646,8 @@ class TokenTests(object): self.token_api.delete_token, token_id) 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() data = {'id': token_id, 'a': 'b', 'user': {'id': user_id}} @@ -2655,6 +2657,15 @@ class TokenTests(object): data['tenant'] = None if trust_id is not None: 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) return new_token['id'] @@ -2907,6 +2918,39 @@ class TokenTests(object): for t in self.token_api.list_revoked_tokens(): 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): def _create_test_data(self): diff --git a/keystone/tests/test_backend_kvs.py b/keystone/tests/test_backend_kvs.py index ac9df717c0..a23882c543 100644 --- a/keystone/tests/test_backend_kvs.py +++ b/keystone/tests/test_backend_kvs.py @@ -70,6 +70,7 @@ class KvsToken(tests.TestCase, test_backend.TokenTests): identity.CONF.identity.driver = ( 'keystone.identity.backends.kvs.Identity') self.load_backends() + self.load_fixtures(default_fixtures) class KvsTrust(tests.TestCase, test_backend.TrustTests): diff --git a/keystone/tests/test_backend_memcache.py b/keystone/tests/test_backend_memcache.py index 964d5b42a3..c99a6a36d3 100644 --- a/keystone/tests/test_backend_memcache.py +++ b/keystone/tests/test_backend_memcache.py @@ -26,6 +26,7 @@ from keystone import exception from keystone.openstack.common import jsonutils from keystone.openstack.common import timeutils from keystone import tests +from keystone.tests import default_fixtures from keystone.tests import test_backend from keystone.tests import test_utils from keystone import token @@ -115,6 +116,7 @@ class MemcacheToken(tests.TestCase, test_backend.TokenTests): def setUp(self): super(MemcacheToken, self).setUp() self.load_backends() + self.load_fixtures(default_fixtures) fake_client = MemcacheClient() self.token_man = token.Manager() self.token_man.driver = token_memcache.Token(client=fake_client) diff --git a/keystone/token/backends/kvs.py b/keystone/token/backends/kvs.py index b3f991ab73..c0d6e36ac8 100644 --- a/keystone/token/backends/kvs.py +++ b/keystone/token/backends/kvs.py @@ -150,5 +150,7 @@ class Token(kvs.Base, token.Driver): def flush_expired_tokens(self): now = timeutils.utcnow() for token, token_ref in self.db.items(): + if not token.startswith('revoked-token-'): + continue if self.is_expired(now, token_ref): self.db.delete(token) diff --git a/keystone/token/backends/memcache.py b/keystone/token/backends/memcache.py index a6fe82694e..08c1c4098c 100644 --- a/keystone/token/backends/memcache.py +++ b/keystone/token/backends/memcache.py @@ -83,12 +83,33 @@ class Token(token.Driver): expires_ts = utils.unixtime(data_copy['expires']) kwargs['time'] = expires_ts self.client.set(ptk, data_copy, **kwargs) - if 'id' in data['user']: - user_id = data['user']['id'] - user_key = self._prefix_user_id(user_id) - # Append the new token_id to the token-index-list stored in the - # user-key within memcache. - self._update_user_list_with_cas(user_key, token_id, data_copy) + user_id = data['user']['id'] + user_key = self._prefix_user_id(user_id) + # Append the new token_id to the token-index-list stored in the + # user-key within memcache. + 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) def _convert_user_index_from_json(self, token_list, user_key):