From 920a49cfa055733d575282973e23558c33087a4a Mon Sep 17 00:00:00 2001 From: RHE Date: Fri, 24 Nov 2017 13:54:26 +0100 Subject: remove keystone-moon Change-Id: I80d7c9b669f19d5f6607e162de8e0e55c2f80fdd Signed-off-by: RHE --- keystone-moon/keystone/token/provider.py | 637 ------------------------------- 1 file changed, 637 deletions(-) delete mode 100644 keystone-moon/keystone/token/provider.py (limited to 'keystone-moon/keystone/token/provider.py') diff --git a/keystone-moon/keystone/token/provider.py b/keystone-moon/keystone/token/provider.py deleted file mode 100644 index 7c4166f4..00000000 --- a/keystone-moon/keystone/token/provider.py +++ /dev/null @@ -1,637 +0,0 @@ -# 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. - -"""Token provider interface.""" - -import abc -import base64 -import datetime -import sys -import uuid - -from oslo_config import cfg -from oslo_log import log -from oslo_utils import timeutils -import six - -from keystone.common import cache -from keystone.common import dependency -from keystone.common import manager -from keystone import exception -from keystone.i18n import _, _LE -from keystone.models import token_model -from keystone import notifications -from keystone.token import persistence -from keystone.token import providers -from keystone.token import utils - - -CONF = cfg.CONF -LOG = log.getLogger(__name__) -MEMOIZE = cache.get_memoization_decorator(group='token') - -# NOTE(morganfainberg): This is for compatibility in case someone was relying -# on the old location of the UnsupportedTokenVersionException for their code. -UnsupportedTokenVersionException = exception.UnsupportedTokenVersionException - -# supported token versions -V2 = token_model.V2 -V3 = token_model.V3 -VERSIONS = token_model.VERSIONS - - -def base64_encode(s): - """Encode a URL-safe string. - - :type s: six.text_type - :rtype: six.text_type - - """ - # urlsafe_b64encode() returns six.binary_type so need to convert to - # six.text_type, might as well do it before stripping. - return base64.urlsafe_b64encode(s).decode('utf-8').rstrip('=') - - -def random_urlsafe_str(): - """Generate a random URL-safe string. - - :rtype: six.text_type - - """ - # chop the padding (==) off the end of the encoding to save space - return base64.urlsafe_b64encode(uuid.uuid4().bytes)[:-2].decode('utf-8') - - -def random_urlsafe_str_to_bytes(s): - """Convert a string from :func:`random_urlsafe_str()` to six.binary_type. - - :type s: six.text_type - :rtype: six.binary_type - - """ - # urlsafe_b64decode() requires str, unicode isn't accepted. - s = str(s) - - # restore the padding (==) at the end of the string - return base64.urlsafe_b64decode(s + '==') - - -def default_expire_time(): - """Determine when a fresh token should expire. - - Expiration time varies based on configuration (see ``[token] expiration``). - - :returns: a naive UTC datetime.datetime object - - """ - expire_delta = datetime.timedelta(seconds=CONF.token.expiration) - return timeutils.utcnow() + expire_delta - - -def audit_info(parent_audit_id): - """Build the audit data for a token. - - If ``parent_audit_id`` is None, the list will be one element in length - containing a newly generated audit_id. - - If ``parent_audit_id`` is supplied, the list will be two elements in length - containing a newly generated audit_id and the ``parent_audit_id``. The - ``parent_audit_id`` will always be element index 1 in the resulting - list. - - :param parent_audit_id: the audit of the original token in the chain - :type parent_audit_id: str - :returns: Keystone token audit data - """ - audit_id = random_urlsafe_str() - if parent_audit_id is not None: - return [audit_id, parent_audit_id] - return [audit_id] - - -@dependency.provider('token_provider_api') -@dependency.requires('assignment_api', 'revoke_api') -class Manager(manager.Manager): - """Default pivot point for the token provider backend. - - See :mod:`keystone.common.manager.Manager` for more details on how this - dynamically calls the backend. - - """ - - driver_namespace = 'keystone.token.provider' - - V2 = V2 - V3 = V3 - VERSIONS = VERSIONS - INVALIDATE_PROJECT_TOKEN_PERSISTENCE = 'invalidate_project_tokens' - INVALIDATE_USER_TOKEN_PERSISTENCE = 'invalidate_user_tokens' - _persistence_manager = None - - def __init__(self): - super(Manager, self).__init__(CONF.token.provider) - self._register_callback_listeners() - - def _register_callback_listeners(self): - # This is used by the @dependency.provider decorator to register the - # provider (token_provider_api) manager to listen for trust deletions. - callbacks = { - notifications.ACTIONS.deleted: [ - ['OS-TRUST:trust', self._trust_deleted_event_callback], - ['user', self._delete_user_tokens_callback], - ['domain', self._delete_domain_tokens_callback], - ], - notifications.ACTIONS.disabled: [ - ['user', self._delete_user_tokens_callback], - ['domain', self._delete_domain_tokens_callback], - ['project', self._delete_project_tokens_callback], - ], - notifications.ACTIONS.internal: [ - [notifications.INVALIDATE_USER_TOKEN_PERSISTENCE, - self._delete_user_tokens_callback], - [notifications.INVALIDATE_USER_PROJECT_TOKEN_PERSISTENCE, - self._delete_user_project_tokens_callback], - [notifications.INVALIDATE_USER_OAUTH_CONSUMER_TOKENS, - self._delete_user_oauth_consumer_tokens_callback], - ] - } - - for event, cb_info in callbacks.items(): - for resource_type, callback_fns in cb_info: - notifications.register_event_callback(event, resource_type, - callback_fns) - - @property - def _needs_persistence(self): - return self.driver.needs_persistence() - - @property - def _persistence(self): - # NOTE(morganfainberg): This should not be handled via __init__ to - # avoid dependency injection oddities circular dependencies (where - # the provider manager requires the token persistence manager, which - # requires the token provider manager). - if self._persistence_manager is None: - self._persistence_manager = persistence.PersistenceManager() - return self._persistence_manager - - def _create_token(self, token_id, token_data): - try: - if isinstance(token_data['expires'], six.string_types): - token_data['expires'] = timeutils.normalize_time( - timeutils.parse_isotime(token_data['expires'])) - self._persistence.create_token(token_id, token_data) - except Exception: - exc_info = sys.exc_info() - # an identical token may have been created already. - # if so, return the token_data as it is also identical - try: - self._persistence.get_token(token_id) - except exception.TokenNotFound: - six.reraise(*exc_info) - - def validate_token(self, token_id, belongs_to=None): - unique_id = utils.generate_unique_id(token_id) - # NOTE(morganfainberg): Ensure we never use the long-form token_id - # (PKI) as part of the cache_key. - token = self._validate_token(unique_id) - self._token_belongs_to(token, belongs_to) - self._is_valid_token(token) - return token - - def check_revocation_v2(self, token): - try: - token_data = token['access'] - except KeyError: - raise exception.TokenNotFound(_('Failed to validate token')) - - token_values = self.revoke_api.model.build_token_values_v2( - token_data, CONF.identity.default_domain_id) - self.revoke_api.check_token(token_values) - - def validate_v2_token(self, token_id, belongs_to=None): - # NOTE(lbragstad): Only go to the persistence backend if the token - # provider requires it. - if self._needs_persistence: - # NOTE(morganfainberg): Ensure we never use the long-form token_id - # (PKI) as part of the cache_key. - unique_id = utils.generate_unique_id(token_id) - token_ref = self._persistence.get_token(unique_id) - token = self._validate_v2_token(token_ref) - else: - # NOTE(lbragstad): If the token doesn't require persistence, then - # it is a fernet token. The fernet token provider doesn't care if - # it's creating version 2.0 tokens or v3 tokens, so we use the same - # validate_non_persistent_token() method to validate both. Then we - # can leverage a separate method to make version 3 token data look - # like version 2.0 token data. The pattern we want to move towards - # is one where the token providers just handle data and the - # controller layers handle interpreting the token data in a format - # that makes sense for the request. - v3_token_ref = self.validate_non_persistent_token(token_id) - v2_token_data_helper = providers.common.V2TokenDataHelper() - token = v2_token_data_helper.v3_to_v2_token(v3_token_ref) - - # these are common things that happen regardless of token provider - token['access']['token']['id'] = token_id - self._token_belongs_to(token, belongs_to) - self._is_valid_token(token) - return token - - def check_revocation_v3(self, token): - try: - token_data = token['token'] - except KeyError: - raise exception.TokenNotFound(_('Failed to validate token')) - token_values = self.revoke_api.model.build_token_values(token_data) - self.revoke_api.check_token(token_values) - - def check_revocation(self, token): - version = self.get_token_version(token) - if version == V2: - return self.check_revocation_v2(token) - else: - return self.check_revocation_v3(token) - - def validate_v3_token(self, token_id): - if not token_id: - raise exception.TokenNotFound(_('No token in the request')) - - try: - # NOTE(lbragstad): Only go to persistent storage if we have a token - # to fetch from the backend (the driver persists the token). - # Otherwise the information about the token must be in the token - # id. - if not self._needs_persistence: - token_ref = self.validate_non_persistent_token(token_id) - else: - unique_id = utils.generate_unique_id(token_id) - # NOTE(morganfainberg): Ensure we never use the long-form - # token_id (PKI) as part of the cache_key. - token_ref = self._persistence.get_token(unique_id) - token_ref = self._validate_v3_token(token_ref) - self._is_valid_token(token_ref) - return token_ref - except exception.Unauthorized as e: - LOG.debug('Unable to validate token: %s', e) - raise exception.TokenNotFound(token_id=token_id) - - @MEMOIZE - def _validate_token(self, token_id): - if not token_id: - raise exception.TokenNotFound(_('No token in the request')) - - if not self._needs_persistence: - # NOTE(lbragstad): This will validate v2 and v3 non-persistent - # tokens. - return self.driver.validate_non_persistent_token(token_id) - token_ref = self._persistence.get_token(token_id) - version = self.get_token_version(token_ref) - if version == self.V3: - try: - return self.driver.validate_v3_token(token_ref) - except exception.Unauthorized as e: - LOG.debug('Unable to validate token: %s', e) - raise exception.TokenNotFound(token_id=token_id) - elif version == self.V2: - return self.driver.validate_v2_token(token_ref) - raise exception.UnsupportedTokenVersionException() - - @MEMOIZE - def _validate_v2_token(self, token_id): - return self.driver.validate_v2_token(token_id) - - @MEMOIZE - def _validate_v3_token(self, token_id): - return self.driver.validate_v3_token(token_id) - - def _is_valid_token(self, token): - """Verify the token is valid format and has not expired.""" - current_time = timeutils.normalize_time(timeutils.utcnow()) - - try: - # Get the data we need from the correct location (V2 and V3 tokens - # differ in structure, Try V3 first, fall back to V2 second) - token_data = token.get('token', token.get('access')) - expires_at = token_data.get('expires_at', - token_data.get('expires')) - if not expires_at: - expires_at = token_data['token']['expires'] - expiry = timeutils.normalize_time( - timeutils.parse_isotime(expires_at)) - except Exception: - LOG.exception(_LE('Unexpected error or malformed token ' - 'determining token expiry: %s'), token) - raise exception.TokenNotFound(_('Failed to validate token')) - - if current_time < expiry: - self.check_revocation(token) - # Token has not expired and has not been revoked. - return None - else: - raise exception.TokenNotFound(_('Failed to validate token')) - - def _token_belongs_to(self, token, belongs_to): - """Check if the token belongs to the right tenant. - - This is only used on v2 tokens. The structural validity of the token - will have already been checked before this method is called. - - """ - if belongs_to: - token_data = token['access']['token'] - if ('tenant' not in token_data or - token_data['tenant']['id'] != belongs_to): - raise exception.Unauthorized() - - def issue_v2_token(self, token_ref, roles_ref=None, catalog_ref=None): - token_id, token_data = self.driver.issue_v2_token( - token_ref, roles_ref, catalog_ref) - - if self._needs_persistence: - data = dict(key=token_id, - id=token_id, - expires=token_data['access']['token']['expires'], - user=token_ref['user'], - tenant=token_ref['tenant'], - metadata=token_ref['metadata'], - token_data=token_data, - bind=token_ref.get('bind'), - trust_id=token_ref['metadata'].get('trust_id'), - token_version=self.V2) - self._create_token(token_id, data) - - return token_id, token_data - - def issue_v3_token(self, user_id, method_names, expires_at=None, - project_id=None, domain_id=None, auth_context=None, - trust=None, metadata_ref=None, include_catalog=True, - parent_audit_id=None): - token_id, token_data = self.driver.issue_v3_token( - user_id, method_names, expires_at, project_id, domain_id, - auth_context, trust, metadata_ref, include_catalog, - parent_audit_id) - - if metadata_ref is None: - metadata_ref = {} - - if 'project' in token_data['token']: - # project-scoped token, fill in the v2 token data - # all we care are the role IDs - - # FIXME(gyee): is there really a need to store roles in metadata? - role_ids = [r['id'] for r in token_data['token']['roles']] - metadata_ref = {'roles': role_ids} - - if trust: - metadata_ref.setdefault('trust_id', trust['id']) - metadata_ref.setdefault('trustee_user_id', - trust['trustee_user_id']) - - data = dict(key=token_id, - id=token_id, - expires=token_data['token']['expires_at'], - user=token_data['token']['user'], - tenant=token_data['token'].get('project'), - metadata=metadata_ref, - token_data=token_data, - trust_id=trust['id'] if trust else None, - token_version=self.V3) - if self._needs_persistence: - self._create_token(token_id, data) - return token_id, token_data - - def invalidate_individual_token_cache(self, token_id): - # NOTE(morganfainberg): invalidate takes the exact same arguments as - # the normal method, this means we need to pass "self" in (which gets - # stripped off). - - # FIXME(morganfainberg): Does this cache actually need to be - # invalidated? We maintain a cached revocation list, which should be - # consulted before accepting a token as valid. For now we will - # do the explicit individual token invalidation. - - self._validate_token.invalidate(self, token_id) - self._validate_v2_token.invalidate(self, token_id) - self._validate_v3_token.invalidate(self, token_id) - - def revoke_token(self, token_id, revoke_chain=False): - revoke_by_expires = False - project_id = None - domain_id = None - - token_ref = token_model.KeystoneToken( - token_id=token_id, - token_data=self.validate_token(token_id)) - - user_id = token_ref.user_id - expires_at = token_ref.expires - audit_id = token_ref.audit_id - audit_chain_id = token_ref.audit_chain_id - if token_ref.project_scoped: - project_id = token_ref.project_id - if token_ref.domain_scoped: - domain_id = token_ref.domain_id - - if audit_id is None and not revoke_chain: - LOG.debug('Received token with no audit_id.') - revoke_by_expires = True - - if audit_chain_id is None and revoke_chain: - LOG.debug('Received token with no audit_chain_id.') - revoke_by_expires = True - - if revoke_by_expires: - self.revoke_api.revoke_by_expiration(user_id, expires_at, - project_id=project_id, - domain_id=domain_id) - elif revoke_chain: - self.revoke_api.revoke_by_audit_chain_id(audit_chain_id, - project_id=project_id, - domain_id=domain_id) - else: - self.revoke_api.revoke_by_audit_id(audit_id) - - if CONF.token.revoke_by_id and self._needs_persistence: - self._persistence.delete_token(token_id=token_id) - - def list_revoked_tokens(self): - return self._persistence.list_revoked_tokens() - - def _trust_deleted_event_callback(self, service, resource_type, operation, - payload): - if CONF.token.revoke_by_id: - trust_id = payload['resource_info'] - trust = self.trust_api.get_trust(trust_id, deleted=True) - self._persistence.delete_tokens(user_id=trust['trustor_user_id'], - trust_id=trust_id) - - def _delete_user_tokens_callback(self, service, resource_type, operation, - payload): - if CONF.token.revoke_by_id: - user_id = payload['resource_info'] - self._persistence.delete_tokens_for_user(user_id) - - def _delete_domain_tokens_callback(self, service, resource_type, - operation, payload): - if CONF.token.revoke_by_id: - domain_id = payload['resource_info'] - self._persistence.delete_tokens_for_domain(domain_id=domain_id) - - def _delete_user_project_tokens_callback(self, service, resource_type, - operation, payload): - if CONF.token.revoke_by_id: - user_id = payload['resource_info']['user_id'] - project_id = payload['resource_info']['project_id'] - self._persistence.delete_tokens_for_user(user_id=user_id, - project_id=project_id) - - def _delete_project_tokens_callback(self, service, resource_type, - operation, payload): - if CONF.token.revoke_by_id: - project_id = payload['resource_info'] - self._persistence.delete_tokens_for_users( - self.assignment_api.list_user_ids_for_project(project_id), - project_id=project_id) - - def _delete_user_oauth_consumer_tokens_callback(self, service, - resource_type, operation, - payload): - if CONF.token.revoke_by_id: - user_id = payload['resource_info']['user_id'] - consumer_id = payload['resource_info']['consumer_id'] - self._persistence.delete_tokens(user_id=user_id, - consumer_id=consumer_id) - - -@six.add_metaclass(abc.ABCMeta) -class Provider(object): - """Interface description for a Token provider.""" - - @abc.abstractmethod - def needs_persistence(self): - """Determine if the token should be persisted. - - If the token provider requires that the token be persisted to a - backend this should return True, otherwise return False. - - """ - raise exception.NotImplemented() # pragma: no cover - - @abc.abstractmethod - def get_token_version(self, token_data): - """Return the version of the given token data. - - If the given token data is unrecognizable, - UnsupportedTokenVersionException is raised. - - :param token_data: token_data - :type token_data: dict - :returns: token version string - :raises keystone.exception.UnsupportedTokenVersionException: - If the token version is not expected. - """ - raise exception.NotImplemented() # pragma: no cover - - @abc.abstractmethod - def issue_v2_token(self, token_ref, roles_ref=None, catalog_ref=None): - """Issue a V2 token. - - :param token_ref: token data to generate token from - :type token_ref: dict - :param roles_ref: optional roles list - :type roles_ref: dict - :param catalog_ref: optional catalog information - :type catalog_ref: dict - :returns: (token_id, token_data) - """ - raise exception.NotImplemented() # pragma: no cover - - @abc.abstractmethod - def issue_v3_token(self, user_id, method_names, expires_at=None, - project_id=None, domain_id=None, auth_context=None, - trust=None, metadata_ref=None, include_catalog=True, - parent_audit_id=None): - """Issue a V3 Token. - - :param user_id: identity of the user - :type user_id: string - :param method_names: names of authentication methods - :type method_names: list - :param expires_at: optional time the token will expire - :type expires_at: string - :param project_id: optional project identity - :type project_id: string - :param domain_id: optional domain identity - :type domain_id: string - :param auth_context: optional context from the authorization plugins - :type auth_context: dict - :param trust: optional trust reference - :type trust: dict - :param metadata_ref: optional metadata reference - :type metadata_ref: dict - :param include_catalog: optional, include the catalog in token data - :type include_catalog: boolean - :param parent_audit_id: optional, the audit id of the parent token - :type parent_audit_id: string - :returns: (token_id, token_data) - """ - raise exception.NotImplemented() # pragma: no cover - - @abc.abstractmethod - def validate_v2_token(self, token_ref): - """Validate the given V2 token and return the token data. - - Must raise Unauthorized exception if unable to validate token. - - :param token_ref: the token reference - :type token_ref: dict - :returns: token data - :raises keystone.exception.TokenNotFound: If the token doesn't exist. - - """ - raise exception.NotImplemented() # pragma: no cover - - @abc.abstractmethod - def validate_non_persistent_token(self, token_id): - """Validate a given non-persistent token id and return the token_data. - - :param token_id: the token id - :type token_id: string - :returns: token data - :raises keystone.exception.TokenNotFound: When the token is invalid - """ - raise exception.NotImplemented() # pragma: no cover - - @abc.abstractmethod - def validate_v3_token(self, token_ref): - """Validate the given V3 token and return the token_data. - - :param token_ref: the token reference - :type token_ref: dict - :returns: token data - :raises keystone.exception.TokenNotFound: If the token doesn't exist. - """ - raise exception.NotImplemented() # pragma: no cover - - @abc.abstractmethod - def _get_token_id(self, token_data): - """Generate the token_id based upon the data in token_data. - - :param token_data: token information - :type token_data: dict - :returns: token identifier - :rtype: six.text_type - """ - raise exception.NotImplemented() # pragma: no cover -- cgit 1.2.3-korg