Merge "Invalidate user tokens when password is changed" into stable/essex
This commit is contained in:
commit
35d5ebd54e
1
AUTHORS
1
AUTHORS
|
@ -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>
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue