"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "keystone/token/token_formatters.py" between
keystone-16.0.1.tar.gz and keystone-17.0.0.tar.gz

About: OpenStack Keystone (Core Service: Identity) provides an authentication and authorization service for other OpenStack services. Provides a catalog of endpoints for all OpenStack services.
The "Ussuri" series (latest release).

token_formatters.py  (keystone-16.0.1):token_formatters.py  (keystone-17.0.0)
skipping to change at line 22 skipping to change at line 22
import base64 import base64
import datetime import datetime
import struct import struct
import uuid import uuid
from cryptography import fernet from cryptography import fernet
import msgpack import msgpack
from oslo_log import log from oslo_log import log
from oslo_utils import timeutils from oslo_utils import timeutils
import six
from six.moves import map
from keystone.auth import plugins as auth_plugins from keystone.auth import plugins as auth_plugins
from keystone.common import fernet_utils as utils from keystone.common import fernet_utils as utils
from keystone.common import utils as ks_utils from keystone.common import utils as ks_utils
import keystone.conf import keystone.conf
from keystone import exception from keystone import exception
from keystone.i18n import _ from keystone.i18n import _
CONF = keystone.conf.CONF CONF = keystone.conf.CONF
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
skipping to change at line 72 skipping to change at line 70
if not keys: if not keys:
raise exception.KeysNotFound() raise exception.KeysNotFound()
fernet_instances = [fernet.Fernet(key) for key in keys] fernet_instances = [fernet.Fernet(key) for key in keys]
return fernet.MultiFernet(fernet_instances) return fernet.MultiFernet(fernet_instances)
def pack(self, payload): def pack(self, payload):
"""Pack a payload for transport as a token. """Pack a payload for transport as a token.
:type payload: six.binary_type :type payload: bytes
:rtype: six.text_type :rtype: str
""" """
# base64 padding (if any) is not URL-safe # base64 padding (if any) is not URL-safe
return self.crypto.encrypt(payload).rstrip(b'=').decode('utf-8') return self.crypto.encrypt(payload).rstrip(b'=').decode('utf-8')
def unpack(self, token): def unpack(self, token):
"""Unpack a token, and validate the payload. """Unpack a token, and validate the payload.
:type token: six.text_type :type token: str
:rtype: six.binary_type :rtype: bytes
""" """
token = TokenFormatter.restore_padding(token) token = TokenFormatter.restore_padding(token)
try: try:
return self.crypto.decrypt(token.encode('utf-8')) return self.crypto.decrypt(token.encode('utf-8'))
except fernet.InvalidToken: except fernet.InvalidToken:
raise exception.ValidationError( raise exception.ValidationError(
_('Could not recognize Fernet token')) _('Could not recognize Fernet token'))
@classmethod @classmethod
def restore_padding(cls, token): def restore_padding(cls, token):
"""Restore padding based on token size. """Restore padding based on token size.
:param token: token to restore padding on :param token: token to restore padding on
:type token: six.text_type :type token: str
:returns: token with correct padding :returns: token with correct padding
""" """
# Re-inflate the padding # Re-inflate the padding
mod_returned = len(token) % 4 mod_returned = len(token) % 4
if mod_returned: if mod_returned:
missing_padding = 4 - mod_returned missing_padding = 4 - mod_returned
token += '=' * missing_padding token += '=' * missing_padding
return token return token
@classmethod @classmethod
def creation_time(cls, fernet_token): def creation_time(cls, fernet_token):
"""Return the creation time of a valid Fernet token. """Return the creation time of a valid Fernet token.
:type fernet_token: six.text_type :type fernet_token: str
""" """
fernet_token = TokenFormatter.restore_padding(fernet_token) fernet_token = TokenFormatter.restore_padding(fernet_token)
# fernet_token is six.text_type # fernet_token is str
# Fernet tokens are base64 encoded, so we need to unpack them first # Fernet tokens are base64 encoded, so we need to unpack them first
# urlsafe_b64decode() requires six.binary_type # urlsafe_b64decode() requires bytes
token_bytes = base64.urlsafe_b64decode(fernet_token.encode('utf-8')) token_bytes = base64.urlsafe_b64decode(fernet_token.encode('utf-8'))
# slice into the byte array to get just the timestamp # slice into the byte array to get just the timestamp
timestamp_bytes = token_bytes[TIMESTAMP_START:TIMESTAMP_END] timestamp_bytes = token_bytes[TIMESTAMP_START:TIMESTAMP_END]
# convert those bytes to an integer # convert those bytes to an integer
# (it's a 64-bit "unsigned long long int" in C) # (it's a 64-bit "unsigned long long int" in C)
timestamp_int = struct.unpack(">Q", timestamp_bytes)[0] timestamp_int = struct.unpack(">Q", timestamp_bytes)[0]
# and with an integer, it's trivial to produce a datetime object # and with an integer, it's trivial to produce a datetime object
skipping to change at line 169 skipping to change at line 167
if len(token) > 255: if len(token) > 255:
LOG.info('Fernet token created with length of %d ' LOG.info('Fernet token created with length of %d '
'characters, which exceeds 255 characters', 'characters, which exceeds 255 characters',
len(token)) len(token))
return token return token
def validate_token(self, token): def validate_token(self, token):
"""Validate a Fernet token and returns the payload attributes. """Validate a Fernet token and returns the payload attributes.
:type token: six.text_type :type token: str
""" """
serialized_payload = self.unpack(token) serialized_payload = self.unpack(token)
versioned_payload = msgpack.unpackb(serialized_payload) versioned_payload = msgpack.unpackb(serialized_payload)
version, payload = versioned_payload[0], versioned_payload[1:] version, payload = versioned_payload[0], versioned_payload[1:]
for payload_class in _PAYLOAD_CLASSES: for payload_class in _PAYLOAD_CLASSES:
if version == payload_class.version: if version == payload_class.version:
(user_id, methods, system, project_id, domain_id, (user_id, methods, system, project_id, domain_id,
expires_at, audit_ids, trust_id, federated_group_ids, expires_at, audit_ids, trust_id, federated_group_ids,
skipping to change at line 324 skipping to change at line 322
# ValueError: this might not be a UUID, depending on the # ValueError: this might not be a UUID, depending on the
# situation (i.e. federation) # situation (i.e. federation)
# TypeError: the provided value may be binary encoded # TypeError: the provided value may be binary encoded
# in which case just return the value (i.e. Python 3) # in which case just return the value (i.e. Python 3)
return (False, value) return (False, value)
@classmethod @classmethod
def base64_encode(cls, s): def base64_encode(cls, s):
"""Encode a URL-safe string. """Encode a URL-safe string.
:type s: six.text_type :type s: str
:rtype: six.text_type :rtype: str
""" """
# urlsafe_b64encode() returns six.binary_type so need to convert to # urlsafe_b64encode() returns bytes so need to convert to
# six.text_type, might as well do it before stripping. # str, might as well do it before stripping.
return base64.urlsafe_b64encode(s).decode('utf-8').rstrip('=') return base64.urlsafe_b64encode(s).decode('utf-8').rstrip('=')
@classmethod @classmethod
def random_urlsafe_str_to_bytes(cls, s): def random_urlsafe_str_to_bytes(cls, s):
"""Convert a string from :func:`random_urlsafe_str()` to six.binary_type . """Convert string from :func:`random_urlsafe_str()` to bytes.
:type s: six.text_type :type s: str
:rtype: six.binary_type :rtype: bytes
""" """
# urlsafe_b64decode() requires str, unicode isn't accepted. # urlsafe_b64decode() requires str, unicode isn't accepted.
s = str(s) s = str(s)
# restore the padding (==) at the end of the string # restore the padding (==) at the end of the string
return base64.urlsafe_b64decode(s + '==') return base64.urlsafe_b64decode(s + '==')
@classmethod @classmethod
def _convert_or_decode(cls, is_stored_as_bytes, value): def _convert_or_decode(cls, is_stored_as_bytes, value):
"""Convert a value to text type, translating uuid -> hex if required. """Convert a value to text type, translating uuid -> hex if required.
:param is_stored_as_bytes: whether value is already bytes :param is_stored_as_bytes: whether value is already bytes
:type is_stored_as_bytes: six.boolean :type is_stored_as_bytes: boolean
:param value: value to attempt to convert to bytes :param value: value to attempt to convert to bytes
:type value: six.text_type or six.binary_type :type value: str or bytes
:rtype: six.text_type :rtype: str
""" """
if is_stored_as_bytes: if is_stored_as_bytes:
return cls.convert_uuid_bytes_to_hex(value) return cls.convert_uuid_bytes_to_hex(value)
elif isinstance(value, six.binary_type): elif isinstance(value, bytes):
return value.decode('utf-8') return value.decode('utf-8')
return value return value
class UnscopedPayload(BasePayload): class UnscopedPayload(BasePayload):
version = 0 version = 0
@classmethod @classmethod
def assemble(cls, user_id, methods, system, project_id, domain_id, def assemble(cls, user_id, methods, system, project_id, domain_id,
expires_at, audit_ids, trust_id, federated_group_ids, expires_at, audit_ids, trust_id, federated_group_ids,
identity_provider_id, protocol_id, access_token_id, identity_provider_id, protocol_id, access_token_id,
skipping to change at line 430 skipping to change at line 428
@classmethod @classmethod
def disassemble(cls, payload): def disassemble(cls, payload):
(is_stored_as_bytes, user_id) = payload[0] (is_stored_as_bytes, user_id) = payload[0]
user_id = cls._convert_or_decode(is_stored_as_bytes, user_id) user_id = cls._convert_or_decode(is_stored_as_bytes, user_id)
methods = auth_plugins.convert_integer_to_method_list(payload[1]) methods = auth_plugins.convert_integer_to_method_list(payload[1])
try: try:
domain_id = cls.convert_uuid_bytes_to_hex(payload[2]) domain_id = cls.convert_uuid_bytes_to_hex(payload[2])
except ValueError: except ValueError:
# the default domain ID is configurable, and probably isn't a UUID # the default domain ID is configurable, and probably isn't a UUID
if six.PY3 and isinstance(payload[2], six.binary_type): if isinstance(payload[2], bytes):
payload[2] = payload[2].decode('utf-8') payload[2] = payload[2].decode('utf-8')
if payload[2] == CONF.identity.default_domain_id: if payload[2] == CONF.identity.default_domain_id:
domain_id = payload[2] domain_id = payload[2]
else: else:
raise raise
expires_at_str = cls._convert_float_to_time_string(payload[3]) expires_at_str = cls._convert_float_to_time_string(payload[3])
audit_ids = list(map(cls.base64_encode, payload[4])) audit_ids = list(map(cls.base64_encode, payload[4]))
system = None system = None
project_id = None project_id = None
trust_id = None trust_id = None
skipping to change at line 568 skipping to change at line 566
@classmethod @classmethod
def disassemble(cls, payload): def disassemble(cls, payload):
(is_stored_as_bytes, user_id) = payload[0] (is_stored_as_bytes, user_id) = payload[0]
user_id = cls._convert_or_decode(is_stored_as_bytes, user_id) user_id = cls._convert_or_decode(is_stored_as_bytes, user_id)
methods = auth_plugins.convert_integer_to_method_list(payload[1]) methods = auth_plugins.convert_integer_to_method_list(payload[1])
group_ids = list(map(cls.unpack_group_id, payload[2])) group_ids = list(map(cls.unpack_group_id, payload[2]))
(is_stored_as_bytes, idp_id) = payload[3] (is_stored_as_bytes, idp_id) = payload[3]
idp_id = cls._convert_or_decode(is_stored_as_bytes, idp_id) idp_id = cls._convert_or_decode(is_stored_as_bytes, idp_id)
protocol_id = payload[4] protocol_id = payload[4]
if isinstance(protocol_id, six.binary_type): if isinstance(protocol_id, bytes):
protocol_id = protocol_id.decode('utf-8') protocol_id = protocol_id.decode('utf-8')
expires_at_str = cls._convert_float_to_time_string(payload[5]) expires_at_str = cls._convert_float_to_time_string(payload[5])
audit_ids = list(map(cls.base64_encode, payload[6])) audit_ids = list(map(cls.base64_encode, payload[6]))
system = None system = None
project_id = None project_id = None
domain_id = None domain_id = None
trust_id = None trust_id = None
access_token_id = None access_token_id = None
app_cred_id = None app_cred_id = None
return (user_id, methods, system, project_id, domain_id, return (user_id, methods, system, project_id, domain_id,
skipping to change at line 620 skipping to change at line 618
project_id = ( project_id = (
scope_id scope_id
if cls.version == FederatedProjectScopedPayload.version else None) if cls.version == FederatedProjectScopedPayload.version else None)
domain_id = ( domain_id = (
scope_id scope_id
if cls.version == FederatedDomainScopedPayload.version else None) if cls.version == FederatedDomainScopedPayload.version else None)
group_ids = list(map(cls.unpack_group_id, payload[3])) group_ids = list(map(cls.unpack_group_id, payload[3]))
(is_stored_as_bytes, idp_id) = payload[4] (is_stored_as_bytes, idp_id) = payload[4]
idp_id = cls._convert_or_decode(is_stored_as_bytes, idp_id) idp_id = cls._convert_or_decode(is_stored_as_bytes, idp_id)
protocol_id = payload[5] protocol_id = payload[5]
if six.PY3 and isinstance(protocol_id, six.binary_type): if isinstance(protocol_id, bytes):
protocol_id = protocol_id.decode('utf-8') protocol_id = protocol_id.decode('utf-8')
expires_at_str = cls._convert_float_to_time_string(payload[6]) expires_at_str = cls._convert_float_to_time_string(payload[6])
audit_ids = list(map(cls.base64_encode, payload[7])) audit_ids = list(map(cls.base64_encode, payload[7]))
system = None system = None
trust_id = None trust_id = None
access_token_id = None access_token_id = None
app_cred_id = None app_cred_id = None
return (user_id, methods, system, project_id, domain_id, return (user_id, methods, system, project_id, domain_id,
expires_at_str, audit_ids, trust_id, group_ids, idp_id, expires_at_str, audit_ids, trust_id, group_ids, idp_id,
protocol_id, access_token_id, app_cred_id) protocol_id, access_token_id, app_cred_id)
 End of changes. 18 change blocks. 
25 lines changed or deleted 23 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)