diff options
Diffstat (limited to 'keystone-moon/keystone/token/persistence/backends/kvs.py')
-rw-r--r-- | keystone-moon/keystone/token/persistence/backends/kvs.py | 367 |
1 files changed, 0 insertions, 367 deletions
diff --git a/keystone-moon/keystone/token/persistence/backends/kvs.py b/keystone-moon/keystone/token/persistence/backends/kvs.py deleted file mode 100644 index 3620db58..00000000 --- a/keystone-moon/keystone/token/persistence/backends/kvs.py +++ /dev/null @@ -1,367 +0,0 @@ -# Copyright 2013 Metacloud, Inc. -# Copyright 2012 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from __future__ import absolute_import -import copy - -from oslo_config import cfg -from oslo_log import log -from oslo_utils import timeutils -import six - -from keystone.common import kvs -from keystone.common import utils -from keystone import exception -from keystone.i18n import _, _LE, _LW -from keystone import token -from keystone.token import provider - - -CONF = cfg.CONF -LOG = log.getLogger(__name__) - - -class Token(token.persistence.TokenDriverV8): - """KeyValueStore backend for tokens. - - This is the base implementation for any/all key-value-stores (e.g. - memcached) for the Token backend. It is recommended to only use the base - in-memory implementation for testing purposes. - """ - - revocation_key = 'revocation-list' - kvs_backend = 'openstack.kvs.Memory' - - def __init__(self, backing_store=None, **kwargs): - super(Token, self).__init__() - self._store = kvs.get_key_value_store('token-driver') - if backing_store is not None: - self.kvs_backend = backing_store - if not self._store.is_configured: - # Do not re-configure the backend if the store has been initialized - self._store.configure(backing_store=self.kvs_backend, **kwargs) - if self.__class__ == Token: - # NOTE(morganfainberg): Only warn if the base KVS implementation - # is instantiated. - LOG.warning(_LW('It is recommended to only use the base ' - 'key-value-store implementation for the token ' - 'driver for testing purposes. Please use ' - "'memcache' or 'sql' instead.")) - - def _prefix_token_id(self, token_id): - return 'token-%s' % token_id.encode('utf-8') - - def _prefix_user_id(self, user_id): - return 'usertokens-%s' % user_id.encode('utf-8') - - def _get_key_or_default(self, key, default=None): - try: - return self._store.get(key) - except exception.NotFound: - return default - - def _get_key(self, key): - return self._store.get(key) - - def _set_key(self, key, value, lock=None): - self._store.set(key, value, lock) - - def _delete_key(self, key): - return self._store.delete(key) - - def get_token(self, token_id): - ptk = self._prefix_token_id(token_id) - try: - token_ref = self._get_key(ptk) - except exception.NotFound: - raise exception.TokenNotFound(token_id=token_id) - - return token_ref - - def create_token(self, token_id, data): - """Create a token by id and data. - - It is assumed the caller has performed data validation on the "data" - parameter. - """ - data_copy = copy.deepcopy(data) - ptk = self._prefix_token_id(token_id) - if not data_copy.get('expires'): - data_copy['expires'] = provider.default_expire_time() - if not data_copy.get('user_id'): - data_copy['user_id'] = data_copy['user']['id'] - - # NOTE(morganfainberg): for ease of manipulating the data without - # concern about the backend, always store the value(s) in the - # index as the isotime (string) version so this is where the string is - # built. - expires_str = utils.isotime(data_copy['expires'], subsecond=True) - - self._set_key(ptk, data_copy) - user_id = data['user']['id'] - user_key = self._prefix_user_id(user_id) - self._update_user_token_list(user_key, token_id, expires_str) - if CONF.trust.enabled and data.get('trust_id'): - # NOTE(morganfainberg): If trusts are enabled and this is a trust - # scoped token, we add the token to the trustee list as well. This - # allows password changes of the trustee to also expire the token. - # There is no harm in placing the token in multiple lists, as - # _list_tokens is smart enough to handle almost any case of - # valid/invalid/expired for a given token. - token_data = data_copy['token_data'] - if data_copy['token_version'] == token.provider.V2: - trustee_user_id = token_data['access']['trust'][ - 'trustee_user_id'] - elif data_copy['token_version'] == token.provider.V3: - trustee_user_id = token_data['OS-TRUST:trust'][ - 'trustee_user_id'] - else: - raise exception.UnsupportedTokenVersionException( - _('Unknown token version %s') % - data_copy.get('token_version')) - - trustee_key = self._prefix_user_id(trustee_user_id) - self._update_user_token_list(trustee_key, token_id, expires_str) - - return data_copy - - def _get_user_token_list_with_expiry(self, user_key): - """Return user token list with token expiry. - - :return: the tuples in the format (token_id, token_expiry) - :rtype: list - """ - return self._get_key_or_default(user_key, default=[]) - - def _get_user_token_list(self, user_key): - """Return a list of token_ids for the user_key.""" - token_list = self._get_user_token_list_with_expiry(user_key) - # Each element is a tuple of (token_id, token_expiry). Most code does - # not care about the expiry, it is stripped out and only a - # list of token_ids are returned. - return [t[0] for t in token_list] - - def _update_user_token_list(self, user_key, token_id, expires_isotime_str): - current_time = self._get_current_time() - revoked_token_list = set([t['id'] for t in - self.list_revoked_tokens()]) - - with self._store.get_lock(user_key) as lock: - filtered_list = [] - token_list = self._get_user_token_list_with_expiry(user_key) - for item in token_list: - try: - item_id, expires = self._format_token_index_item(item) - except (ValueError, TypeError): - # NOTE(morganfainberg): Skip on expected errors - # possibilities from the `_format_token_index_item` method. - continue - - if expires < current_time: - LOG.debug(('Token `%(token_id)s` is expired, removing ' - 'from `%(user_key)s`.'), - {'token_id': item_id, 'user_key': user_key}) - continue - - if item_id in revoked_token_list: - # NOTE(morganfainberg): If the token has been revoked, it - # can safely be removed from this list. This helps to keep - # the user_token_list as reasonably small as possible. - LOG.debug(('Token `%(token_id)s` is revoked, removing ' - 'from `%(user_key)s`.'), - {'token_id': item_id, 'user_key': user_key}) - continue - filtered_list.append(item) - filtered_list.append((token_id, expires_isotime_str)) - self._set_key(user_key, filtered_list, lock) - return filtered_list - - def _get_current_time(self): - return timeutils.normalize_time(timeutils.utcnow()) - - def _add_to_revocation_list(self, data, lock): - filtered_list = [] - revoked_token_data = {} - - current_time = self._get_current_time() - expires = data['expires'] - - if isinstance(expires, six.string_types): - expires = timeutils.parse_isotime(expires) - - expires = timeutils.normalize_time(expires) - - if expires < current_time: - LOG.warning(_LW('Token `%s` is expired, not adding to the ' - 'revocation list.'), data['id']) - return - - revoked_token_data['expires'] = utils.isotime(expires, - subsecond=True) - revoked_token_data['id'] = data['id'] - - token_data = data['token_data'] - if 'access' in token_data: - # It's a v2 token. - audit_ids = token_data['access']['token']['audit_ids'] - else: - # It's a v3 token. - audit_ids = token_data['token']['audit_ids'] - revoked_token_data['audit_id'] = audit_ids[0] - - token_list = self._get_key_or_default(self.revocation_key, default=[]) - if not isinstance(token_list, list): - # NOTE(morganfainberg): In the case that the revocation list is not - # in a format we understand, reinitialize it. This is an attempt to - # not allow the revocation list to be completely broken if - # somehow the key is changed outside of keystone (e.g. memcache - # that is shared by multiple applications). Logging occurs at error - # level so that the cloud administrators have some awareness that - # the revocation_list needed to be cleared out. In all, this should - # be recoverable. Keystone cannot control external applications - # from changing a key in some backends, however, it is possible to - # gracefully handle and notify of this event. - LOG.error(_LE('Reinitializing revocation list due to error ' - 'in loading revocation list from backend. ' - 'Expected `list` type got `%(type)s`. Old ' - 'revocation list data: %(list)r'), - {'type': type(token_list), 'list': token_list}) - token_list = [] - - # NOTE(morganfainberg): on revocation, cleanup the expired entries, try - # to keep the list of tokens revoked at the minimum. - for token_data in token_list: - try: - expires_at = timeutils.normalize_time( - timeutils.parse_isotime(token_data['expires'])) - except ValueError: - LOG.warning(_LW('Removing `%s` from revocation list due to ' - 'invalid expires data in revocation list.'), - token_data.get('id', 'INVALID_TOKEN_DATA')) - continue - if expires_at > current_time: - filtered_list.append(token_data) - filtered_list.append(revoked_token_data) - self._set_key(self.revocation_key, filtered_list, lock) - - def delete_token(self, token_id): - # Test for existence - with self._store.get_lock(self.revocation_key) as lock: - data = self.get_token(token_id) - ptk = self._prefix_token_id(token_id) - result = self._delete_key(ptk) - self._add_to_revocation_list(data, lock) - return result - - def delete_tokens(self, user_id, tenant_id=None, trust_id=None, - consumer_id=None): - return super(Token, self).delete_tokens( - user_id=user_id, - tenant_id=tenant_id, - trust_id=trust_id, - consumer_id=consumer_id, - ) - - def _format_token_index_item(self, item): - try: - token_id, expires = item - except (TypeError, ValueError): - LOG.debug(('Invalid token entry expected tuple of ' - '`(<token_id>, <expires>)` got: `%(item)r`'), - dict(item=item)) - raise - - try: - expires = timeutils.normalize_time( - timeutils.parse_isotime(expires)) - except ValueError: - LOG.debug(('Invalid expires time on token `%(token_id)s`:' - ' %(expires)r'), - dict(token_id=token_id, expires=expires)) - raise - return token_id, expires - - def _token_match_tenant(self, token_ref, tenant_id): - if token_ref.get('tenant'): - return token_ref['tenant'].get('id') == tenant_id - return False - - def _token_match_trust(self, token_ref, trust_id): - if not token_ref.get('trust_id'): - return False - return token_ref['trust_id'] == trust_id - - def _token_match_consumer(self, token_ref, consumer_id): - try: - oauth = token_ref['token_data']['token']['OS-OAUTH1'] - return oauth.get('consumer_id') == consumer_id - except KeyError: - return False - - def _list_tokens(self, user_id, tenant_id=None, trust_id=None, - consumer_id=None): - # This function is used to generate the list of tokens that should be - # revoked when revoking by token identifiers. This approach will be - # deprecated soon, probably in the Juno release. Setting revoke_by_id - # to False indicates that this kind of recording should not be - # performed. In order to test the revocation events, tokens shouldn't - # be deleted from the backends. This check ensures that tokens are - # still recorded. - if not CONF.token.revoke_by_id: - return [] - tokens = [] - user_key = self._prefix_user_id(user_id) - token_list = self._get_user_token_list_with_expiry(user_key) - current_time = self._get_current_time() - for item in token_list: - try: - token_id, expires = self._format_token_index_item(item) - except (TypeError, ValueError): - # NOTE(morganfainberg): Skip on expected error possibilities - # from the `_format_token_index_item` method. - continue - - if expires < current_time: - continue - - try: - token_ref = self.get_token(token_id) - except exception.TokenNotFound: - # NOTE(morganfainberg): Token doesn't exist, skip it. - continue - if token_ref: - if tenant_id is not None: - if not self._token_match_tenant(token_ref, tenant_id): - continue - if trust_id is not None: - if not self._token_match_trust(token_ref, trust_id): - continue - if consumer_id is not None: - if not self._token_match_consumer(token_ref, consumer_id): - continue - - tokens.append(token_id) - return tokens - - def list_revoked_tokens(self): - revoked_token_list = self._get_key_or_default(self.revocation_key, - default=[]) - if isinstance(revoked_token_list, list): - return revoked_token_list - return [] - - def flush_expired_tokens(self): - """Archive or delete tokens that have expired.""" - raise exception.NotImplemented() |