aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/token/persistence/backends/kvs.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/token/persistence/backends/kvs.py')
-rw-r--r--keystone-moon/keystone/token/persistence/backends/kvs.py367
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()