summaryrefslogtreecommitdiff
blob: a19d9440258f1ae5da3be4e9539fd0b867a9267d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
From b6f0e26da0e2ab0892a5658da281a065e668637b Mon Sep 17 00:00:00 2001
From: Morgan Fainberg <m@metacloud.com>
Date: Fri, 21 Feb 2014 21:33:25 +0000
Subject: 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
---
diff --git a/keystone/tests/test_backend.py b/keystone/tests/test_backend.py
index e0e81ca..1e926c8 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 ac9df71..a23882c 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 964d5b4..c99a6a3 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 b3f991a..c0d6e36 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 a6fe826..08c1c40 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):
--
cgit v0.9.2