Skip to content
Permalink
Browse files

Invalidate user tokens when password is changed

Fixes bug 996595

This commit will cause all valid tokens to be deleted for a user
who's password is changed (implemented for the sql and kvs backends)

Change-Id: I6ad7da8957b7041983a3fc91d9ba9368667d06ac
  • Loading branch information...
derekhiggins committed May 9, 2012
1 parent 09d6352 commit a67b24878a6156eab17b9098fa649f0279256f5d
@@ -24,12 +24,15 @@
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 @@ def set_user_enabled(self, context, user_id, user):
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."""
@@ -44,3 +44,18 @@ def delete_token(self, token_id):
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
@@ -81,3 +81,17 @@ def delete_token(self, token_id):
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
@@ -87,6 +87,16 @@ def delete_token(self, token_id):
"""
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.
@@ -286,6 +286,29 @@ def test_invalid_user_password(self):
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

0 comments on commit a67b248

Please sign in to comment.
You can’t perform that action at this time.