summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZack M. Davis <zdavis@swiftstack.com>2015-09-04 14:57:30 -0700
committerZack M. Davis <zdavis@swiftstack.com>2015-09-04 14:57:30 -0700
commit52d39bebc11979fa1be5090ff75466710638e561 (patch)
treef700b14751375eadd84f2688a73f1e930a24930d
parent93666bb84abc1edc36fbb3ef5ec194cf774e4826 (diff)
downloadpython-swiftclient-52d39bebc11979fa1be5090ff75466710638e561.tar.gz
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
-rw-r--r--doc/manpages/swift.112
-rwxr-xr-xswiftclient/shell.py20
-rw-r--r--swiftclient/utils.py9
-rw-r--r--tests/unit/test_shell.py16
-rw-r--r--tests/unit/test_utils.py15
5 files changed, 52 insertions, 20 deletions
diff --git a/doc/manpages/swift.1 b/doc/manpages/swift.1
index 446ade4..4cfc23f 100644
--- a/doc/manpages/swift.1
+++ b/doc/manpages/swift.1
@@ -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
diff --git a/swiftclient/shell.py b/swiftclient/shell.py
index d908d66..652980f 100755
--- a/swiftclient/shell.py
+++ b/swiftclient/shell.py
@@ -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)
diff --git a/swiftclient/utils.py b/swiftclient/utils.py
index 6ff6259..8316a8f 100644
--- a/swiftclient/utils.py
+++ b/swiftclient/utils.py
@@ -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')
diff --git a/tests/unit/test_shell.py b/tests/unit/test_shell.py
index 12ceadb..e2b87d0 100644
--- a/tests/unit/test_shell.py
+++ b/tests/unit/test_shell.py
@@ -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', 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')
+ '/v1/AUTH_account/c/o', 60, 'secret_key', 'GET', absolute=True)
@mock.patch('swiftclient.service.Connection')
def test_capabilities(self, connection):
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index ca3531e..7d7f6b6 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -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,