From ff76a1b5cd3308cfb0ce936800364e27413ed946 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Fri, 3 May 2013 14:04:09 +1000 Subject: [PATCH] Implement Token Flush via keystone-manage. Creates a cli entry 'token_flush' which removes all expired tokens. Fixes: bug 1032633 Implements: blueprint keystone-manage-token-flush Change-Id: I47eab99b577ff9e9ee74fee08e18fd07c4af5aad --- doc/source/configuration.rst | 13 +++++++++++++ doc/source/man/keystone-manage.rst | 1 + keystone/cli.py | 13 +++++++++++++ keystone/token/backends/kvs.py | 6 ++++++ keystone/token/backends/sql.py | 9 +++++++++ keystone/token/core.py | 5 +++++ tests/test_backend.py | 26 ++++++++++++++++++++++++++ tests/test_backend_memcache.py | 5 +++++ 8 files changed, 78 insertions(+) diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst index 4b09f2c425..8990d156b1 100644 --- a/doc/source/configuration.rst +++ b/doc/source/configuration.rst @@ -975,6 +975,19 @@ example:: $ keystone service-delete 08741d8ed88242ca88d1f61484a0fe3b + +Removing Expired Tokens +=========================================================== + +In the SQL and KVS token stores expired tokens are not automatically +removed. These tokens can be removed with:: + + $ keystone-manage token_flush + +The memcache backend automatically discards expired tokens and so flushing +is unnecessary and if attempted will fail with a NotImplemented error. + + Configuring the LDAP Identity Provider =========================================================== diff --git a/doc/source/man/keystone-manage.rst b/doc/source/man/keystone-manage.rst index b7c2131ce7..84a3ec9f73 100644 --- a/doc/source/man/keystone-manage.rst +++ b/doc/source/man/keystone-manage.rst @@ -49,6 +49,7 @@ Available commands: * ``import_nova_auth``: Import a dump of nova auth data into keystone. * ``pki_setup``: Initialize the certificates used to sign tokens. * ``ssl_setup``: Generate certificates for SSL. +* ``token_flush``: Purge expired tokens. OPTIONS diff --git a/keystone/cli.py b/keystone/cli.py index b635878e98..dfa38c7f2c 100644 --- a/keystone/cli.py +++ b/keystone/cli.py @@ -26,6 +26,7 @@ from keystone.openstack.common import importutils from keystone.openstack.common import jsonutils from keystone.openstack.common import version +from keystone import token CONF = config.CONF @@ -111,6 +112,17 @@ def main(cls): conf_ssl.run() +class TokenFlush(BaseApp): + """Flush expired tokens from the backend.""" + + name = 'token_flush' + + @classmethod + def main(cls): + token_manager = token.Manager() + token_manager.driver.flush_expired_tokens() + + class ImportLegacy(BaseApp): """Import a legacy database.""" @@ -173,6 +185,7 @@ def main(): ImportNovaAuth, PKISetup, SSLSetup, + TokenFlush, ] diff --git a/keystone/token/backends/kvs.py b/keystone/token/backends/kvs.py index 361416b7ee..75c14eec4f 100644 --- a/keystone/token/backends/kvs.py +++ b/keystone/token/backends/kvs.py @@ -116,3 +116,9 @@ def list_revoked_tokens(self): record['expires'] = token_ref['expires'] tokens.append(record) return tokens + + def flush_expired_tokens(self): + now = timeutils.utcnow() + for token, token_ref in self.db.items(): + if self.is_expired(now, token_ref): + self.db.delete(token) diff --git a/keystone/token/backends/sql.py b/keystone/token/backends/sql.py index 2e68bdc975..ac567d7fe2 100644 --- a/keystone/token/backends/sql.py +++ b/keystone/token/backends/sql.py @@ -131,3 +131,12 @@ def list_revoked_tokens(self): } tokens.append(record) return tokens + + def flush_expired_tokens(self): + session = self.get_session() + + query = session.query(TokenModel) + query = query.filter(TokenModel.expires < timeutils.utcnow()) + query.delete(synchronize_session=False) + + session.flush() diff --git a/keystone/token/core.py b/keystone/token/core.py index 5c3830dae6..5a47d0278b 100644 --- a/keystone/token/core.py +++ b/keystone/token/core.py @@ -187,3 +187,8 @@ def list_revoked_tokens(self): """ raise exception.NotImplemented() + + def flush_expired_tokens(self): + """Archive or delete tokens that have expired. + """ + raise exception.NotImplemented() diff --git a/tests/test_backend.py b/tests/test_backend.py index 613aaedcb3..dd9bc91b45 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -2188,6 +2188,32 @@ def test_list_revoked_tokens_for_multiple_tokens(self): self.check_list_revoked_tokens([self.delete_token() for x in xrange(2)]) + def test_flush_expired_token(self): + token_id = uuid.uuid4().hex + expire_time = timeutils.utcnow() - datetime.timedelta(minutes=1) + data = {'id_hash': token_id, 'id': token_id, 'a': 'b', + 'expires': expire_time, + 'trust_id': None, + 'user': {'id': 'testuserid'}} + data_ref = self.token_api.create_token(token_id, data) + data_ref.pop('user_id') + self.assertDictEqual(data_ref, data) + + token_id = uuid.uuid4().hex + expire_time = timeutils.utcnow() + datetime.timedelta(minutes=1) + data = {'id_hash': token_id, 'id': token_id, 'a': 'b', + 'expires': expire_time, + 'trust_id': None, + 'user': {'id': 'testuserid'}} + data_ref = self.token_api.create_token(token_id, data) + data_ref.pop('user_id') + self.assertDictEqual(data_ref, data) + + self.token_api.flush_expired_tokens() + tokens = self.token_api.list_tokens('testuserid') + self.assertEqual(len(tokens), 1) + self.assertIn(token_id, tokens) + class TrustTests(object): def create_sample_trust(self, new_id): diff --git a/tests/test_backend_memcache.py b/tests/test_backend_memcache.py index 9fbaeb9067..f599900267 100644 --- a/tests/test_backend_memcache.py +++ b/tests/test_backend_memcache.py @@ -19,6 +19,7 @@ import memcache from keystone.common import utils +from keystone import exception from keystone.openstack.common import timeutils from keystone import test from keystone.token.backends import memcache as token_memcache @@ -96,3 +97,7 @@ def test_create_unicode_user_id(self): def test_list_tokens_unicode_user_id(self): user_id = unicode(uuid.uuid4().hex) self.token_api.list_tokens(user_id) + + def test_flush_expired_token(self): + with self.assertRaises(exception.NotImplemented): + self.token_api.flush_expired_tokens()