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/common/utils.py | 92 ++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 11 deletions(-) (limited to 'keystone-moon/keystone/common/utils.py') diff --git a/keystone-moon/keystone/common/utils.py b/keystone-moon/keystone/common/utils.py index 48336af7..5438ad43 100644 --- a/keystone-moon/keystone/common/utils.py +++ b/keystone-moon/keystone/common/utils.py @@ -22,10 +22,12 @@ import grp import hashlib import os import pwd +import uuid from oslo_config import cfg from oslo_log import log from oslo_serialization import jsonutils +from oslo_utils import reflection from oslo_utils import strutils from oslo_utils import timeutils import passlib.hash @@ -42,6 +44,26 @@ CONF = cfg.CONF LOG = log.getLogger(__name__) +# NOTE(stevermar): This UUID must stay the same, forever, across +# all of keystone to preserve its value as a URN namespace, which is +# used for ID transformation. +RESOURCE_ID_NAMESPACE = uuid.UUID('4332ecab-770b-4288-a680-b9aca3b1b153') + + +def resource_uuid(value): + """Converts input to valid UUID hex digits.""" + try: + uuid.UUID(value) + return value + except ValueError: + if len(value) <= 64: + if six.PY2 and isinstance(value, six.text_type): + value = value.encode('utf-8') + return uuid.uuid5(RESOURCE_ID_NAMESPACE, value).hex + raise ValueError(_('Length of transformable resource id > 64, ' + 'which is max allowed characters')) + + def flatten_dict(d, parent_key=''): """Flatten a nested dictionary @@ -81,6 +103,7 @@ def read_cached_file(filename, cache_info, reload_func=None): class SmarterEncoder(jsonutils.json.JSONEncoder): """Help for JSON encoding dict-like objects.""" + def default(self, obj): if not isinstance(obj, dict) and hasattr(obj, 'iteritems'): return dict(obj.iteritems()) @@ -89,6 +112,7 @@ class SmarterEncoder(jsonutils.json.JSONEncoder): class PKIEncoder(SmarterEncoder): """Special encoder to make token JSON a bit shorter.""" + item_separator = ',' key_separator = ':' @@ -113,6 +137,8 @@ def verify_length_and_trunc_password(password): def hash_access_key(access): hash_ = hashlib.sha256() + if not isinstance(access, six.binary_type): + access = access.encode('utf-8') hash_.update(access) return hash_.hexdigest() @@ -206,7 +232,7 @@ def auth_str_equal(provided, known): :params provided: the first string :params known: the second string - :return: True if the strings are equal. + :returns: True if the strings are equal. This function takes two strings and compares them. It is intended to be used when doing a comparison for authentication purposes to help guard @@ -271,10 +297,9 @@ def get_unix_user(user=None): :param object user: string, int or None specifying the user to lookup. - :return: tuple of (uid, name) + :returns: tuple of (uid, name) """ - if isinstance(user, six.string_types): try: user_info = pwd.getpwnam(user) @@ -295,8 +320,10 @@ def get_unix_user(user=None): elif user is None: user_info = pwd.getpwuid(os.geteuid()) else: + user_cls_name = reflection.get_class_name(user, + fully_qualified=False) raise TypeError('user must be string, int or None; not %s (%r)' % - (user.__class__.__name__, user)) + (user_cls_name, user)) return user_info.pw_uid, user_info.pw_name @@ -328,10 +355,9 @@ def get_unix_group(group=None): :param object group: string, int or None specifying the group to lookup. - :return: tuple of (gid, name) + :returns: tuple of (gid, name) """ - if isinstance(group, six.string_types): try: group_info = grp.getgrnam(group) @@ -354,8 +380,10 @@ def get_unix_group(group=None): elif group is None: group_info = grp.getgrgid(os.getegid()) else: + group_cls_name = reflection.get_class_name(group, + fully_qualified=False) raise TypeError('group must be string, int or None; not %s (%r)' % - (group.__class__.__name__, group)) + (group_cls_name, group)) return group_info.gr_gid, group_info.gr_name @@ -380,7 +408,6 @@ def set_permissions(path, mode=None, user=None, group=None, log=None): if None no logging is performed. """ - if user is None: user_uid, user_name = None, None else: @@ -447,7 +474,6 @@ def make_dirs(path, mode=None, user=None, group=None, log=None): if None no logging is performed. """ - if log: if mode is None: mode_string = str(mode) @@ -483,7 +509,6 @@ _ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' def isotime(at=None, subsecond=False): """Stringify time in ISO 8601 format.""" - # Python provides a similar instance method for datetime.datetime objects # called isoformat(). The format of the strings generated by isoformat() # have a couple of problems: @@ -515,7 +540,7 @@ def get_token_ref(context): """Retrieves KeystoneToken object from the auth context and returns it. :param dict context: The request context. - :raises: exception.Unauthorized if auth context cannot be found. + :raises keystone.exception.Unauthorized: If auth context cannot be found. :returns: The KeystoneToken object. """ try: @@ -526,3 +551,48 @@ def get_token_ref(context): except KeyError: LOG.warning(_LW("Couldn't find the auth context.")) raise exception.Unauthorized() + + +URL_RESERVED_CHARS = ":/?#[]@!$&'()*+,;=" + + +def is_not_url_safe(name): + """Check if a string contains any url reserved characters.""" + return len(list_url_unsafe_chars(name)) > 0 + + +def list_url_unsafe_chars(name): + """Return a list of the reserved characters.""" + reserved_chars = '' + for i in name: + if i in URL_RESERVED_CHARS: + reserved_chars += i + return reserved_chars + + +def lower_case_hostname(url): + """Change the URL's hostname to lowercase""" + # NOTE(gyee): according to + # https://www.w3.org/TR/WD-html40-970708/htmlweb.html, the netloc portion + # of the URL is case-insensitive + parsed = moves.urllib.parse.urlparse(url) + # Note: _replace method for named tuples is public and defined in docs + replaced = parsed._replace(netloc=parsed.netloc.lower()) + return moves.urllib.parse.urlunparse(replaced) + + +def remove_standard_port(url): + # remove the default ports specified in RFC2616 and 2818 + o = moves.urllib.parse.urlparse(url) + separator = ':' + (host, separator, port) = o.netloc.partition(':') + if o.scheme.lower() == 'http' and port == '80': + # NOTE(gyee): _replace() is not a private method. It has an + # an underscore prefix to prevent conflict with field names. + # See https://docs.python.org/2/library/collections.html# + # collections.namedtuple + o = o._replace(netloc=host) + if o.scheme.lower() == 'https' and port == '443': + o = o._replace(netloc=host) + + return moves.urllib.parse.urlunparse(o) -- cgit 1.2.3-korg