Allow tempurl times to have units

Specifically, let users add a suffix for seconds, minutes, hours, or
days.

Change-Id: Ibbe7e5aa8aa8e54935da76109c2ea13fb83bc7ab
This commit is contained in:
Tim Burke 2020-10-15 14:05:28 -07:00
parent 7f2649bfb7
commit defbb4a8f3
4 changed files with 112 additions and 42 deletions

View File

@ -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)

View File

@ -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:

View File

@ -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/",

View File

@ -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',