summaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/token/provider.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/token/provider.py')
-rw-r--r--keystone-moon/keystone/token/provider.py128
1 files changed, 95 insertions, 33 deletions
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