diff options
Diffstat (limited to 'swiftclient/utils.py')
-rw-r--r-- | swiftclient/utils.py | 60 |
1 files changed, 36 insertions, 24 deletions
diff --git a/swiftclient/utils.py b/swiftclient/utils.py index 656acad..c865d27 100644 --- a/swiftclient/utils.py +++ b/swiftclient/utils.py @@ -13,17 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. """Miscellaneous utility functions for use with Swift.""" + +import base64 from calendar import timegm -try: - from collections.abc import Mapping -except ImportError: - from collections import Mapping +from collections.abc import Mapping import gzip import hashlib import hmac +import io import json import logging -import six import time import traceback @@ -42,7 +41,7 @@ def config_true_value(value): This function comes from swift.common.utils.config_true_value() """ return value is True or \ - (isinstance(value, six.string_types) and value.lower() in TRUE_VALUES) + (isinstance(value, str) and value.lower() in TRUE_VALUES) def prt_bytes(num_bytes, human_flag): @@ -72,7 +71,8 @@ def prt_bytes(num_bytes, human_flag): def generate_temp_url(path, seconds, key, method, absolute=False, - prefix=False, iso8601=False, ip_range=None): + prefix=False, iso8601=False, ip_range=None, + digest='sha256'): """Generates a temporary URL that gives unauthenticated access to the Swift object. @@ -97,7 +97,11 @@ def generate_temp_url(path, seconds, key, method, absolute=False, instead of a UNIX timestamp will be created. :param ip_range: if a valid ip range, restricts the temporary URL to the range of ips. - :raises ValueError: if timestamp or path is not in valid format. + :param digest: digest algorithm to use. Must be one of ``sha1``, + ``sha256``, or ``sha512``. + :raises ValueError: if timestamp or path is not in valid format, + or if digest is not one of ``sha1``, ``sha256``, or + ``sha512``. :return: the path portion of a temporary URL """ try: @@ -134,7 +138,7 @@ def generate_temp_url(path, seconds, key, method, absolute=False, except ValueError: raise ValueError(TIME_ERRMSG) - if isinstance(path, six.binary_type): + if isinstance(path, bytes): try: path_for_body = path.decode('utf-8') except UnicodeDecodeError: @@ -142,6 +146,11 @@ def generate_temp_url(path, seconds, key, method, absolute=False, else: path_for_body = path + if isinstance(digest, str) and digest in ('sha1', 'sha256', 'sha512'): + digest = getattr(hashlib, digest) + if digest not in (hashlib.sha1, hashlib.sha256, hashlib.sha512): + raise ValueError('digest must be one of sha1, sha256, or sha512') + parts = path_for_body.split('/', 4) if len(parts) != 5 or parts[0] or not all(parts[1:(4 if prefix else 5)]): if prefix: @@ -165,7 +174,7 @@ def generate_temp_url(path, seconds, key, method, absolute=False, ('prefix:' if prefix else '') + path_for_body] if ip_range: - if isinstance(ip_range, six.binary_type): + if isinstance(ip_range, bytes): try: ip_range = ip_range.decode('utf-8') except UnicodeDecodeError: @@ -174,27 +183,32 @@ def generate_temp_url(path, seconds, key, method, absolute=False, ) hmac_parts.insert(0, "ip=%s" % ip_range) - hmac_body = u'\n'.join(hmac_parts) + hmac_body = '\n'.join(hmac_parts) # Encode to UTF-8 for py3 compatibility - if not isinstance(key, six.binary_type): + if not isinstance(key, bytes): key = key.encode('utf-8') - sig = hmac.new(key, hmac_body.encode('utf-8'), hashlib.sha1).hexdigest() + mac = hmac.new(key, hmac_body.encode('utf-8'), digest) + if digest == hashlib.sha512: + sig = 'sha512:' + base64.urlsafe_b64encode( + mac.digest()).decode('ascii').strip('=') + else: + sig = mac.hexdigest() if iso8601: expiration = time.strftime( EXPIRES_ISO8601_FORMAT, time.gmtime(expiration)) - temp_url = u'{path}?temp_url_sig={sig}&temp_url_expires={exp}'.format( + temp_url = '{path}?temp_url_sig={sig}&temp_url_expires={exp}'.format( path=path_for_body, sig=sig, exp=expiration) if ip_range: - temp_url += u'&temp_url_ip_range={}'.format(ip_range) + temp_url += '&temp_url_ip_range={}'.format(ip_range) if prefix: - temp_url += u'&temp_url_prefix={}'.format(parts[4]) + temp_url += '&temp_url_prefix={}'.format(parts[4]) # Have return type match path from caller - if isinstance(path, six.binary_type): + if isinstance(path, bytes): return temp_url.encode('utf-8') else: return temp_url @@ -202,7 +216,7 @@ def generate_temp_url(path, seconds, key, method, absolute=False, def get_body(headers, body): if headers.get('content-encoding') == 'gzip': - with gzip.GzipFile(fileobj=six.BytesIO(body), mode='r') as gz: + with gzip.GzipFile(fileobj=io.BytesIO(body), mode='r') as gz: nbody = gz.read() return nbody return body @@ -224,7 +238,7 @@ def split_request_headers(options, prefix=''): if isinstance(options, Mapping): options = options.items() for item in options: - if isinstance(item, six.string_types): + if isinstance(item, str): if ':' not in item: raise ValueError( "Metadata parameter %s must contain a ':'.\n" @@ -256,7 +270,7 @@ def report_traceback(): return None, None -class NoopMD5(object): +class NoopMD5: def __init__(self, *a, **kw): pass @@ -267,7 +281,7 @@ class NoopMD5(object): return '' -class ReadableToIterable(object): +class ReadableToIterable: """ Wrap a filelike object and act as an iterator. @@ -316,7 +330,7 @@ class ReadableToIterable(object): return self -class LengthWrapper(object): +class LengthWrapper: """ Wrap a filelike object with a maximum length. @@ -401,8 +415,6 @@ def n_groups(seq, n): def normalize_manifest_path(path): - if six.PY2 and isinstance(path, six.text_type): - path = path.encode('utf-8') if path.startswith('/'): return path[1:] return path |