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>
Dean Troyer <dtroyer@gmail.com>
Deepak Garg <deepakgarg.iitg@gmail.com>
Derek Higgins <derekh@redhat.com>
Devin Carlen <devin.carlen@gmail.com>
Dolph Mathews <dolph.mathews@gmail.com>
Dolph Mathews <dolph.mathews@rackspace.com>

View File

@ -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."""

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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