Merge "Invalidate user tokens when password is changed" into stable/essex

This commit is contained in:
Jenkins 2012-06-14 16:58:30 +00:00 committed by Gerrit Code Review
commit 35d5ebd54e
6 changed files with 76 additions and 1 deletions

View File

@ -25,6 +25,7 @@ Darren Birkett <darren.birkett@gmail.com>
dcramer <david.cramer@rackspace.com> dcramer <david.cramer@rackspace.com>
Dean Troyer <dtroyer@gmail.com> Dean Troyer <dtroyer@gmail.com>
Deepak Garg <deepakgarg.iitg@gmail.com> Deepak Garg <deepakgarg.iitg@gmail.com>
Derek Higgins <derekh@redhat.com>
Devin Carlen <devin.carlen@gmail.com> Devin Carlen <devin.carlen@gmail.com>
Dolph Mathews <dolph.mathews@gmail.com> Dolph Mathews <dolph.mathews@gmail.com>
Dolph Mathews <dolph.mathews@rackspace.com> Dolph Mathews <dolph.mathews@rackspace.com>

View File

@ -24,12 +24,15 @@ from keystone import config
from keystone import exception from keystone import exception
from keystone import policy from keystone import policy
from keystone import token from keystone import token
from keystone.common import logging
from keystone.common import manager from keystone.common import manager
from keystone.common import wsgi from keystone.common import wsgi
CONF = config.CONF CONF = config.CONF
LOG = logging.getLogger(__name__)
class Manager(manager.Manager): class Manager(manager.Manager):
"""Default pivot point for the Identity backend. """Default pivot point for the Identity backend.
@ -418,7 +421,16 @@ class UserController(wsgi.Application):
return self.update_user(context, user_id, user) return self.update_user(context, user_id, user)
def set_user_password(self, 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): def update_user_tenant(self, context, user_id, user):
"""Update the default tenant.""" """Update the default tenant."""

View File

@ -44,3 +44,18 @@ class Token(kvs.Base, token.Driver):
return self.db.delete('token-%s' % token_id) return self.db.delete('token-%s' % token_id)
except KeyError: except KeyError:
raise exception.TokenNotFound(token_id=token_id) 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

View File

@ -81,3 +81,17 @@ class Token(sql.Base, token.Driver):
with session.begin(): with session.begin():
session.delete(token_ref) session.delete(token_ref)
session.flush() 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

View File

@ -87,6 +87,16 @@ class Driver(object):
""" """
raise exception.NotImplemented() 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): def _get_default_expire_time(self):
"""Determine when a token should expire based on the config. """Determine when a token should expire based on the config.

View File

@ -286,6 +286,29 @@ class KeystoneClientTests(object):
username='blah', username='blah',
password='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): def test_user_create_update_delete(self):
from keystoneclient import exceptions as client_exceptions from keystoneclient import exceptions as client_exceptions