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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
From 8d23da1302dde9d38bbc227d9aba30da919b60c8 Mon Sep 17 00:00:00 2001
From: Adam Young <ayoung@redhat.com>
Date: Mon, 13 May 2013 16:07:51 -0400
Subject: [PATCH] Check token Expiration
Backport for Folsom.
Bug 1179615
Change-Id: I8516d87ffc72cf35d3bff6fc21cb5324da4ad2bb
---
keystone/middleware/auth_token.py | 30 +++++++++++++--------
tests/signing/Makefile | 2 +-
tests/signing/auth_token_revoked.pem | 10 +++----
tests/signing/auth_token_scoped_expired.json | 1 +
tests/signing/auth_token_scoped_expired.pem | 40 ++++++++++++++++++++++++++++
tests/test_auth_token_middleware.py | 10 +++++++
6 files changed, 76 insertions(+), 17 deletions(-)
create mode 100644 tests/signing/auth_token_scoped_expired.json
create mode 100644 tests/signing/auth_token_scoped_expired.pem
diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py
index 01e6c58..b1a574b 100644
--- a/keystone/middleware/auth_token.py
+++ b/keystone/middleware/auth_token.py
@@ -95,6 +95,7 @@ HTTP_X_ROLE
import datetime
import httplib
+import iso8601
import json
import logging
import os
@@ -259,13 +260,12 @@ class AuthProtocol(object):
self._token_revocation_list_fetched_time = None
self.token_revocation_list_cache_timeout = \
datetime.timedelta(seconds=0)
+ self._iso8601 = iso8601
if memcache_servers:
try:
import memcache
- import iso8601
LOG.info('Using memcache for caching token')
self._cache = memcache.Client(memcache_servers.split(','))
- self._iso8601 = iso8601
except ImportError as e:
LOG.warn('disabled caching due to missing libraries %s', e)
@@ -512,7 +512,8 @@ class AuthProtocol(object):
data = json.loads(verified)
else:
data = self.verify_uuid_token(user_token, retry)
- self._cache_put(token_id, data)
+ expires = self._confirm_token_not_expired(data)
+ self._cache_put(token_id, data, expires)
return data
except Exception as e:
LOG.debug('Token validation failure.', exc_info=True)
@@ -642,7 +643,19 @@ class AuthProtocol(object):
else:
LOG.debug('Cached Token %s seems expired', token)
- def _cache_put(self, token, data):
+ def _confirm_token_not_expired(self, data):
+ if 'token' in data.get('access', {}):
+ timestamp = data['access']['token']['expires']
+ expires = self._iso8601.parse_date(timestamp).strftime('%s')
+ else:
+ LOG.error('invalid token format')
+ raise InvalidUserToken('Token authorization failed')
+ if time.time() >= float(expires):
+ self.LOG.debug('Token expired a %s', timestamp)
+ raise InvalidUserToken('Token authorization failed')
+ return expires
+
+ def _cache_put(self, token, data, expires):
"""Put token data into the cache.
Stores the parsed expire date in cache allowing
@@ -650,12 +663,6 @@ class AuthProtocol(object):
"""
if self._cache and data:
key = 'tokens/%s' % token
- if 'token' in data.get('access', {}):
- timestamp = data['access']['token']['expires']
- expires = self._iso8601.parse_date(timestamp).strftime('%s')
- else:
- LOG.error('invalid token format')
- return
LOG.debug('Storing %s token in memcache', token)
self._cache.set(key,
(data, expires),
@@ -693,7 +700,8 @@ class AuthProtocol(object):
additional_headers=headers)
if response.status == 200:
- self._cache_put(user_token, data)
+ expires = self._confirm_token_not_expired(data)
+ self._cache_put(user_token, data, expires)
return data
if response.status == 404:
# FIXME(ja): I'm assuming the 404 status means that user_token is
diff --git a/tests/signing/Makefile b/tests/signing/Makefile
index b56c000..27f5ff8 100644
--- a/tests/signing/Makefile
+++ b/tests/signing/Makefile
@@ -19,7 +19,7 @@
.SUFFIXES: .json .pem
-SOURCES=auth_token_unscoped.json auth_token_scoped.json revocation_list.json
+SOURCES=auth_token_unscoped.json auth_token_scoped.json auth_token_scoped.json auth_token_scoped_expired.json revocation_list.json
SIGNED=$(SOURCES:.json=.pem)
TARGETS=$(SIGNED)
diff --git a/tests/signing/auth_token_revoked.pem b/tests/signing/auth_token_revoked.pem
index 186c080..27cef18 100644
--- a/tests/signing/auth_token_revoked.pem
+++ b/tests/signing/auth_token_revoked.pem
@@ -24,7 +24,7 @@ MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy
bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV
UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf
bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u
-ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMi0wNi0wMlQxNDo0NzozNFoi
+ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjExMi0wNi0wMlQxNDo0NzozNFoi
LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1
ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg
ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJyZXZv
@@ -33,8 +33,8 @@ LCAiaWQiOiAicmV2b2tlZF91c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAi
cm9sZTEifSwgeyJuYW1lIjogInJvbGUyIn1dLCAibmFtZSI6ICJyZXZva2VkX3Vz
ZXJuYW1lMSJ9fX0NCjGB9zCB9AIBATBUME8xFTATBgNVBAoTDFJlZCBIYXQsIElu
YzERMA8GA1UEBxMIV2VzdGZvcmQxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxCzAJ
-BgNVBAYTAlVTAgEBMAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIGAXstA+yZ5N/cS
-+i7Mmlhi585cckvwSVAGj9huPTpqBItpbO44+U3yUojEwcghomtpygI/wzUa8Z40
-UW/L3nGlATlOG833zhGvLKrp76GIitYMgk1e0OEmzGXeAWLnQZFev8ooMPs9rwYW
-MgEdAfDMWWqX+Tb7exdboLpRUiCQx1c=
+BgNVBAYTAlVTAgEBMAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIGAdnQ5zU60aOc+
+TGK+5ESmYbOllqe7QGkcB2fWzuiIY4/9l53X0m3ThYNzxeloJ0NgETLWoHO24xIi
+YoCUtAGP8BQI0D21Amg4Nb3jBxiwObzdONytEpAYOXxMq8pDMgboi8eU0esch1jJ
+r+9/uR3R/xksWkPtPsl+qnt/KpUsL+A=
-----END CMS-----
diff --git a/tests/signing/auth_token_scoped_expired.json b/tests/signing/auth_token_scoped_expired.json
new file mode 100644
index 0000000..d36d8cf
--- /dev/null
+++ b/tests/signing/auth_token_scoped_expired.json
@@ -0,0 +1 @@
+{"access": {"serviceCatalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v2.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}],"token": {"expires": "2010-06-02T14:47:34Z", "id": "placeholder", "tenant": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1"}}, "user": {"username": "user_name1", "roles_links": ["role1","role2"], "id": "user_id1", "roles": [{"name": "role1"}, {"name": "role2"}], "name": "user_name1"}}}
diff --git a/tests/signing/auth_token_scoped_expired.pem b/tests/signing/auth_token_scoped_expired.pem
new file mode 100644
index 0000000..8116b11
--- /dev/null
+++ b/tests/signing/auth_token_scoped_expired.pem
@@ -0,0 +1,40 @@
+-----BEGIN CMS-----
+MIIG9QYJKoZIhvcNAQcCoIIG5jCCBuICAQExCTAHBgUrDgMCGjCCBc4GCSqGSIb3
+DQEHAaCCBb8EggW7eyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k
+cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx
+LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy
+ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2
+L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS
+TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh
+NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi
+OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si
+YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6
+ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5
+MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi
+fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt
+ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw
+Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5
+YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog
+Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw
+ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3
+NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu
+ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi
+bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu
+MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy
+bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV
+UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf
+bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u
+ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMC0wNi0wMlQxNDo0NzozNFoi
+LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1
+ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg
+ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJ1c2Vy
+X25hbWUxIiwgInJvbGVzX2xpbmtzIjogWyJyb2xlMSIsInJvbGUyIl0sICJpZCI6
+ICJ1c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAicm9sZTEifSwgeyJuYW1l
+IjogInJvbGUyIn1dLCAibmFtZSI6ICJ1c2VyX25hbWUxIn19fQ0KMYH/MIH8AgEB
+MFwwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVVuc2V0MQ4wDAYDVQQHEwVVbnNl
+dDEOMAwGA1UEChMFVW5zZXQxGDAWBgNVBAMTD3d3dy5leGFtcGxlLmNvbQIBATAH
+BgUrDgMCGjANBgkqhkiG9w0BAQEFAASBgJP+wKRwFaPY8xXAolDd6gmlID41yuAw
+nd+IKeD54Ack0NI9h/M0Iv2LzTo0l84VbMOijmq++kbtdnDJ2pn4VAoNk7dQcTTy
+lz2c78Xnu0NXvq7gsPRF4zDtIpjHbUXJ3ZRPHs342suG7Tb4nvQAbxYMJQHSN10k
+W6w+gEeN7t7V
+-----END CMS-----
diff --git a/tests/test_auth_token_middleware.py b/tests/test_auth_token_middleware.py
index e6893ee..dfe424f 100644
--- a/tests/test_auth_token_middleware.py
+++ b/tests/test_auth_token_middleware.py
@@ -154,6 +154,9 @@ def setUpModule(self):
signing_path = os.path.join(os.path.dirname(__file__), 'signing')
with open(os.path.join(signing_path, 'auth_token_scoped.pem')) as f:
self.SIGNED_TOKEN_SCOPED = cms.cms_to_token(f.read())
+ with open(os.path.join(signing_path,
+ 'auth_token_scoped_expired.pem')) as f:
+ self.SIGNED_TOKEN_SCOPED_EXPIRED = cms.cms_to_token(f.read())
with open(os.path.join(signing_path, 'auth_token_unscoped.pem')) as f:
self.SIGNED_TOKEN_UNSCOPED = cms.cms_to_token(f.read())
with open(os.path.join(signing_path, 'auth_token_revoked.pem')) as f:
@@ -612,6 +615,13 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
self.middleware(req.environ, self.start_fake_response)
self.assertEqual(self.middleware._cache.set_value, None)
+ def test_expired(self):
+ req = webob.Request.blank('/')
+ token = SIGNED_TOKEN_SCOPED_EXPIRED
+ req.headers['X-Auth-Token'] = token
+ self.middleware(req.environ, self.start_fake_response)
+ self.assertEqual(self.response_status, 401)
+
def test_memcache_set_invalid(self):
req = webob.Request.blank('/')
req.headers['X-Auth-Token'] = 'invalid-token'
--
1.8.1.2
|