From 2e7b4f2027a1147ca28301e4f88adf8274b39a1f Mon Sep 17 00:00:00 2001 From: DUVAL Thomas Date: Thu, 9 Jun 2016 09:11:50 +0200 Subject: Update Keystone core to Mitaka. Change-Id: Ia10d6add16f4a9d25d1f42d420661c46332e69db --- keystone-moon/keystone/token/provider.py | 128 +++++++++++++++++++++++-------- 1 file changed, 95 insertions(+), 33 deletions(-) (limited to 'keystone-moon/keystone/token/provider.py') diff --git a/keystone-moon/keystone/token/provider.py b/keystone-moon/keystone/token/provider.py index 1422e41f..7c4166f4 100644 --- a/keystone-moon/keystone/token/provider.py +++ b/keystone-moon/keystone/token/provider.py @@ -33,12 +33,13 @@ 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(section='token') +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. @@ -51,18 +52,37 @@ VERSIONS = token_model.VERSIONS def base64_encode(s): - """Encode a URL-safe string.""" - return base64.urlsafe_b64encode(s).rstrip('=') + """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.""" + """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] + return base64.urlsafe_b64encode(uuid.uuid4().bytes)[:-2].decode('utf-8') def random_urlsafe_str_to_bytes(s): - """Convert a string generated by ``random_urlsafe_str()`` to bytes.""" + """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 + '==') @@ -201,14 +221,29 @@ class Manager(manager.Manager): self.revoke_api.check_token(token_values) def validate_v2_token(self, token_id, belongs_to=None): - unique_id = utils.generate_unique_id(token_id) + # 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: - token_ref = token_id - token = self._validate_v2_token(token_ref) + # 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) @@ -223,37 +258,52 @@ class Manager(manager.Manager): self.revoke_api.check_token(token_values) def check_revocation(self, token): - version = self.driver.get_token_version(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): - unique_id = utils.generate_unique_id(token_id) - # NOTE(lbragstad): Only go to persistent storage if we have a token to - # fetch from the backend. If the Fernet token provider is being used - # this step isn't necessary. The Fernet token reference is persisted in - # the token_id, so in this case set the token_ref as the identifier of - # the token. - if not self._needs_persistence: - token_ref = token_id - else: - # 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 = self._validate_v3_token(token_ref) - self._is_valid_token(token) - return token + 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: - return self.driver.validate_v3_token(token_id) + # 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.driver.get_token_version(token_ref) + version = self.get_token_version(token_ref) if version == self.V3: - return self.driver.validate_v3_token(token_ref) + 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() @@ -268,7 +318,6 @@ class Manager(manager.Manager): def _is_valid_token(self, token): """Verify the token is valid format and has not expired.""" - current_time = timeutils.normalize_time(timeutils.utcnow()) try: @@ -490,7 +539,8 @@ class Provider(object): :param token_data: token_data :type token_data: dict :returns: token version string - :raises: keystone.token.provider.UnsupportedTokenVersionException + :raises keystone.exception.UnsupportedTokenVersionException: + If the token version is not expected. """ raise exception.NotImplemented() # pragma: no cover @@ -548,8 +598,19 @@ class Provider(object): :param token_ref: the token reference :type token_ref: dict :returns: token data - :raises: keystone.exception.TokenNotFound + :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 @@ -560,7 +621,7 @@ class Provider(object): :param token_ref: the token reference :type token_ref: dict :returns: token data - :raises: keystone.exception.TokenNotFound + :raises keystone.exception.TokenNotFound: If the token doesn't exist. """ raise exception.NotImplemented() # pragma: no cover @@ -570,6 +631,7 @@ class Provider(object): :param token_data: token information :type token_data: dict - returns: token identifier + :returns: token identifier + :rtype: six.text_type """ raise exception.NotImplemented() # pragma: no cover -- cgit 1.2.3-korg