From 7f68afd7345a338130fca3b22380d7426ffc8513 Mon Sep 17 00:00:00 2001 From: Adam Young 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 | 26 ++++++++++++++-------- tests/signing/Makefile | 2 +- tests/signing/auth_token_revoked.pem | 10 ++++----- tests/signing/auth_token_scoped_expired.json | 1 + ...n_revoked.pem => auth_token_scoped_expired.pem} | 26 +++++++++++----------- tests/test_auth_token_middleware.py | 10 +++++++++ 6 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 tests/signing/auth_token_scoped_expired.json copy tests/signing/{auth_token_revoked.pem => auth_token_scoped_expired.pem} (67%) diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py index 01e6c585c67c9ab5b884555947b68aabc38d873e..f5e631ae207f7b0feea60166522feffbc07f3162 100644 --- a/keystone/middleware/auth_token.py +++ b/keystone/middleware/auth_token.py @@ -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 b56c0008b875ee0e39a2a253b84583bd0c5d680f..27f5ff85a034694077cefc7ca3c1aa00538b9f7a 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 186c0800d058f4919bb9ae716044009682a2dee1..27cef1864f986cbe11f94d05181a29a994d56ce7 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 0000000000000000000000000000000000000000..d36d8cf0d8cd4606b2f9cdca1685e51eeeae820a --- /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_revoked.pem b/tests/signing/auth_token_scoped_expired.pem similarity index 67% copy from tests/signing/auth_token_revoked.pem copy to tests/signing/auth_token_scoped_expired.pem index 186c0800d058f4919bb9ae716044009682a2dee1..8116b117bf2553be6bc9bc12bff5d6c81f26358e 100644 --- a/tests/signing/auth_token_revoked.pem +++ b/tests/signing/auth_token_scoped_expired.pem @@ -1,6 +1,6 @@ -----BEGIN CMS----- -MIIHAwYJKoZIhvcNAQcCoIIG9DCCBvACAQExCTAHBgUrDgMCGjCCBeQGCSqGSIb3 -DQEHAaCCBdUEggXReyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k +MIIG9QYJKoZIhvcNAQcCoIIG5jCCBuICAQExCTAHBgUrDgMCGjCCBc4GCSqGSIb3 +DQEHAaCCBb8EggW7eyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2 @@ -24,17 +24,17 @@ MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u -ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMi0wNi0wMlQxNDo0NzozNFoi +ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMC0wNi0wMlQxNDo0NzozNFoi LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1 ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg -ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJyZXZv -a2VkX3VzZXJuYW1lMSIsICJyb2xlc19saW5rcyI6IFsicm9sZTEiLCJyb2xlMiJd -LCAiaWQiOiAicmV2b2tlZF91c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAi -cm9sZTEifSwgeyJuYW1lIjogInJvbGUyIn1dLCAibmFtZSI6ICJyZXZva2VkX3Vz -ZXJuYW1lMSJ9fX0NCjGB9zCB9AIBATBUME8xFTATBgNVBAoTDFJlZCBIYXQsIElu -YzERMA8GA1UEBxMIV2VzdGZvcmQxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxCzAJ -BgNVBAYTAlVTAgEBMAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIGAXstA+yZ5N/cS -+i7Mmlhi585cckvwSVAGj9huPTpqBItpbO44+U3yUojEwcghomtpygI/wzUa8Z40 -UW/L3nGlATlOG833zhGvLKrp76GIitYMgk1e0OEmzGXeAWLnQZFev8ooMPs9rwYW -MgEdAfDMWWqX+Tb7exdboLpRUiCQx1c= +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 e6893eedd50e0e41f20b0553c3436f9d5a0ee84d..dfe424f7dec69476a11c3de7494182938f613d59 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.4