diff options
| -rw-r--r-- | keystone/identity/core.py | 14 | ||||
| -rw-r--r-- | keystone/token/backends/kvs.py | 15 | ||||
| -rw-r--r-- | keystone/token/backends/sql.py | 14 | ||||
| -rw-r--r-- | keystone/token/core.py | 10 | ||||
| -rw-r--r-- | tests/test_keystoneclient.py | 23 |
5 files changed, 75 insertions, 1 deletions
diff --git a/keystone/identity/core.py b/keystone/identity/core.py index ee225264..5efd142e 100644 --- a/keystone/identity/core.py +++ b/keystone/identity/core.py @@ -24,12 +24,15 @@ from keystone import config from keystone import exception from keystone import policy from keystone import token +from keystone.common import logging from keystone.common import manager from keystone.common import wsgi CONF = config.CONF +LOG = logging.getLogger(__name__) + class Manager(manager.Manager): """Default pivot point for the Identity backend. @@ -418,7 +421,16 @@ class UserController(wsgi.Application): return self.update_user(context, user_id, user) def set_user_password(self, context, user_id, user): - return self.update_user(context, user_id, user) + user_ref = self.update_user(context, user_id, user) + try: + for token_id in self.token_api.list_tokens(context, user_id): + self.token_api.delete_token(context, token_id) + except exception.NotImplemented: + # The password has been changed but tokens remain valid for + # backends that can't list tokens for users + LOG.warning('Password changed for %s, but existing tokens remain ' + 'valid' % user_id) + return user_ref def update_user_tenant(self, context, user_id, user): """Update the default tenant.""" diff --git a/keystone/token/backends/kvs.py b/keystone/token/backends/kvs.py index 71b86f8e..b033dd31 100644 --- a/keystone/token/backends/kvs.py +++ b/keystone/token/backends/kvs.py @@ -44,3 +44,18 @@ class Token(kvs.Base, token.Driver): return self.db.delete('token-%s' % token_id) except KeyError: raise exception.TokenNotFound(token_id=token_id) + + def list_tokens(self, user_id): + tokens = [] + now = datetime.datetime.utcnow() + for token, user_ref in self.db.items(): + if not token.startswith('token-'): + continue + if 'user' not in user_ref: + continue + if user_ref['user'].get('id') != user_id: + continue + if user_ref.get('expires') and user_ref.get('expires') < now: + continue + tokens.append(token.split('-', 1)[1]) + return tokens diff --git a/keystone/token/backends/sql.py b/keystone/token/backends/sql.py index 7a9a5515..543f9628 100644 --- a/keystone/token/backends/sql.py +++ b/keystone/token/backends/sql.py @@ -81,3 +81,17 @@ class Token(sql.Base, token.Driver): with session.begin(): session.delete(token_ref) session.flush() + + def list_tokens(self, user_id): + session = self.get_session() + tokens = [] + now = datetime.datetime.utcnow() + for token_ref in session.query(TokenModel)\ + .filter(TokenModel.expires > now): + token_ref_dict = token_ref.to_dict() + if 'user' not in token_ref_dict: + continue + if token_ref_dict['user'].get('id') != user_id: + continue + tokens.append(token_ref['id']) + return tokens diff --git a/keystone/token/core.py b/keystone/token/core.py index 0a28e382..0d1101db 100644 --- a/keystone/token/core.py +++ b/keystone/token/core.py @@ -87,6 +87,16 @@ class Driver(object): """ raise exception.NotImplemented() + def list_tokens(self, user_id): + """Returns a list of current token_id's for a user + + :param user_id: identity of the user + :type user_id: string + :returns: list of token_id's + + """ + raise exception.NotImplemented() + def _get_default_expire_time(self): """Determine when a token should expire based on the config. diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py index 064a8e34..a02af87c 100644 --- a/tests/test_keystoneclient.py +++ b/tests/test_keystoneclient.py @@ -286,6 +286,29 @@ class KeystoneClientTests(object): username='blah', password='blah') + def test_change_password_invalidates_token(self): + from keystoneclient import exceptions as client_exceptions + + client = self.get_client(admin=True) + + username = uuid.uuid4().hex + passwd = uuid.uuid4().hex + user = client.users.create(name=username, password=passwd, + email=uuid.uuid4().hex) + + token_id = client.tokens.authenticate(username=username, + password=passwd).id + + # authenticate with a token should work before a password change + client.tokens.authenticate(token=token_id) + + client.users.update_password(user=user.id, password=uuid.uuid4().hex) + + # authenticate with a token should not work after a password change + self.assertRaises(client_exceptions.Unauthorized, + client.tokens.authenticate, + token=token_id) + def test_user_create_update_delete(self): from keystoneclient import exceptions as client_exceptions |
