Author: tomaz
Date: Wed Aug  8 04:34:45 2012
New Revision: 1370664

URL: http://svn.apache.org/viewvc?rev=1370664&view=rev
Log:
Add the following new methods to the CloudFiles driver:
ex_set_account_metadata_temp_url_key, ex_get_object_temp_url. Contributed by
Shawn Smith, part of GITHUB-72.

Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
    libcloud/trunk/libcloud/test/storage/test_cloudfiles.py

Modified: libcloud/trunk/CHANGES
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1370664&r1=1370663&r2=1370664&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Wed Aug  8 04:34:45 2012
@@ -7,6 +7,12 @@ Changes with Apache Libcloud in developm
     - Add new Rackspace Nova driver for Chicago (ORD) location ; LIBCLOUD-234
       [Brian McDaniel]
 
+  *) Storage
+
+    - Add the following new methods to the CloudFiles driver:
+      ex_set_account_metadata_temp_url_key, ex_get_object_temp_url. ; GITHUB-72
+      [Shawn Smith]
+
 Changes with Apache Libcloud 0.11.1:
 
   *) General

Modified: libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py?rev=1370664&r1=1370663&r2=1370664&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/drivers/cloudfiles.py (original)
+++ libcloud/trunk/libcloud/storage/drivers/cloudfiles.py Wed Aug  8 04:34:45 
2012
@@ -13,9 +13,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from hashlib import sha1
+import hmac
 import os
+from time import time
 
 from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import urlencode
 
 try:
     import simplejson as json
@@ -407,10 +411,13 @@ class CloudFilesStorageDriver(StorageDri
                 'x-account-object-count', 'unknown')
             bytes_used = response.headers.get(
                 'x-account-bytes-used', 'unknown')
+            temp_url_key = response.headers.get(
+                'x-account-meta-temp-url-key', None)
 
             return { 'container_count': int(container_count),
                       'object_count': int(object_count),
-                      'bytes_used': int(bytes_used) }
+                      'bytes_used': int(bytes_used),
+                      'temp_url_key': temp_url_key }
 
         raise LibcloudError('Unexpected status code: %s' % (response.status))
 
@@ -472,6 +479,63 @@ class CloudFilesStorageDriver(StorageDri
 
         return response.status in [ httplib.CREATED, httplib.ACCEPTED ]
 
+    def ex_set_account_metadata_temp_url_key(self, key):
+        """
+        Set the metadata header X-Account-Meta-Temp-URL-Key on your Cloud
+        Files account.
+
+        @param key: X-Account-Meta-Temp-URL-Key
+        @type key: C{str}
+        """
+        headers = {'X-Account-Meta-Temp-URL-Key': key}
+
+        response = self.connection.request('',
+                                           method='POST',
+                                           headers=headers,
+                                           cdn_request=False)
+
+        return response.status in [httplib.OK, httplib.NO_CONTENT,
+                                   httplib.CREATED, httplib.ACCEPTED]
+
+    def ex_get_object_temp_url(self, obj, method='GET', timeout=60):
+        """
+        Create a temporary URL to allow others to retrieve or put objects
+        in your Cloud Files account for as long or as short a time as you
+        wish.  This method is specifically for allowing users to retrieve
+        or update an object.
+
+        @param object: The object that you wish to make temporarily public
+        @type container: C{Object}
+        @param method: Which method you would like to allow, 'PUT' or 'GET'
+        @type method: C{str}
+        @param timeout: Time (in seconds) after which you want the TempURL
+        to expire.
+        @type timeout: C{int}
+        """
+        self.connection._populate_hosts_and_request_paths()
+        expires = int(time() + timeout)
+        path = '%s/%s/%s' % (self.connection.request_path,
+                            obj.container.name, obj.name)
+        try:
+            key = self.ex_get_meta_data()['temp_url_key']
+            assert key is not None
+        except Exception:
+            raise KeyError('You must first set the ' +
+                           'X-Account-Meta-Temp-URL-Key header on your ' +
+                           'Cloud Files account using ' +
+                           'ex_set_account_metadata_temp_url_key before ' +
+                           'you can use this method.')
+        hmac_body = '%s\n%s\n%s' % (method, expires, path)
+        sig = hmac.new(b(key), b(hmac_body), sha1).hexdigest()
+        params = urlencode({'temp_url_sig': sig,
+                            'temp_url_expires': expires})
+
+        temp_url = 'https://%s/%s/%s?%s' % \
+            (self.connection.host + self.connection.request_path,
+                    obj.container.name, obj.name, params)
+
+        return temp_url
+
     def _upload_object_part(self, container, object_name, part_number,
                             iterator, verify_hash=True):
 

Modified: libcloud/trunk/libcloud/test/storage/test_cloudfiles.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/storage/test_cloudfiles.py?rev=1370664&r1=1370663&r2=1370664&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/storage/test_cloudfiles.py (original)
+++ libcloud/trunk/libcloud/test/storage/test_cloudfiles.py Wed Aug  8 04:34:45 
2012
@@ -12,6 +12,8 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+from hashlib import sha1
+import hmac
 import os
 import os.path                          # pylint: disable-msg=W0404
 import math
@@ -493,6 +495,7 @@ class CloudFilesTests(unittest.TestCase)
         self.assertTrue('object_count' in meta_data)
         self.assertTrue('container_count' in meta_data)
         self.assertTrue('bytes_used' in meta_data)
+        self.assertTrue('temp_url_key' in meta_data)
 
     @mock.patch('os.path.getsize')
     def test_ex_multipart_upload_object_for_small_files(self, getsize_mock):
@@ -615,6 +618,42 @@ class CloudFilesTests(unittest.TestCase)
                                               file_name='error.html')
         self.assertTrue(result)
 
