absolute expiry option for tempURL generation

The `tempurl` subcommand's second positional argument is called
`seconds` and has heretofore interpreted as the number of seconds for
which the tempURL should be valid, counting from the moment of running
the command. This is indeed a common, if not the most common,
use-case. But some users, occasionally, might want to generate a tempURL
that expires at some particular ("absolute") time, rather than a
particular amount of time relative to the moment of happening to run the
command. (One might make an analogy to the way in which Swift's expiring
object support supports an `X-Delete-At` header in addition to
`X-Delete-After`—and it's the former that must be regarded as
ontologically prior.) Thus, this commit adds an `--absolute` optional
argument to the `tempurl` subcommand; if present, the `seconds` argument
will be interpreted as a Unix timestamp of when the tempURL should be
expire, rather than a duration for which the tempURL should be valid
starting from "now".

Change-Id: If9ded96f2799800958d5063127f3de812f50ef06
This commit is contained in:
Zack M. Davis 2015-09-04 14:57:30 -07:00
parent 93666bb84a
commit 52d39bebc1
5 changed files with 52 additions and 20 deletions

View File

@ -104,12 +104,14 @@ is not provided the storage-url retrieved after authentication is used as
proxy-url.
.RE
\fBtempurl\fR method seconds path key
\fBtempurl\fR \fImethod\fR \fIseconds\fR \fIpath\fR \fIkey\fR [\fI--absolute\fR]
.RS 4
Generates a temporary URL allowing unauthenticated access to the Swift object at
the given path, using the given HTTP method, for the given number of seconds,
using the given TempURL key. \fBExample\fR: tempurl GET 86400
/v1/AUTH_foo/bar_container/quux.md my_secret_tempurl_key
Generates a temporary URL allowing unauthenticated access to the Swift object
at the given path, using the given HTTP method, for the given number of
seconds, using the given TempURL key. If optional --absolute argument is
provided, seconds is instead interpreted as a Unix timestamp at which the URL
should expire. \fBExample\fR: tempurl GET $(date -d "Jan 1 2016" +%s)
/v1/AUTH_foo/bar_container/quux.md my_secret_tempurl_key --absolute
.RE
.SH OPTIONS

View File

@ -1013,17 +1013,30 @@ 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.
<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.
<path> The full path to the Swift object. Example:
/v1/AUTH_account/c/o.
<key> The secret temporary URL key set on the Swift cluster.
To set a key, run \'swift post -m
"Temp-URL-Key:b3968d0207b54ece87cccc06515a89d4"\'
Optional arguments:
--absolute Interpet the <seconds> positional argument as a Unix
timestamp rather than a number of seconds in the
future.
'''.strip('\n')
def st_tempurl(parser, args, thread_manager):
parser.add_option(
'--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")
)
(options, args) = parse_args(parser, args)
args = args[1:]
if len(args) < 4:
@ -1040,7 +1053,8 @@ def st_tempurl(parser, args, thread_manager):
thread_manager.print_msg('WARNING: Non default HTTP method %s for '
'tempurl specified, possibly an error' %
method.upper())
url = generate_temp_url(path, seconds, key, method)
url = generate_temp_url(path, seconds, key, method,
absolute=options.absolute_expiry)
thread_manager.print_msg(url)

View File

@ -65,8 +65,8 @@ def prt_bytes(bytes, human_flag):
return bytes
def generate_temp_url(path, seconds, key, method):
""" Generates a temporary URL that gives unauthenticated access to the
def generate_temp_url(path, seconds, key, method, absolute=False):
"""Generates a temporary URL that gives unauthenticated access to the
Swift object.
:param path: The full path to the Swift object. Example:
@ -85,7 +85,10 @@ def generate_temp_url(path, seconds, key, method):
if seconds < 0:
raise ValueError('seconds must be a positive integer')
try:
expiration = int(time.time() + seconds)
if not absolute:
expiration = int(time.time() + seconds)
else:
expiration = int(seconds)
except TypeError:
raise TypeError('seconds must be an integer')

View File

@ -922,15 +922,21 @@ class TestShell(unittest.TestCase):
self.assertTrue(output.err != '')
self.assertTrue(output.err.startswith('Usage'))
@mock.patch('swiftclient.shell.generate_temp_url')
@mock.patch('swiftclient.shell.generate_temp_url', return_value='')
def test_temp_url(self, temp_url):
argv = ["", "tempurl", "GET", "60", "/v1/AUTH_account/c/o",
"secret_key"
]
temp_url.return_value = ""
"secret_key"]
swiftclient.shell.main(argv)
temp_url.assert_called_with(
'/v1/AUTH_account/c/o', 60, 'secret_key', 'GET')
'/v1/AUTH_account/c/o', 60, 'secret_key', 'GET', absolute=False)
@mock.patch('swiftclient.shell.generate_temp_url', return_value='')
def test_absolute_expiry_temp_url(self, temp_url):
argv = ["", "tempurl", "GET", "60", "/v1/AUTH_account/c/o",
"secret_key", "--absolute"]
swiftclient.shell.main(argv)
temp_url.assert_called_with(
'/v1/AUTH_account/c/o', 60, 'secret_key', 'GET', absolute=True)
@mock.patch('swiftclient.service.Connection')
def test_capabilities(self, connection):

View File

@ -132,11 +132,9 @@ class TestTempURL(testtools.TestCase):
self.key = 'correcthorsebatterystaple'
self.method = 'GET'
@mock.patch('hmac.HMAC.hexdigest')
@mock.patch('time.time')
@mock.patch('hmac.HMAC.hexdigest', return_value='temp_url_signature')
@mock.patch('time.time', return_value=1400000000)
def test_generate_temp_url(self, time_mock, hmac_mock):
time_mock.return_value = 1400000000
hmac_mock.return_value = 'temp_url_signature'
expected_url = (
'/v1/AUTH_account/c/o?'
'temp_url_sig=temp_url_signature&'
@ -145,6 +143,15 @@ class TestTempURL(testtools.TestCase):
self.method)
self.assertEqual(url, expected_url)
@mock.patch('hmac.HMAC.hexdigest', return_value="temp_url_signature")
def test_generate_absolute_expiry_temp_url(self, hmac_mock):
expected_url = ('/v1/AUTH_account/c/o?'
'temp_url_sig=temp_url_signature&'
'temp_url_expires=2146636800')
url = u.generate_temp_url(self.url, 2146636800, self.key, self.method,
absolute=True)
self.assertEqual(url, expected_url)
def test_generate_temp_url_bad_seconds(self):
self.assertRaises(TypeError,
u.generate_temp_url,