diff options
-rwxr-xr-x | swiftclient/shell.py | 18 | ||||
-rw-r--r-- | swiftclient/utils.py | 89 | ||||
-rw-r--r-- | test/unit/test_shell.py | 8 | ||||
-rw-r--r-- | test/unit/test_utils.py | 39 |
4 files changed, 112 insertions, 42 deletions
diff --git a/swiftclient/shell.py b/swiftclient/shell.py index 5bcff7f..ed39b81 100755 --- a/swiftclient/shell.py +++ b/swiftclient/shell.py @@ -1381,14 +1381,16 @@ Positional arguments: <method> An HTTP method to allow for this temporary URL. Usually 'GET' or 'PUT'. <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: + valid. The time can be specified in three ways: + an integer representing the time in seconds; + a number with a 's', 'm', 'h', or 'd' suffix to specify + the time in seconds, minutes, hours, or days; 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) diff --git a/swiftclient/utils.py b/swiftclient/utils.py index c865d27..1b80e6d 100644 --- a/swiftclient/utils.py +++ b/swiftclient/utils.py @@ -70,40 +70,7 @@ def prt_bytes(num_bytes, human_flag): return '%.1f%s' % (num, suffix) -def generate_temp_url(path, seconds, key, method, absolute=False, - prefix=False, iso8601=False, ip_range=None, - digest='sha256'): - """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: 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 a - Unix timestamp, if seconds represents an integer. - :param prefix: if True then a prefix-based temporary URL will be generated. - :param iso8601: if True, a URL containing an ISO 8601 UTC timestamp - 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. - :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 - """ +def parse_timestamp(seconds, absolute=False): try: try: timestamp = float(seconds) @@ -127,6 +94,20 @@ def generate_temp_url(path, seconds, key, method, absolute=False, absolute = True break + if t is None and not absolute: + for suffix, multiplier in ( + ('s', 1), + ('m', 60), + ('min', 60), + ('h', 60 * 60), + ('hr', 60 * 60), + ('d', 24 * 60 * 60), + ): + if seconds.endswith(suffix): + timestamp = t = int( + multiplier * float(seconds[:-len(suffix)])) + break + if t is None: raise ValueError() else: @@ -137,6 +118,46 @@ def generate_temp_url(path, seconds, key, method, absolute=False, raise ValueError() except ValueError: raise ValueError(TIME_ERRMSG) + return timestamp, absolute + + +def generate_temp_url(path, seconds, key, method, absolute=False, + prefix=False, iso8601=False, ip_range=None, + digest='sha256'): + """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: 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. This may include a suffix to scale + the value: 's' for seconds, 'm' (or 'min') for minutes, + 'h' (or 'hr') for hours, or 'd' for days. + 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 a + Unix timestamp, if seconds represents an integer. + :param prefix: if True then a prefix-based temporary URL will be generated. + :param iso8601: if True, a URL containing an ISO 8601 UTC timestamp + 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. + :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 + """ + timestamp, absolute = parse_timestamp(seconds, absolute) if isinstance(path, bytes): try: diff --git a/test/unit/test_shell.py b/test/unit/test_shell.py index 8254388..80c031e 100644 --- a/test/unit/test_shell.py +++ b/test/unit/test_shell.py @@ -2037,6 +2037,14 @@ class TestShell(unittest.TestCase): '/v1/AUTH_account/c/o', "60", 'secret_key', 'GET', absolute=False, iso8601=False, prefix=False, ip_range=None, digest='sha256') + # sanity check that suffixes will just pass through to utils.py + argv = ["", "tempurl", "GET", "2d", "/v1/AUTH_account/c/o", + "secret_key"] + swiftclient.shell.main(argv) + temp_url.assert_called_with( + '/v1/AUTH_account/c/o', "2d", 'secret_key', 'GET', absolute=False, + iso8601=False, prefix=False, ip_range=None, digest='sha256') + @mock.patch('swiftclient.shell.generate_temp_url', return_value='') def test_temp_url_prefix_based(self, temp_url): argv = ["", "tempurl", "GET", "60", "/v1/AUTH_account/c/", diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index 129208d..6d2f4a1 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -122,6 +122,43 @@ class TestPrtBytes(unittest.TestCase): self.assertEqual('1024Y', u.prt_bytes(bytes_, True).lstrip()) +class TestParseTimestamp(unittest.TestCase): + def test_int(self): + self.assertEqual((1234, False), u.parse_timestamp(1234, False)) + self.assertEqual((3600, False), u.parse_timestamp('3600', False)) + + def test_suffixed(self): + self.assertEqual((54, False), u.parse_timestamp('54.321s', False)) + self.assertEqual((int(54.321 * 60), False), + u.parse_timestamp('54.321m', False)) + self.assertEqual((900, False), + u.parse_timestamp('15min', False)) + self.assertEqual((int(54.321 * 60 * 60), False), + u.parse_timestamp('54.321h', False)) + self.assertEqual((7200, False), + u.parse_timestamp('2hr', False)) + self.assertEqual((60 * 60 * 24, False), u.parse_timestamp('1d', False)) + + def test_str(self): + self.assertEqual((1615852800, True), + u.parse_timestamp('2021-03-16T00:00:00Z', False)) + + def test_absolute(self): + self.assertEqual((1234, True), u.parse_timestamp(1234, True)) + self.assertEqual((1615852800, True), + u.parse_timestamp('2021-03-16T00:00:00Z', True)) + + def test_error(self): + with self.assertRaises(ValueError): + u.parse_timestamp('asdf', False) + with self.assertRaises(ValueError): + u.parse_timestamp(12.34, False) + with self.assertRaises(ValueError): + u.parse_timestamp('54.321', True) + with self.assertRaises(ValueError): + u.parse_timestamp(-1, False) + + class TestTempURL(unittest.TestCase): url = '/v1/AUTH_account/c/o' seconds = 3600 @@ -422,6 +459,7 @@ class TestTempURL(unittest.TestCase): class TestTempURLUnicodePathAndKey(TestTempURL): url = '/v1/\u00e4/c/\u00f3' key = 'k\u00e9y' + seconds = '1hr' expected_body = '\n'.join([ 'GET', '1400003600', @@ -432,6 +470,7 @@ class TestTempURLUnicodePathAndKey(TestTempURL): class TestTempURLUnicodePathBytesKey(TestTempURL): url = '/v1/\u00e4/c/\u00f3' key = 'k\u00e9y'.encode('utf-8') + seconds = '60m' expected_body = '\n'.join([ 'GET', '1400003600', |