aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/common/utils.py
diff options
context:
space:
mode:
authorRuan HE <ruan.he@orange.com>2016-06-09 08:12:34 +0000
committerGerrit Code Review <gerrit@172.30.200.206>2016-06-09 08:12:34 +0000
commit4bc079a2664f9a407e332291f34d174625a9d5ea (patch)
tree7481cd5d0a9b3ce37c44c797a1e0d39881221cbe /keystone-moon/keystone/common/utils.py
parent2f179c5790fbbf6144205d3c6e5089e6eb5f048a (diff)
parent2e7b4f2027a1147ca28301e4f88adf8274b39a1f (diff)
Merge "Update Keystone core to Mitaka."
Diffstat (limited to 'keystone-moon/keystone/common/utils.py')
-rw-r--r--keystone-moon/keystone/common/utils.py92
1 files changed, 81 insertions, 11 deletions
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)