diff options
author | Christopher Bartz <bartz@dkrz.de> | 2017-01-20 18:04:31 +0100 |
---|---|---|
committer | Thiago da Silva <thiago@redhat.com> | 2017-03-29 14:27:39 -0400 |
commit | 8e08931b9ffd872a2b5537149a5d3b9f3e9bb5d4 (patch) | |
tree | 600a9698b01d9536c53185b3a7619ac938f4b862 /swiftclient | |
parent | 2710ff255b5d3954eebdf469587bf73bbfd48431 (diff) | |
download | python-swiftclient-8e08931b9ffd872a2b5537149a5d3b9f3e9bb5d4.tar.gz |
ISO 8601 timestamps for tempurl
Client-side implementation for ISO 8601 timestamp
support of tempurl middleware. Please see
https://review.openstack.org/#/c/422679/
Change-Id: I76da28b48948475ec1bae5258e0b39a316553fb7
Diffstat (limited to 'swiftclient')
-rwxr-xr-x | swiftclient/shell.py | 68 | ||||
-rw-r--r-- | swiftclient/utils.py | 73 |
2 files changed, 107 insertions, 34 deletions
diff --git a/swiftclient/shell.py b/swiftclient/shell.py index 4a8a356..841ed6e 100755 --- a/swiftclient/shell.py +++ b/swiftclient/shell.py @@ -1224,8 +1224,8 @@ def st_auth(parser, args, thread_manager): print('export OS_AUTH_TOKEN=%s' % sh_quote(token)) -st_tempurl_options = '''[--absolute] [--prefix-based] - <method> <seconds> <path> <key>''' +st_tempurl_options = '''[--absolute] [--prefix-based] [--iso8601] + <method> <time> <path> <key>''' st_tempurl_help = ''' @@ -1234,9 +1234,35 @@ Generates a temporary URL for a Swift object. Positional arguments: <method> An HTTP method to allow for this temporary URL. Usually 'GET' or 'PUT'. - <seconds> The amount of time in seconds the temporary URL will be - valid for; or, if --absolute is passed, the Unix - timestamp when the temporary URL will expire. + <time> The amount of time the temporary URL will be + valid. The time can be specified in two ways: + an integer representing the time in seconds or an + ISO 8601 timestamp in a specific format. + If --absolute is passed and time + is an integer, the seconds are intepreted as the Unix + timestamp when the temporary URL will expire. The ISO + 8601 timestamp can be specified in one of following + formats: + + i) Complete date: YYYY-MM-DD (eg 1997-07-16) + + ii) Complete date plus hours, minutes and seconds: + + YYYY-MM-DDThh:mm:ss + + (eg 1997-07-16T19:20:30) + + iii) Complete date plus hours, minutes and seconds with + UTC designator: + + YYYY-MM-DDThh:mm:ssZ + + (eg 1997-07-16T19:20:30Z) + + Please be aware that if you don't provide the UTC + designator (i.e., Z) the timestamp is generated using + your local timezone. If only a date is specified, + the time part used will equal to 00:00:00. <path> The full path or storage URL to the Swift object. Example: /v1/AUTH_account/c/o or: http://saio:8080/v1/AUTH_account/c/o @@ -1245,10 +1271,14 @@ Positional arguments: "Temp-URL-Key:b3968d0207b54ece87cccc06515a89d4"\' Optional arguments: - --absolute Interpret the <seconds> positional argument as a Unix + --absolute Interpret the <time> positional argument as a Unix timestamp rather than a number of seconds in the - future. - --prefix-based If present, a prefix-based tempURL will be generated. + future. If an ISO 8601 timestamp is passed for <time>, + this argument is ignored. + --prefix-based If present, a prefix-based temporary URL will be + generated. + --iso8601 If present, the generated temporary URL will contain an + ISO 8601 UTC timestamp instead of a Unix timestamp. '''.strip('\n') @@ -1256,14 +1286,21 @@ def st_tempurl(parser, args, thread_manager): parser.add_argument( '--absolute', action='store_true', dest='absolute_expiry', default=False, - help=("If present, seconds argument will be interpreted as a Unix " - "timestamp representing when the tempURL should expire, rather " - "than an offset from the current time"), + help=("If present, and time argument is an integer, " + "time argument will be interpreted as a Unix " + "timestamp representing when the temporary URL should expire, " + "rather than an offset from the current time."), ) parser.add_argument( '--prefix-based', action='store_true', default=False, - help=("If present, a prefix-based tempURL will be generated."), + help=("If present, a prefix-based temporary URL will be generated."), + ) + parser.add_argument( + '--iso8601', action='store_true', + default=False, + help=("If present, the temporary URL will contain an ISO 8601 UTC " + "timestamp instead of a Unix timestamp."), ) (options, args) = parse_args(parser, args) @@ -1272,7 +1309,7 @@ def st_tempurl(parser, args, thread_manager): thread_manager.error('Usage: %s tempurl %s\n%s', BASENAME, st_tempurl_options, st_tempurl_help) return - method, seconds, path, key = args[:4] + method, timestamp, path, key = args[:4] parsed = urlparse(path) @@ -1281,9 +1318,10 @@ def st_tempurl(parser, args, thread_manager): 'tempurl specified, possibly an error' % method.upper()) try: - path = generate_temp_url(parsed.path, seconds, key, method, + path = generate_temp_url(parsed.path, timestamp, key, method, absolute=options['absolute_expiry'], - prefix=options['prefix_based'],) + iso8601=options['iso8601'], + prefix=options['prefix_based']) except ValueError as err: thread_manager.error(err) return diff --git a/swiftclient/utils.py b/swiftclient/utils.py index 08cd7b2..8afcde9 100644 --- a/swiftclient/utils.py +++ b/swiftclient/utils.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Miscellaneous utility functions for use with Swift.""" +from calendar import timegm import collections import gzip import hashlib @@ -25,6 +26,10 @@ import traceback TRUE_VALUES = set(('true', '1', 'yes', 'on', 't', 'y')) EMPTY_ETAG = 'd41d8cd98f00b204e9800998ecf8427e' +EXPIRES_ISO8601_FORMAT = '%Y-%m-%dT%H:%M:%SZ' +SHORT_EXPIRES_ISO8601_FORMAT = '%Y-%m-%d' +TIME_ERRMSG = ('time must either be a whole number or in specific ' + 'ISO 8601 format.') def config_true_value(value): @@ -64,39 +69,65 @@ def prt_bytes(num_bytes, human_flag): def generate_temp_url(path, seconds, key, method, absolute=False, - prefix=False): + prefix=False, iso8601=False): """Generates a temporary URL that gives unauthenticated access to the Swift object. :param path: The full path to the Swift object or prefix if a prefix-based temporary URL should be generated. Example: /v1/AUTH_account/c/o or /v1/AUTH_account/c/prefix. - :param seconds: If absolute is False then this specifies the amount of time - in seconds for which the temporary URL will be valid. If absolute is - True then this specifies an absolute time at which the temporary URL - will expire. + :param seconds: time in seconds or ISO 8601 timestamp. + If absolute is False and this is the string representation of an + integer, then this specifies the amount of time in seconds for which + the temporary URL will be valid. + If absolute is True then this specifies an absolute time at which the + temporary URL will expire. :param key: The secret temporary URL key set on the Swift cluster. To set a key, run 'swift post -m "Temp-URL-Key: <substitute tempurl key here>"' :param method: A HTTP method, typically either GET or PUT, to allow for this temporary URL. - :param absolute: if True then the seconds parameter is interpreted as an - absolute Unix time, otherwise seconds is interpreted as a relative time - offset from current time. + :param absolute: if True then the seconds parameter is interpreted as a + Unix timestamp, if seconds represents an integer. :param prefix: if True then a prefix-based temporary URL will be generated. - :raises ValueError: if seconds is not a whole number or path is not to - an object. + :param iso8601: if True, a URL containing an ISO 8601 UTC timestamp + instead of a UNIX timestamp will be created. + :raises ValueError: if timestamp or path is not in valid format. :return: the path portion of a temporary URL """ try: - seconds = float(seconds) - if not seconds.is_integer(): - raise ValueError() - seconds = int(seconds) - if seconds < 0: - raise ValueError() + try: + timestamp = float(seconds) + except ValueError: + formats = ( + EXPIRES_ISO8601_FORMAT, + EXPIRES_ISO8601_FORMAT[:-1], + SHORT_EXPIRES_ISO8601_FORMAT) + for f in formats: + try: + t = time.strptime(seconds, f) + except ValueError: + t = None + else: + if f == EXPIRES_ISO8601_FORMAT: + timestamp = timegm(t) + else: + # Use local time if UTC designator is missing. + timestamp = int(time.mktime(t)) + + absolute = True + break + + if t is None: + raise ValueError() + else: + if not timestamp.is_integer(): + raise ValueError() + timestamp = int(timestamp) + if timestamp < 0: + raise ValueError() except ValueError: - raise ValueError('seconds must be a whole number') + raise ValueError(TIME_ERRMSG) if isinstance(path, six.binary_type): try: @@ -121,9 +152,9 @@ def generate_temp_url(path, seconds, key, method, absolute=False, 'possibly an error', method.upper()) if not absolute: - expiration = int(time.time() + seconds) + expiration = int(time.time() + timestamp) else: - expiration = seconds + expiration = timestamp hmac_body = u'\n'.join([method.upper(), str(expiration), ('prefix:' if prefix else '') + path_for_body]) @@ -132,6 +163,10 @@ def generate_temp_url(path, seconds, key, method, absolute=False, key = key.encode('utf-8') sig = hmac.new(key, hmac_body.encode('utf-8'), hashlib.sha1).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( path=path_for_body, sig=sig, exp=expiration) if prefix: |