+    def test_ex_set_account_metadata_temp_url_key(self):
+        result = self.driver.ex_set_account_metadata_temp_url_key("a key")
+        self.assertTrue(result)
+
+    @mock.patch("libcloud.storage.drivers.cloudfiles.time")
+    def test_ex_get_object_temp_url(self, time):
+        time.return_value = 0
+        self.driver.ex_get_meta_data = mock.Mock()
+        self.driver.ex_get_meta_data.return_value = {'container_count': 1,
+                                                     'object_count': 1,
+                                                     'bytes_used': 1,
+                                                     'temp_url_key': 'foo'}
+        container = Container(name='foo_bar_container', extra={}, driver=self)
+        obj = Object(name='foo_bar_object', size=1000, hash=None, extra={},
+                     container=container, meta_data=None,
+                     driver=self)
+        hmac_body = "%s\n%s\n%s" % ('GET', 60,
+                                    
"/v1/MossoCloudFS/foo_bar_container/foo_bar_object")
+        sig = hmac.new(b('foo'), b(hmac_body), sha1).hexdigest()
+        ret = self.driver.ex_get_object_temp_url(obj, 'GET')
+        temp_url = 
'https://storage101.ord1.clouddrive.com/v1/MossoCloudFS/foo_bar_container/foo_bar_object?temp_url_expires=60&temp_url_sig=%s'
 % (sig)
+
+        self.assertEquals(ret, temp_url)
+
+    def test_ex_get_object_temp_url_no_key_raises_key_error(self):
+        self.driver.ex_get_meta_data = mock.Mock()
+        self.driver.ex_get_meta_data.return_value = {'container_count': 1,
+                                                     'object_count': 1,
+                                                     'bytes_used': 1,
+                                                     'temp_url_key': None}
+        container = Container(name='foo_bar_container', extra={}, driver=self)
+        obj = Object(name='foo_bar_object', size=1000, hash=None, extra={},
+                     container=container, meta_data=None,
+                     driver=self)
+        self.assertRaises(KeyError, self.driver.ex_get_object_temp_url, obj, 
'GET')
+
     def _remove_test_file(self):
         file_path = os.path.abspath(__file__) + '.temp'
 
@@ -674,6 +713,9 @@ class CloudFilesMockHttp(StorageMockHttp
                              'x-account-object-count': 400,
                              'x-account-bytes-used': 1234567
                            })
+        elif method == 'POST':
+            body = ''
+            status_code = httplib.NO_CONTENT
         return (status_code, body, headers, httplib.responses[httplib.OK])
 
     def _v1_MossoCloudFS_not_found(self, method, url, body, headers):


Reply via email to