Merge "tempurls with a prefix-based scope"
This commit is contained in:
commit
54d1e1bae8
@ -14,6 +14,10 @@ the object. When the web browser user clicks on the link, the browser
|
|||||||
downloads the object directly from Object Storage, eliminating the need
|
downloads the object directly from Object Storage, eliminating the need
|
||||||
for the website to act as a proxy for the request.
|
for the website to act as a proxy for the request.
|
||||||
|
|
||||||
|
Furthermore, a temporary URL can be prefix-based. These URLs
|
||||||
|
contain a signature which is is valid for all objects which share
|
||||||
|
a common prefix. They are useful for sharing a set of objects.
|
||||||
|
|
||||||
Ask your cloud administrator to enable the temporary URL feature. For
|
Ask your cloud administrator to enable the temporary URL feature. For
|
||||||
information, see :ref:`tempurl` in the *Source Documentation*.
|
information, see :ref:`tempurl` in the *Source Documentation*.
|
||||||
|
|
||||||
@ -60,6 +64,17 @@ object name. Object Storage returns this value in the ``Content-Disposition``
|
|||||||
response header. Browsers can interpret this file name value as a file
|
response header. Browsers can interpret this file name value as a file
|
||||||
attachment to be saved.
|
attachment to be saved.
|
||||||
|
|
||||||
|
A prefix-based temporary URL is similar but requires the parameter
|
||||||
|
``temp_url_prefix``, which must be equal to the common prefix shared
|
||||||
|
by all object names for which the URL is valid.
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
https://swift-cluster.example.com/v1/my_account/container/my_prefix/object
|
||||||
|
?temp_url_sig=da39a3ee5e6b4b0d3255bfef95601890afd80709
|
||||||
|
&temp_url_expires=1323479485
|
||||||
|
&temp_url_prefix=my_prefix
|
||||||
|
|
||||||
.. _secret_keys:
|
.. _secret_keys:
|
||||||
|
|
||||||
Secret Keys
|
Secret Keys
|
||||||
@ -114,17 +129,16 @@ signature includes these elements:
|
|||||||
into the future.
|
into the future.
|
||||||
|
|
||||||
- The path. Starting with ``/v1/`` onwards and including a container
|
- The path. Starting with ``/v1/`` onwards and including a container
|
||||||
name and object. In the example below, the path is
|
name and object. The path for prefix-based signatures must start with
|
||||||
``/v1/my_account/container/object``. Do not URL-encode the path at
|
``prefix:/v1/``. Do not URL-encode the path at this stage.
|
||||||
this stage.
|
|
||||||
|
|
||||||
- The secret key. Use one of the key values as described
|
- The secret key. Use one of the key values as described
|
||||||
in :ref:`secret_keys`.
|
in :ref:`secret_keys`.
|
||||||
|
|
||||||
This sample Python code shows how to compute a signature for use with
|
These sample Python codes show how to compute a signature for use with
|
||||||
temporary URLs:
|
temporary URLs:
|
||||||
|
|
||||||
**Example HMAC-SHA1 signature for temporary URLs**
|
**Example HMAC-SHA1 signature for object-based temporary URLs**
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
@ -139,6 +153,21 @@ temporary URLs:
|
|||||||
hmac_body = '%s\n%s\n%s' % (method, expires, path)
|
hmac_body = '%s\n%s\n%s' % (method, expires, path)
|
||||||
signature = hmac.new(key, hmac_body, sha1).hexdigest()
|
signature = hmac.new(key, hmac_body, sha1).hexdigest()
|
||||||
|
|
||||||
|
**Example HMAC-SHA1 signature for prefix-based temporary URLs**
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
import hmac
|
||||||
|
from hashlib import sha1
|
||||||
|
from time import time
|
||||||
|
method = 'GET'
|
||||||
|
duration_in_seconds = 60*60*24
|
||||||
|
expires = int(time() + duration_in_seconds)
|
||||||
|
path = 'prefix:/v1/my_account/container/my_prefix'
|
||||||
|
key = 'MYKEY'
|
||||||
|
hmac_body = '%s\n%s\n%s' % (method, expires, path)
|
||||||
|
signature = hmac.new(key, hmac_body, sha1).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
Do not URL-encode the path when you generate the HMAC-SHA1 signature.
|
Do not URL-encode the path when you generate the HMAC-SHA1 signature.
|
||||||
However, when you make the actual HTTP request, you should properly
|
However, when you make the actual HTTP request, you should properly
|
||||||
|
@ -44,11 +44,16 @@ If the user were to share the link with all his friends, or
|
|||||||
accidentally post it on a forum, etc. the direct access would be
|
accidentally post it on a forum, etc. the direct access would be
|
||||||
limited to the expiration time set when the website created the link.
|
limited to the expiration time set when the website created the link.
|
||||||
|
|
||||||
|
Beyond that, the middleware provides the ability to create URLs, which
|
||||||
|
contain signatures which are valid for all objects which share a
|
||||||
|
common prefix. These prefix-based URLs are useful for sharing a set
|
||||||
|
of objects.
|
||||||
|
|
||||||
------------
|
------------
|
||||||
Client Usage
|
Client Usage
|
||||||
------------
|
------------
|
||||||
|
|
||||||
To create such temporary URLs, first an ``X-Account-Meta-Temp-URL-Key``
|
To create temporary URLs, first an ``X-Account-Meta-Temp-URL-Key``
|
||||||
header must be set on the Swift account. Then, an HMAC-SHA1 (RFC 2104)
|
header must be set on the Swift account. Then, an HMAC-SHA1 (RFC 2104)
|
||||||
signature is generated using the HTTP method to allow (``GET``, ``PUT``,
|
signature is generated using the HTTP method to allow (``GET``, ``PUT``,
|
||||||
``DELETE``, etc.), the Unix timestamp the access should be allowed until,
|
``DELETE``, etc.), the Unix timestamp the access should be allowed until,
|
||||||
@ -77,10 +82,32 @@ Let's say ``sig`` ends up equaling
|
|||||||
temp_url_sig=da39a3ee5e6b4b0d3255bfef95601890afd80709&
|
temp_url_sig=da39a3ee5e6b4b0d3255bfef95601890afd80709&
|
||||||
temp_url_expires=1323479485
|
temp_url_expires=1323479485
|
||||||
|
|
||||||
Any alteration of the resource path or query arguments would result in
|
If a prefix-based signature with the prefix ``pre`` is desired, set path to::
|
||||||
``401 Unauthorized``. Similarly, a ``PUT`` where ``GET`` was the allowed method
|
|
||||||
would be rejected with ``401 Unauthorized``. However, ``HEAD`` is allowed if
|
path = 'prefix:/v1/AUTH_account/container/pre'
|
||||||
``GET``, ``PUT``, or ``POST`` is allowed.
|
|
||||||
|
The generated signature would be valid for all objects starting
|
||||||
|
with ``pre``. The middleware detects a prefix-based temporary URL by
|
||||||
|
a query parameter called ``temp_url_prefix``. So, if ``sig`` and ``expires``
|
||||||
|
would end up like above, following URL would be valid::
|
||||||
|
|
||||||
|
https://swift-cluster.example.com/v1/AUTH_account/container/pre/object?
|
||||||
|
temp_url_sig=da39a3ee5e6b4b0d3255bfef95601890afd80709&
|
||||||
|
temp_url_expires=1323479485&
|
||||||
|
temp_url_prefix=pre
|
||||||
|
|
||||||
|
Another valid URL::
|
||||||
|
|
||||||
|
https://swift-cluster.example.com/v1/AUTH_account/container/pre/
|
||||||
|
subfolder/another_object?
|
||||||
|
temp_url_sig=da39a3ee5e6b4b0d3255bfef95601890afd80709&
|
||||||
|
temp_url_expires=1323479485&
|
||||||
|
temp_url_prefix=pre
|
||||||
|
|
||||||
|
Any alteration of the resource path or query arguments of a temporary URL
|
||||||
|
would result in ``401 Unauthorized``. Similarly, a ``PUT`` where ``GET`` was
|
||||||
|
the allowed method would be rejected with ``401 Unauthorized``.
|
||||||
|
However, ``HEAD`` is allowed if ``GET``, ``PUT``, or ``POST`` is allowed.
|
||||||
|
|
||||||
Using this in combination with browser form post translation
|
Using this in combination with browser form post translation
|
||||||
middleware could also allow direct-from-browser uploads to specific
|
middleware could also allow direct-from-browser uploads to specific
|
||||||
@ -357,28 +384,36 @@ class TempURL(object):
|
|||||||
if env['REQUEST_METHOD'] == 'OPTIONS':
|
if env['REQUEST_METHOD'] == 'OPTIONS':
|
||||||
return self.app(env, start_response)
|
return self.app(env, start_response)
|
||||||
info = self._get_temp_url_info(env)
|
info = self._get_temp_url_info(env)
|
||||||
temp_url_sig, temp_url_expires, filename, inline_disposition = info
|
temp_url_sig, temp_url_expires, temp_url_prefix, filename,\
|
||||||
|
inline_disposition = info
|
||||||
if temp_url_sig is None and temp_url_expires is None:
|
if temp_url_sig is None and temp_url_expires is None:
|
||||||
return self.app(env, start_response)
|
return self.app(env, start_response)
|
||||||
if not temp_url_sig or not temp_url_expires:
|
if not temp_url_sig or not temp_url_expires:
|
||||||
return self._invalid(env, start_response)
|
return self._invalid(env, start_response)
|
||||||
account, container = self._get_account_and_container(env)
|
account, container, obj = self._get_path_parts(env)
|
||||||
if not account:
|
if not account:
|
||||||
return self._invalid(env, start_response)
|
return self._invalid(env, start_response)
|
||||||
keys = self._get_keys(env)
|
keys = self._get_keys(env)
|
||||||
if not keys:
|
if not keys:
|
||||||
return self._invalid(env, start_response)
|
return self._invalid(env, start_response)
|
||||||
|
if temp_url_prefix is None:
|
||||||
|
path = '/v1/%s/%s/%s' % (account, container, obj)
|
||||||
|
else:
|
||||||
|
if not obj.startswith(temp_url_prefix):
|
||||||
|
return self._invalid(env, start_response)
|
||||||
|
path = 'prefix:/v1/%s/%s/%s' % (account, container,
|
||||||
|
temp_url_prefix)
|
||||||
if env['REQUEST_METHOD'] == 'HEAD':
|
if env['REQUEST_METHOD'] == 'HEAD':
|
||||||
hmac_vals = (
|
hmac_vals = (
|
||||||
self._get_hmacs(env, temp_url_expires, keys) +
|
self._get_hmacs(env, temp_url_expires, path, keys) +
|
||||||
self._get_hmacs(env, temp_url_expires, keys,
|
self._get_hmacs(env, temp_url_expires, path, keys,
|
||||||
request_method='GET') +
|
request_method='GET') +
|
||||||
self._get_hmacs(env, temp_url_expires, keys,
|
self._get_hmacs(env, temp_url_expires, path, keys,
|
||||||
request_method='POST') +
|
request_method='POST') +
|
||||||
self._get_hmacs(env, temp_url_expires, keys,
|
self._get_hmacs(env, temp_url_expires, path, keys,
|
||||||
request_method='PUT'))
|
request_method='PUT'))
|
||||||
else:
|
else:
|
||||||
hmac_vals = self._get_hmacs(env, temp_url_expires, keys)
|
hmac_vals = self._get_hmacs(env, temp_url_expires, path, keys)
|
||||||
|
|
||||||
is_valid_hmac = False
|
is_valid_hmac = False
|
||||||
hmac_scope = None
|
hmac_scope = None
|
||||||
@ -410,6 +445,8 @@ class TempURL(object):
|
|||||||
env['REMOTE_USER'] = '.wsgi.tempurl'
|
env['REMOTE_USER'] = '.wsgi.tempurl'
|
||||||
qs = {'temp_url_sig': temp_url_sig,
|
qs = {'temp_url_sig': temp_url_sig,
|
||||||
'temp_url_expires': temp_url_expires}
|
'temp_url_expires': temp_url_expires}
|
||||||
|
if temp_url_prefix is not None:
|
||||||
|
qs['temp_url_prefix'] = temp_url_prefix
|
||||||
if filename:
|
if filename:
|
||||||
qs['filename'] = filename
|
qs['filename'] = filename
|
||||||
env['QUERY_STRING'] = urlencode(qs)
|
env['QUERY_STRING'] = urlencode(qs)
|
||||||
@ -457,36 +494,38 @@ class TempURL(object):
|
|||||||
|
|
||||||
return self.app(env, _start_response)
|
return self.app(env, _start_response)
|
||||||
|
|
||||||
def _get_account_and_container(self, env):
|
def _get_path_parts(self, env):
|
||||||
"""
|
"""
|
||||||
Returns just the account and container for the request, if it's an
|
Return the account, container and object name for the request,
|
||||||
object request and one of the configured methods; otherwise, None is
|
if it's an object request and one of the configured methods;
|
||||||
returned.
|
otherwise, None is returned.
|
||||||
|
|
||||||
:param env: The WSGI environment for the request.
|
:param env: The WSGI environment for the request.
|
||||||
:returns: (Account str, container str) or (None, None).
|
:returns: (Account str, container str, object str) or
|
||||||
|
(None, None, None).
|
||||||
"""
|
"""
|
||||||
if env['REQUEST_METHOD'] in self.conf['methods']:
|
if env['REQUEST_METHOD'] in self.conf['methods']:
|
||||||
try:
|
try:
|
||||||
ver, acc, cont, obj = split_path(env['PATH_INFO'], 4, 4, True)
|
ver, acc, cont, obj = split_path(env['PATH_INFO'], 4, 4, True)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return (None, None)
|
return (None, None, None)
|
||||||
if ver == 'v1' and obj.strip('/'):
|
if ver == 'v1' and obj.strip('/'):
|
||||||
return (acc, cont)
|
return (acc, cont, obj)
|
||||||
return (None, None)
|
return (None, None, None)
|
||||||
|
|
||||||
def _get_temp_url_info(self, env):
|
def _get_temp_url_info(self, env):
|
||||||
"""
|
"""
|
||||||
Returns the provided temporary URL parameters (sig, expires),
|
Returns the provided temporary URL parameters (sig, expires, prefix),
|
||||||
if given and syntactically valid. Either sig or expires could
|
if given and syntactically valid. Either sig, expires or prefix could
|
||||||
be None if not provided. If provided, expires is also
|
be None if not provided. If provided, expires is also
|
||||||
converted to an int if possible or 0 if not, and checked for
|
converted to an int if possible or 0 if not, and checked for
|
||||||
expiration (returns 0 if expired).
|
expiration (returns 0 if expired).
|
||||||
|
|
||||||
:param env: The WSGI environment for the request.
|
:param env: The WSGI environment for the request.
|
||||||
:returns: (sig, expires, filename, inline) as described above.
|
:returns: (sig, expires, prefix, filename, inline) as described above.
|
||||||
"""
|
"""
|
||||||
temp_url_sig = temp_url_expires = filename = inline = None
|
temp_url_sig = temp_url_expires = temp_url_prefix = filename =\
|
||||||
|
inline = None
|
||||||
qs = parse_qs(env.get('QUERY_STRING', ''), keep_blank_values=True)
|
qs = parse_qs(env.get('QUERY_STRING', ''), keep_blank_values=True)
|
||||||
if 'temp_url_sig' in qs:
|
if 'temp_url_sig' in qs:
|
||||||
temp_url_sig = qs['temp_url_sig'][0]
|
temp_url_sig = qs['temp_url_sig'][0]
|
||||||
@ -497,11 +536,14 @@ class TempURL(object):
|
|||||||
temp_url_expires = 0
|
temp_url_expires = 0
|
||||||
if temp_url_expires < time():
|
if temp_url_expires < time():
|
||||||
temp_url_expires = 0
|
temp_url_expires = 0
|
||||||
|
if 'temp_url_prefix' in qs:
|
||||||
|
temp_url_prefix = qs['temp_url_prefix'][0]
|
||||||
if 'filename' in qs:
|
if 'filename' in qs:
|
||||||
filename = qs['filename'][0]
|
filename = qs['filename'][0]
|
||||||
if 'inline' in qs:
|
if 'inline' in qs:
|
||||||
inline = True
|
inline = True
|
||||||
return temp_url_sig, temp_url_expires, filename, inline
|
return (temp_url_sig, temp_url_expires, temp_url_prefix, filename,
|
||||||
|
inline)
|
||||||
|
|
||||||
def _get_keys(self, env):
|
def _get_keys(self, env):
|
||||||
"""
|
"""
|
||||||
@ -531,11 +573,13 @@ class TempURL(object):
|
|||||||
return ([(ak, ACCOUNT_SCOPE) for ak in account_keys] +
|
return ([(ak, ACCOUNT_SCOPE) for ak in account_keys] +
|
||||||
[(ck, CONTAINER_SCOPE) for ck in container_keys])
|
[(ck, CONTAINER_SCOPE) for ck in container_keys])
|
||||||
|
|
||||||
def _get_hmacs(self, env, expires, scoped_keys, request_method=None):
|
def _get_hmacs(self, env, expires, path, scoped_keys,
|
||||||
|
request_method=None):
|
||||||
"""
|
"""
|
||||||
:param env: The WSGI environment for the request.
|
:param env: The WSGI environment for the request.
|
||||||
:param expires: Unix timestamp as an int for when the URL
|
:param expires: Unix timestamp as an int for when the URL
|
||||||
expires.
|
expires.
|
||||||
|
:param path: The path which is used for hashing.
|
||||||
:param scoped_keys: (key, scope) tuples like _get_keys() returns
|
:param scoped_keys: (key, scope) tuples like _get_keys() returns
|
||||||
:param request_method: Optional override of the request in
|
:param request_method: Optional override of the request in
|
||||||
the WSGI env. For example, if a HEAD
|
the WSGI env. For example, if a HEAD
|
||||||
@ -547,8 +591,9 @@ class TempURL(object):
|
|||||||
"""
|
"""
|
||||||
if not request_method:
|
if not request_method:
|
||||||
request_method = env['REQUEST_METHOD']
|
request_method = env['REQUEST_METHOD']
|
||||||
|
|
||||||
return [
|
return [
|
||||||
(get_hmac(request_method, env['PATH_INFO'], expires, key), scope)
|
(get_hmac(request_method, path, expires, key), scope)
|
||||||
for (key, scope) in scoped_keys]
|
for (key, scope) in scoped_keys]
|
||||||
|
|
||||||
def _invalid(self, env, start_response):
|
def _invalid(self, env, start_response):
|
||||||
|
@ -87,17 +87,16 @@ class TestTempurl(Base):
|
|||||||
(self.env.tempurl_enabled,))
|
(self.env.tempurl_enabled,))
|
||||||
|
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
self.obj_tempurl_parms = self.tempurl_parms(
|
||||||
'GET', expires, self.env.conn.make_path(self.env.obj.path),
|
'GET', expires, self.env.conn.make_path(self.env.obj.path),
|
||||||
self.env.tempurl_key)
|
self.env.tempurl_key)
|
||||||
self.obj_tempurl_parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
|
|
||||||
def tempurl_sig(self, method, expires, path, key):
|
def tempurl_parms(self, method, expires, path, key):
|
||||||
return hmac.new(
|
sig = hmac.new(
|
||||||
key,
|
key,
|
||||||
'%s\n%s\n%s' % (method, expires, urllib.parse.unquote(path)),
|
'%s\n%s\n%s' % (method, expires, urllib.parse.unquote(path)),
|
||||||
hashlib.sha1).hexdigest()
|
hashlib.sha1).hexdigest()
|
||||||
|
return {'temp_url_sig': sig, 'temp_url_expires': str(expires)}
|
||||||
|
|
||||||
def test_GET(self):
|
def test_GET(self):
|
||||||
contents = self.env.obj.read(
|
contents = self.env.obj.read(
|
||||||
@ -111,11 +110,9 @@ class TestTempurl(Base):
|
|||||||
|
|
||||||
def test_GET_with_key_2(self):
|
def test_GET_with_key_2(self):
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
parms = self.tempurl_parms(
|
||||||
'GET', expires, self.env.conn.make_path(self.env.obj.path),
|
'GET', expires, self.env.conn.make_path(self.env.obj.path),
|
||||||
self.env.tempurl_key2)
|
self.env.tempurl_key2)
|
||||||
parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
|
|
||||||
contents = self.env.obj.read(parms=parms, cfg={'no_auth_token': True})
|
contents = self.env.obj.read(parms=parms, cfg={'no_auth_token': True})
|
||||||
self.assertEqual(contents, "obj contents")
|
self.assertEqual(contents, "obj contents")
|
||||||
@ -135,11 +132,9 @@ class TestTempurl(Base):
|
|||||||
(self.env.container.name,)})
|
(self.env.container.name,)})
|
||||||
|
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
parms = self.tempurl_parms(
|
||||||
'GET', expires, self.env.conn.make_path(manifest.path),
|
'GET', expires, self.env.conn.make_path(manifest.path),
|
||||||
self.env.tempurl_key)
|
self.env.tempurl_key)
|
||||||
parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
|
|
||||||
contents = manifest.read(parms=parms, cfg={'no_auth_token': True})
|
contents = manifest.read(parms=parms, cfg={'no_auth_token': True})
|
||||||
self.assertEqual(contents, "one fish two fish red fish blue fish")
|
self.assertEqual(contents, "one fish two fish red fish blue fish")
|
||||||
@ -162,11 +157,9 @@ class TestTempurl(Base):
|
|||||||
(self.env.container.name,)})
|
(self.env.container.name,)})
|
||||||
|
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
parms = self.tempurl_parms(
|
||||||
'GET', expires, self.env.conn.make_path(manifest.path),
|
'GET', expires, self.env.conn.make_path(manifest.path),
|
||||||
self.env.tempurl_key)
|
self.env.tempurl_key)
|
||||||
parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
|
|
||||||
# cross container tempurl works fine for account tempurl key
|
# cross container tempurl works fine for account tempurl key
|
||||||
contents = manifest.read(parms=parms, cfg={'no_auth_token': True})
|
contents = manifest.read(parms=parms, cfg={'no_auth_token': True})
|
||||||
@ -177,11 +170,9 @@ class TestTempurl(Base):
|
|||||||
new_obj = self.env.container.file(Utils.create_name())
|
new_obj = self.env.container.file(Utils.create_name())
|
||||||
|
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
put_parms = self.tempurl_parms(
|
||||||
'PUT', expires, self.env.conn.make_path(new_obj.path),
|
'PUT', expires, self.env.conn.make_path(new_obj.path),
|
||||||
self.env.tempurl_key)
|
self.env.tempurl_key)
|
||||||
put_parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
|
|
||||||
new_obj.write('new obj contents',
|
new_obj.write('new obj contents',
|
||||||
parms=put_parms, cfg={'no_auth_token': True})
|
parms=put_parms, cfg={'no_auth_token': True})
|
||||||
@ -196,11 +187,9 @@ class TestTempurl(Base):
|
|||||||
|
|
||||||
# give out a signature which allows a PUT to new_obj
|
# give out a signature which allows a PUT to new_obj
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
put_parms = self.tempurl_parms(
|
||||||
'PUT', expires, self.env.conn.make_path(new_obj.path),
|
'PUT', expires, self.env.conn.make_path(new_obj.path),
|
||||||
self.env.tempurl_key)
|
self.env.tempurl_key)
|
||||||
put_parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
|
|
||||||
# try to create manifest pointing to some random container
|
# try to create manifest pointing to some random container
|
||||||
try:
|
try:
|
||||||
@ -230,11 +219,9 @@ class TestTempurl(Base):
|
|||||||
# try again using a tempurl POST to an already created object
|
# try again using a tempurl POST to an already created object
|
||||||
new_obj.write('', {}, parms=put_parms, cfg={'no_auth_token': True})
|
new_obj.write('', {}, parms=put_parms, cfg={'no_auth_token': True})
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
post_parms = self.tempurl_parms(
|
||||||
'POST', expires, self.env.conn.make_path(new_obj.path),
|
'POST', expires, self.env.conn.make_path(new_obj.path),
|
||||||
self.env.tempurl_key)
|
self.env.tempurl_key)
|
||||||
post_parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
try:
|
try:
|
||||||
new_obj.post({'x-object-manifest': '%s/foo' % other_container},
|
new_obj.post({'x-object-manifest': '%s/foo' % other_container},
|
||||||
parms=post_parms, cfg={'no_auth_token': True})
|
parms=post_parms, cfg={'no_auth_token': True})
|
||||||
@ -245,11 +232,9 @@ class TestTempurl(Base):
|
|||||||
|
|
||||||
def test_HEAD(self):
|
def test_HEAD(self):
|
||||||
expires = int(time.time()) + 86400
|
expires = int(time.time()) + 86400
|
||||||
sig = self.tempurl_sig(
|
head_parms = self.tempurl_parms(
|
||||||
'HEAD', expires, self.env.conn.make_path(self.env.obj.path),
|
'HEAD', expires, self.env.conn.make_path(self.env.obj.path),
|
||||||
self.env.tempurl_key)
|
self.env.tempurl_key)
|
||||||
head_parms = {'temp_url_sig': sig,
|
|
||||||
'temp_url_expires': str(expires)}
|
|
||||||
|
|
||||||
self.assertTrue(self.env.obj.info(parms=head_parms,
|
self.assertTrue(self.env.obj.info(parms=head_parms,
|
||||||
cfg={'no_auth_token': True}))
|
cfg={'no_auth_token': True}))
|
||||||
@ -312,6 +297,84 @@ class TestTempurl(Base):
|
|||||||
self.assert_status([401])
|
self.assert_status([401])
|
||||||
|
|
||||||
|
|
||||||
|
class TestTempURLPrefix(TestTempurl):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestTempurl, self).setUp()
|
||||||
|
if self.env.tempurl_enabled is False:
|
||||||
|
raise SkipTest("TempURL not enabled")
|
||||||
|
elif self.env.tempurl_enabled is not True:
|
||||||
|
# just some sanity checking
|
||||||
|
raise Exception(
|
||||||
|
"Expected tempurl_enabled to be True/False, got %r" %
|
||||||
|
(self.env.tempurl_enabled,))
|
||||||
|
|
||||||
|
self.expires = int(time.time()) + 86400
|
||||||
|
self.obj_tempurl_parms = self.tempurl_parms(
|
||||||
|
'GET', self.expires,
|
||||||
|
self.env.conn.make_path(self.env.obj.path),
|
||||||
|
self.env.tempurl_key)
|
||||||
|
|
||||||
|
def tempurl_parms(self, method, expires, path, key,
|
||||||
|
prefix=None):
|
||||||
|
path_parts = urllib.parse.unquote(path).split('/')
|
||||||
|
|
||||||
|
if prefix is None:
|
||||||
|
# Choose the first 4 chars of object name as prefix.
|
||||||
|
prefix = path_parts[4][0:4]
|
||||||
|
sig = hmac.new(
|
||||||
|
key,
|
||||||
|
'%s\n%s\nprefix:%s' % (method, expires,
|
||||||
|
'/'.join(path_parts[0:4]) + '/' + prefix),
|
||||||
|
hashlib.sha1).hexdigest()
|
||||||
|
return {
|
||||||
|
'temp_url_sig': sig, 'temp_url_expires': str(expires),
|
||||||
|
'temp_url_prefix': prefix}
|
||||||
|
|
||||||
|
def test_empty_prefix(self):
|
||||||
|
parms = self.tempurl_parms(
|
||||||
|
'GET', self.expires,
|
||||||
|
self.env.conn.make_path(self.env.obj.path),
|
||||||
|
self.env.tempurl_key, '')
|
||||||
|
|
||||||
|
contents = self.env.obj.read(
|
||||||
|
parms=parms,
|
||||||
|
cfg={'no_auth_token': True})
|
||||||
|
self.assertEqual(contents, "obj contents")
|
||||||
|
|
||||||
|
def test_no_prefix_match(self):
|
||||||
|
prefix = 'b' if self.env.obj.name[0] == 'a' else 'a'
|
||||||
|
|
||||||
|
parms = self.tempurl_parms(
|
||||||
|
'GET', self.expires,
|
||||||
|
self.env.conn.make_path(self.env.obj.path),
|
||||||
|
self.env.tempurl_key, prefix)
|
||||||
|
|
||||||
|
self.assertRaises(ResponseError, self.env.obj.read,
|
||||||
|
cfg={'no_auth_token': True},
|
||||||
|
parms=parms)
|
||||||
|
self.assert_status([401])
|
||||||
|
|
||||||
|
def test_object_url_with_prefix(self):
|
||||||
|
parms = super(TestTempURLPrefix, self).tempurl_parms(
|
||||||
|
'GET', self.expires,
|
||||||
|
self.env.conn.make_path(self.env.obj.path),
|
||||||
|
self.env.tempurl_key)
|
||||||
|
parms['temp_url_prefix'] = self.env.obj.name
|
||||||
|
|
||||||
|
self.assertRaises(ResponseError, self.env.obj.read,
|
||||||
|
cfg={'no_auth_token': True},
|
||||||
|
parms=parms)
|
||||||
|
self.assert_status([401])
|
||||||
|
|
||||||
|
def test_missing_query_parm(self):
|
||||||
|
del self.obj_tempurl_parms['temp_url_prefix']
|
||||||
|
|
||||||
|
self.assertRaises(ResponseError, self.env.obj.read,
|
||||||
|
cfg={'no_auth_token': True},
|
||||||
|
parms=self.obj_tempurl_parms)
|
||||||
|
self.assert_status([401])
|
||||||
|
|
||||||
|
|
||||||
class TestTempurlUTF8(Base2, TestTempurl):
|
class TestTempurlUTF8(Base2, TestTempurl):
|
||||||
set_up = False
|
set_up = False
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ class TestTempURL(unittest.TestCase):
|
|||||||
if environ is None:
|
if environ is None:
|
||||||
environ = {}
|
environ = {}
|
||||||
|
|
||||||
_junk, account, _junk, _junk = utils.split_path(path, 2, 4)
|
_junk, account, _junk, _junk = utils.split_path(path, 2, 4, True)
|
||||||
self._fake_cache_environ(environ, account, keys,
|
self._fake_cache_environ(environ, account, keys,
|
||||||
container_keys=container_keys)
|
container_keys=container_keys)
|
||||||
req = Request.blank(path, environ=environ, **kwargs)
|
req = Request.blank(path, environ=environ, **kwargs)
|
||||||
@ -125,11 +125,14 @@ class TestTempURL(unittest.TestCase):
|
|||||||
environ={'REQUEST_METHOD': 'OPTIONS'}).get_response(self.tempurl)
|
environ={'REQUEST_METHOD': 'OPTIONS'}).get_response(self.tempurl)
|
||||||
self.assertEqual(resp.status_int, 200)
|
self.assertEqual(resp.status_int, 200)
|
||||||
|
|
||||||
def assert_valid_sig(self, expires, path, keys, sig, environ=None):
|
def assert_valid_sig(self, expires, path, keys, sig, environ=None,
|
||||||
|
prefix=None):
|
||||||
if not environ:
|
if not environ:
|
||||||
environ = {}
|
environ = {}
|
||||||
environ['QUERY_STRING'] = 'temp_url_sig=%s&temp_url_expires=%s' % (
|
environ['QUERY_STRING'] = 'temp_url_sig=%s&temp_url_expires=%s' % (
|
||||||
sig, expires)
|
sig, expires)
|
||||||
|
if prefix is not None:
|
||||||
|
environ['QUERY_STRING'] += '&temp_url_prefix=%s' % prefix
|
||||||
req = self._make_request(path, keys=keys, environ=environ)
|
req = self._make_request(path, keys=keys, environ=environ)
|
||||||
self.tempurl.app = FakeApp(iter([('200 Ok', (), '123')]))
|
self.tempurl.app = FakeApp(iter([('200 Ok', (), '123')]))
|
||||||
resp = req.get_response(self.tempurl)
|
resp = req.get_response(self.tempurl)
|
||||||
@ -294,6 +297,33 @@ class TestTempURL(unittest.TestCase):
|
|||||||
self.assertEqual(req.environ['swift.authorize_override'], True)
|
self.assertEqual(req.environ['swift.authorize_override'], True)
|
||||||
self.assertEqual(req.environ['REMOTE_USER'], '.wsgi.tempurl')
|
self.assertEqual(req.environ['REMOTE_USER'], '.wsgi.tempurl')
|
||||||
|
|
||||||
|
def test_get_valid_with_prefix(self):
|
||||||
|
method = 'GET'
|
||||||
|
expires = int(time() + 86400)
|
||||||
|
prefix = 'p1/p2/'
|
||||||
|
sig_path = 'prefix:/v1/a/c/' + prefix
|
||||||
|
query_path = '/v1/a/c/' + prefix + 'o'
|
||||||
|
key = 'abc'
|
||||||
|
hmac_body = '%s\n%s\n%s' % (method, expires, sig_path)
|
||||||
|
sig = hmac.new(key, hmac_body, sha1).hexdigest()
|
||||||
|
self.assert_valid_sig(expires, query_path, [key], sig, prefix=prefix)
|
||||||
|
|
||||||
|
query_path = query_path[:-1] + 'p3/o'
|
||||||
|
key = 'abc'
|
||||||
|
hmac_body = '%s\n%s\n%s' % (method, expires, sig_path)
|
||||||
|
sig = hmac.new(key, hmac_body, sha1).hexdigest()
|
||||||
|
self.assert_valid_sig(expires, query_path, [key], sig, prefix=prefix)
|
||||||
|
|
||||||
|
def test_get_valid_with_prefix_empty(self):
|
||||||
|
method = 'GET'
|
||||||
|
expires = int(time() + 86400)
|
||||||
|
sig_path = 'prefix:/v1/a/c/'
|
||||||
|
query_path = '/v1/a/c/o'
|
||||||
|
key = 'abc'
|
||||||
|
hmac_body = '%s\n%s\n%s' % (method, expires, sig_path)
|
||||||
|
sig = hmac.new(key, hmac_body, sha1).hexdigest()
|
||||||
|
self.assert_valid_sig(expires, query_path, [key], sig, prefix='')
|
||||||
|
|
||||||
def test_obj_odd_chars(self):
|
def test_obj_odd_chars(self):
|
||||||
method = 'GET'
|
method = 'GET'
|
||||||
expires = int(time() + 86400)
|
expires = int(time() + 86400)
|
||||||
@ -786,6 +816,41 @@ class TestTempURL(unittest.TestCase):
|
|||||||
self.assertTrue('Temp URL invalid' in resp.body)
|
self.assertTrue('Temp URL invalid' in resp.body)
|
||||||
self.assertTrue('Www-Authenticate' in resp.headers)
|
self.assertTrue('Www-Authenticate' in resp.headers)
|
||||||
|
|
||||||
|
def test_no_prefix_match_invalid(self):
|
||||||
|
method = 'GET'
|
||||||
|
expires = int(time() + 86400)
|
||||||
|
sig_path = 'prefix:/v1/a/c/p1/p2/'
|
||||||
|
query_path = '/v1/a/c/o'
|
||||||
|
key = 'abc'
|
||||||
|
hmac_body = '%s\n%s\n%s' % (method, expires, sig_path)
|
||||||
|
sig = hmac.new(key, hmac_body, sha1).hexdigest()
|
||||||
|
req = self._make_request(
|
||||||
|
query_path, keys=[key],
|
||||||
|
environ={'QUERY_STRING':
|
||||||
|
'temp_url_sig=%s&temp_url_expires=%s&temp_url_prefix=%s' %
|
||||||
|
(sig, expires, 'p1/p2/')})
|
||||||
|
resp = req.get_response(self.tempurl)
|
||||||
|
self.assertEqual(resp.status_int, 401)
|
||||||
|
self.assertTrue('Temp URL invalid' in resp.body)
|
||||||
|
self.assertTrue('Www-Authenticate' in resp.headers)
|
||||||
|
|
||||||
|
def test_object_url_with_prefix_invalid(self):
|
||||||
|
method = 'GET'
|
||||||
|
expires = int(time() + 86400)
|
||||||
|
path = '/v1/a/c/o'
|
||||||
|
key = 'abc'
|
||||||
|
hmac_body = '%s\n%s\n%s' % (method, expires, path)
|
||||||
|
sig = hmac.new(key, hmac_body, sha1).hexdigest()
|
||||||
|
req = self._make_request(
|
||||||
|
path, keys=[key],
|
||||||
|
environ={'QUERY_STRING':
|
||||||
|
'temp_url_sig=%s&temp_url_expires=%s&temp_url_prefix=o' %
|
||||||
|
(sig, expires)})
|
||||||
|
resp = req.get_response(self.tempurl)
|
||||||
|
self.assertEqual(resp.status_int, 401)
|
||||||
|
self.assertTrue('Temp URL invalid' in resp.body)
|
||||||
|
self.assertTrue('Www-Authenticate' in resp.headers)
|
||||||
|
|
||||||
def test_disallowed_header_object_manifest(self):
|
def test_disallowed_header_object_manifest(self):
|
||||||
self.tempurl = tempurl.filter_factory({})(self.auth)
|
self.tempurl = tempurl.filter_factory({})(self.auth)
|
||||||
expires = int(time() + 86400)
|
expires = int(time() + 86400)
|
||||||
@ -965,38 +1030,49 @@ class TestTempURL(unittest.TestCase):
|
|||||||
self.assertTrue('x-conflict-header-test' in resp.headers)
|
self.assertTrue('x-conflict-header-test' in resp.headers)
|
||||||
self.assertEqual(resp.headers['x-conflict-header-test'], 'value')
|
self.assertEqual(resp.headers['x-conflict-header-test'], 'value')
|
||||||
|
|
||||||
def test_get_account_and_container(self):
|
def test_get_path_parts(self):
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/v1/a/c/o'}), ('a', 'c'))
|
'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/v1/a/c/o'}),
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
('a', 'c', 'o'))
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c/o'}), ('a', 'c'))
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c/o'}),
|
||||||
'REQUEST_METHOD': 'PUT', 'PATH_INFO': '/v1/a/c/o'}), ('a', 'c'))
|
('a', 'c', 'o'))
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'POST', 'PATH_INFO': '/v1/a/c/o'}), ('a', 'c'))
|
'REQUEST_METHOD': 'PUT', 'PATH_INFO': '/v1/a/c/o'}),
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
('a', 'c', 'o'))
|
||||||
'REQUEST_METHOD': 'DELETE', 'PATH_INFO': '/v1/a/c/o'}), ('a', 'c'))
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
'REQUEST_METHOD': 'POST', 'PATH_INFO': '/v1/a/c/o'}),
|
||||||
|
('a', 'c', 'o'))
|
||||||
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
|
'REQUEST_METHOD': 'DELETE', 'PATH_INFO': '/v1/a/c/o'}),
|
||||||
|
('a', 'c', 'o'))
|
||||||
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'UNKNOWN', 'PATH_INFO': '/v1/a/c/o'}),
|
'REQUEST_METHOD': 'UNKNOWN', 'PATH_INFO': '/v1/a/c/o'}),
|
||||||
(None, None))
|
(None, None, None))
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c/'}), (None, None))
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c/'}),
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
(None, None, None))
|
||||||
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c//////'}),
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c//////'}),
|
||||||
(None, None))
|
(None, None, None))
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c///o///'}),
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c///o///'}),
|
||||||
('a', 'c'))
|
('a', 'c', '//o///'))
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c'}), (None, None))
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c'}),
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
(None, None, None))
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a//o'}), (None, None))
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a//o'}),
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1//c/o'}), (None, None))
|
(None, None, None))
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '//a/c/o'}), (None, None))
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1//c/o'}),
|
||||||
self.assertEqual(self.tempurl._get_account_and_container({
|
(None, None, None))
|
||||||
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v2/a/c/o'}), (None, None))
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '//a/c/o'}),
|
||||||
|
(None, None, None))
|
||||||
|
self.assertEqual(self.tempurl._get_path_parts({
|
||||||
|
'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v2/a/c/o'}),
|
||||||
|
(None, None, None))
|
||||||
|
|
||||||
def test_get_temp_url_info(self):
|
def test_get_temp_url_info(self):
|
||||||
s = 'f5d5051bddf5df7e27c628818738334f'
|
s = 'f5d5051bddf5df7e27c628818738334f'
|
||||||
@ -1005,55 +1081,61 @@ class TestTempURL(unittest.TestCase):
|
|||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s' % (
|
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s' % (
|
||||||
s, e)}),
|
s, e)}),
|
||||||
(s, e, None, None))
|
(s, e, None, None, None))
|
||||||
|
self.assertEqual(
|
||||||
|
self.tempurl._get_temp_url_info(
|
||||||
|
{'QUERY_STRING':
|
||||||
|
'temp_url_sig=%s&temp_url_expires=%s&temp_url_prefix=%s' % (
|
||||||
|
s, e, 'prefix')}),
|
||||||
|
(s, e, 'prefix', None, None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s&'
|
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s&'
|
||||||
'filename=bobisyouruncle' % (s, e)}),
|
'filename=bobisyouruncle' % (s, e)}),
|
||||||
(s, e, 'bobisyouruncle', None))
|
(s, e, None, 'bobisyouruncle', None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info({}),
|
self.tempurl._get_temp_url_info({}),
|
||||||
(None, None, None, None))
|
(None, None, None, None, None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_expires=%s' % e}),
|
{'QUERY_STRING': 'temp_url_expires=%s' % e}),
|
||||||
(None, e, None, None))
|
(None, e, None, None, None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_sig=%s' % s}),
|
{'QUERY_STRING': 'temp_url_sig=%s' % s}),
|
||||||
(s, None, None, None))
|
(s, None, None, None, None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=bad' % (
|
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=bad' % (
|
||||||
s)}),
|
s)}),
|
||||||
(s, 0, None, None))
|
(s, 0, None, None, None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s&'
|
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s&'
|
||||||
'inline=' % (s, e)}),
|
'inline=' % (s, e)}),
|
||||||
(s, e, None, True))
|
(s, e, None, None, True))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s&'
|
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s&'
|
||||||
'filename=bobisyouruncle&inline=' % (s, e)}),
|
'filename=bobisyouruncle&inline=' % (s, e)}),
|
||||||
(s, e, 'bobisyouruncle', True))
|
(s, e, None, 'bobisyouruncle', True))
|
||||||
e = int(time() - 1)
|
e = int(time() - 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_temp_url_info(
|
self.tempurl._get_temp_url_info(
|
||||||
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s' % (
|
{'QUERY_STRING': 'temp_url_sig=%s&temp_url_expires=%s' % (
|
||||||
s, e)}),
|
s, e)}),
|
||||||
(s, 0, None, None))
|
(s, 0, None, None, None))
|
||||||
|
|
||||||
def test_get_hmacs(self):
|
def test_get_hmacs(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_hmacs(
|
self.tempurl._get_hmacs(
|
||||||
{'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c/o'},
|
{'REQUEST_METHOD': 'GET'}, 1, '/v1/a/c/o',
|
||||||
1, [('abc', 'account')]),
|
[('abc', 'account')]),
|
||||||
[('026d7f7cc25256450423c7ad03fc9f5ffc1dab6d', 'account')])
|
[('026d7f7cc25256450423c7ad03fc9f5ffc1dab6d', 'account')])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.tempurl._get_hmacs(
|
self.tempurl._get_hmacs(
|
||||||
{'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/v1/a/c/o'},
|
{'REQUEST_METHOD': 'HEAD'}, 1, '/v1/a/c/o',
|
||||||
1, [('abc', 'account')], request_method='GET'),
|
[('abc', 'account')], request_method='GET'),
|
||||||
[('026d7f7cc25256450423c7ad03fc9f5ffc1dab6d', 'account')])
|
[('026d7f7cc25256450423c7ad03fc9f5ffc1dab6d', 'account')])
|
||||||
|
|
||||||
def test_invalid(self):
|
def test_invalid(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user