summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Burke <tim.burke@gmail.com>2020-10-15 14:05:28 -0700
committerTim Burke <tim.burke@gmail.com>2022-08-19 17:40:10 -0700
commitdefbb4a8f390c7de73ac6a90fc1ab5009e8105ee (patch)
tree53844949e13846163414cd6f9f21c312c01da6e3
parent7f2649bfb717f0f1cc077c04e206841581d6dab5 (diff)
downloadpython-swiftclient-defbb4a8f390c7de73ac6a90fc1ab5009e8105ee.tar.gz
Allow tempurl times to have units
Specifically, let users add a suffix for seconds, minutes, hours, or days. Change-Id: Ibbe7e5aa8aa8e54935da76109c2ea13fb83bc7ab
-rwxr-xr-xswiftclient/shell.py18
-rw-r--r--swiftclient/utils.py89
-rw-r--r--test/unit/test_shell.py8
-rw-r--r--test/unit/test_utils.py39
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',