From a411c944af78c36f2fdb87d305ba452dc52d7ed3 Mon Sep 17 00:00:00 2001 From: Morgan Fainberg Date: Fri, 21 Feb 2014 14:09:04 -0800 Subject: [PATCH] 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 --- keystone/token/backends/memcache.py | 27 ++++++++++++++----- tests/test_backend.py | 41 ++++++++++++++++++++++++++++- tests/test_backend_kvs.py | 2 ++ tests/test_backend_memcache.py | 3 +++ 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/keystone/token/backends/memcache.py b/keystone/token/backends/memcache.py index c2c9b516ff..dc5c34e859 100644 --- a/keystone/token/backends/memcache.py +++ b/keystone/token/backends/memcache.py @@ -62,6 +62,15 @@ class Token(token.Driver): return token_ref def create_token(self, token_id, data): + + def update_index(user_id, token_data): + user_key = self._prefix_user_id(user_id) + if not self.client.append(user_key, ',%s' % token_data): + if not self.client.add(user_key, token_data): + if not self.client.append(user_key, ',%s' % token_data): + msg = _('Unable to add token user list.') + raise exception.UnexpectedError(msg) + data_copy = copy.deepcopy(data) ptk = self._prefix_token_id(token.unique_id(token_id)) if not data_copy.get('expires'): @@ -73,15 +82,19 @@ class Token(token.Driver): expires_ts = utils.unixtime(data_copy['expires']) kwargs['time'] = expires_ts self.client.set(ptk, data_copy, **kwargs) + token_data = jsonutils.dumps(token_id) if 'id' in data['user']: - token_data = jsonutils.dumps(token_id) user_id = data['user']['id'] - user_key = self._prefix_user_id(user_id) - if not self.client.append(user_key, ',%s' % token_data): - if not self.client.add(user_key, token_data): - if not self.client.append(user_key, ',%s' % token_data): - msg = _('Unable to add token user list.') - raise exception.UnexpectedError(msg) + update_index(user_id, token_data) + + if CONF.trust.enabled and data.get('trust_id'): + if 'access' in data_copy: + trustee_user_id = data_copy['access']['trust'][ + 'trustee_user_id'] + else: + trustee_user_id = data_copy['OS-TRUST:trust'][ + 'trustee_user_id'] + update_index(trustee_user_id, token_data) return copy.deepcopy(data_copy) def _add_to_revocation_list(self, token_id, token_data): diff --git a/tests/test_backend.py b/tests/test_backend.py index 1af3c16d16..19caa0ce1a 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -2096,7 +2096,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}} @@ -2104,6 +2105,11 @@ class TokenTests(object): data['tenant'] = {'id': tenant_id, 'name': tenant_id} 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 self.token_api.create_token(token_id, data) return token_id @@ -2290,6 +2296,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 TrustTests(object): def create_sample_trust(self, new_id): diff --git a/tests/test_backend_kvs.py b/tests/test_backend_kvs.py index f3a8ece0b9..15a87b5d5b 100644 --- a/tests/test_backend_kvs.py +++ b/tests/test_backend_kvs.py @@ -73,6 +73,8 @@ class KvsToken(test.TestCase, test_backend.TokenTests): def setUp(self): super(KvsToken, self).setUp() self.token_api = token_kvs.Token(db={}) + self.load_backends() + self.load_fixtures(default_fixtures) class KvsTrust(test.TestCase, test_backend.TrustTests): diff --git a/tests/test_backend_memcache.py b/tests/test_backend_memcache.py index 9fbaeb9067..6339e6f9df 100644 --- a/tests/test_backend_memcache.py +++ b/tests/test_backend_memcache.py @@ -18,6 +18,7 @@ import uuid import memcache +import default_fixtures from keystone.common import utils from keystone.openstack.common import timeutils from keystone import test @@ -75,8 +76,10 @@ class MemcacheClient(object): class MemcacheToken(test.TestCase, test_backend.TokenTests): def setUp(self): super(MemcacheToken, self).setUp() + self.load_backends() fake_client = MemcacheClient() self.token_api = token_memcache.Token(client=fake_client) + self.load_fixtures(default_fixtures) def test_create_unicode_token_id(self): token_id = unicode(self._create_token_id())