summaryrefslogtreecommitdiff
blob: ef760abb037c36b34a750b7bde110adceba371a7 (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
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