Improve the performance of tokens deletion for user

Provide new delete the tokens api 'delete_tokens' to support
delete all the tokens for user in one session in the sql backend. For
the kvs and memcache, I also provide the corresponding implementation.

Fix bug 1178063

Change-Id: I986a583e5900ea04e26cbdb7c49638a33818bca7
This commit is contained in:
gengjh 2013-05-11 17:20:10 +08:00
parent 6d33805d0f
commit d6cfe4f2e2
4 changed files with 109 additions and 22 deletions

View File

@ -153,23 +153,14 @@ class V2Controller(wsgi.Application):
"""Base controller class for Identity API v2."""
def _delete_tokens_for_trust(self, context, user_id, trust_id):
try:
token_list = self.token_api.list_tokens(context, user_id,
trust_id=trust_id)
for token in token_list:
self.token_api.delete_token(context, token)
except exception.NotFound:
pass
self.token_api.delete_tokens(context, user_id,
trust_id=trust_id)
def _delete_tokens_for_user(self, context, user_id, project_id=None):
#First delete tokens that could get other tokens.
for token_id in self.token_api.list_tokens(context,
user_id,
tenant_id=project_id):
try:
self.token_api.delete_token(context, token_id)
except exception.NotFound:
pass
self.token_api.delete_tokens(context,
user_id,
tenant_id=project_id)
#delete tokens generated from trusts
for trust in self.trust_api.list_trusts_for_trustee(context, user_id):

View File

@ -77,6 +77,40 @@ class Token(sql.Base, token.Driver):
token_ref.valid = False
session.flush()
def delete_tokens(self, user_id, tenant_id=None, trust_id=None):
"""Deletes all tokens in one session
The user_id will be ignored if the trust_id is specified. user_id
will always be specified.
If using a trust, the token's user_id is set to the trustee's user ID
or the trustor's user ID, so will use trust_id to query the tokens.
"""
session = self.get_session()
with session.begin():
now = timeutils.utcnow()
query = session.query(TokenModel)
query = query.filter_by(valid=True)
query = query.filter(TokenModel.expires > now)
if trust_id:
query = query.filter(TokenModel.trust_id == trust_id)
else:
query = query.filter(TokenModel.user_id == user_id)
for token_ref in query.all():
if tenant_id:
token_ref_dict = token_ref.to_dict()
if not self._tenant_matches(tenant_id, token_ref_dict):
continue
token_ref.valid = False
session.flush()
def _tenant_matches(self, tenant_id, token_ref_dict):
return ((tenant_id is None) or
(token_ref_dict.get('tenant') and
token_ref_dict['tenant'].get('id') == tenant_id))
def _list_tokens_for_trust(self, trust_id):
session = self.get_session()
tokens = []
@ -92,11 +126,6 @@ class Token(sql.Base, token.Driver):
return tokens
def _list_tokens_for_user(self, user_id, tenant_id=None):
def tenant_matches(tenant_id, token_ref_dict):
return ((tenant_id is None) or
(token_ref_dict.get('tenant') and
token_ref_dict['tenant'].get('id') == tenant_id))
session = self.get_session()
tokens = []
now = timeutils.utcnow()
@ -107,7 +136,7 @@ class Token(sql.Base, token.Driver):
token_references = query.filter_by(valid=True)
for token_ref in token_references:
token_ref_dict = token_ref.to_dict()
if tenant_matches(tenant_id, token_ref_dict):
if self._tenant_matches(tenant_id, token_ref_dict):
tokens.append(token_ref['id'])
return tokens

View File

@ -166,6 +166,32 @@ class Driver(object):
"""
raise exception.NotImplemented()
def delete_tokens(self, user_id, tenant_id=None, trust_id=None):
"""Deletes tokens by user.
If the tenant_id is not None, only delete the tokens by user id under
the specified tenant.
If the trust_id is not None, it will be used to query tokens and the
user_id will be ignored.
:param user_id: identity of user
:type token_id: string
:param tenant_id: identity of the tenant
:type tenant_id: string
:param trust_id: identified of the trust
:type trust_id: string
:returns: None.
:raises: keystone.exception.TokenNotFound
"""
token_list = self.list_tokens(user_id,
tenant_id=tenant_id,
trust_id=trust_id)
for token in token_list:
try:
self.delete_token(token)
except exception.NotFound:
pass
def list_tokens(self, user_id, tenant_id=None, trust_id=None):
"""Returns a list of current token_id's for a user

View File

@ -2052,10 +2052,11 @@ class TokenTests(object):
self.assertRaises(exception.TokenNotFound,
self.token_api.delete_token, token_id)
def create_token_sample_data(self, tenant_id=None, trust_id=None):
def create_token_sample_data(self, tenant_id=None, trust_id=None,
user_id="testuserid"):
token_id = self._create_token_id()
data = {'id': token_id, 'a': 'b',
'user': {'id': 'testuserid'}}
'user': {'id': user_id}}
if tenant_id is not None:
data['tenant'] = {'id': tenant_id, 'name': tenant_id}
if tenant_id is NULL_OBJECT:
@ -2065,6 +2066,46 @@ class TokenTests(object):
new_token = self.token_api.create_token(token_id, data)
return new_token['id']
def test_delete_tokens(self):
tokens = self.token_api.list_tokens('testuserid')
self.assertEquals(len(tokens), 0)
token_id1 = self.create_token_sample_data('testtenantid')
token_id2 = self.create_token_sample_data('testtenantid')
token_id3 = self.create_token_sample_data(tenant_id='testtenantid',
user_id="testuserid1")
tokens = self.token_api.list_tokens('testuserid')
self.assertEquals(len(tokens), 2)
self.assertIn(token_id2, tokens)
self.assertIn(token_id1, tokens)
self.token_api.delete_tokens(user_id='testuserid',
tenant_id='testtenantid')
tokens = self.token_api.list_tokens('testuserid')
self.assertEquals(len(tokens), 0)
self.assertRaises(exception.TokenNotFound,
self.token_api.get_token, token_id1)
self.assertRaises(exception.TokenNotFound,
self.token_api.get_token, token_id2)
self.token_api.get_token(token_id3)
def test_delete_tokens_trust(self):
tokens = self.token_api.list_tokens(user_id='testuserid')
self.assertEquals(len(tokens), 0)
token_id1 = self.create_token_sample_data(tenant_id='testtenantid',
trust_id='testtrustid')
token_id2 = self.create_token_sample_data(tenant_id='testtenantid',
user_id="testuserid1",
trust_id="testtrustid1")
tokens = self.token_api.list_tokens('testuserid')
self.assertEquals(len(tokens), 1)
self.assertIn(token_id1, tokens)
self.token_api.delete_tokens(user_id='testuserid',
tenant_id='testtenantid',
trust_id='testtrustid')
self.assertRaises(exception.TokenNotFound,
self.token_api.get_token, token_id1)
self.token_api.get_token(token_id2)
def test_token_list(self):
tokens = self.token_api.list_tokens('testuserid')
self.assertEquals(len(tokens), 0)