Source code for gcloud.storage.connection

+import base64
+import datetime
 import httplib2
 import json
+import time
 import urllib
 
+from Crypto.Hash import SHA256
+from Crypto.PublicKey import RSA
+from Crypto.Signature import PKCS1_v1_5
+from OpenSSL import crypto
+import pytz
 
 from gcloud import connection
 from gcloud.storage import exceptions
@@ -140,6 +148,8 @@ 

Source code for gcloud.storage.connection

   API_URL_TEMPLATE = '{api_base_url}/storage/{api_version}{path}'
   """A template used to craft the URL pointing toward a particular API call."""
 
+  API_ACCESS_ENDPOINT = 'https://storage.googleapis.com'
+
   def __init__(self, project_name, *args, **kwargs):
     """
     :type project_name: string
@@ -482,7 +492,86 @@ 

Source code for gcloud.storage.connection

     if isinstance(bucket, string_type):
       return Bucket(connection=self, name=bucket)
 
-    raise TypeError('Invalid bucket: %s' % bucket)
+ raise TypeError('Invalid bucket: %s' % bucket) +
+
[docs] def generate_signed_url(self, resource, expiration, method='GET', content_md5=None, content_type=None): + """Generate a signed URL to provide query-string authentication to a resource. + + :type resource: string + :param resource: A pointer to a specific resource + (typically, ``/bucket-name/path/to/key.txt``). + + :type expiration: int, long, datetime.datetime, datetime.timedelta + :param expiration: When the signed URL should expire. + + :type method: string + :param method: The HTTP verb that will be used when requesting the URL. + + :type content_md5: string + :param content_md5: The MD5 hash of the object referenced by ``resource``. + + :type content_type: string + :param content_type: The content type of the object referenced by + ``resource``. + + :rtype: string + :returns: A signed URL you can use to access the resource until expiration. + """ + + # expiration can be an absolute timestamp (int, long), + # an absolute time (datetime.datetime), + # or a relative time (datetime.timedelta). + # We should convert all of these into an absolute timestamp. + + # If it's a timedelta, add it to `now` in UTC. + if isinstance(expiration, datetime.timedelta): + now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) + expiration = now + expiration + + # If it's a datetime, convert to a timestamp. + if isinstance(expiration, datetime.datetime): + # Make sure the timezone on the value is UTC + # (either by converting or replacing the value). + if expiration.tzinfo: + expiration = expiration.astimezone(pytz.utc) + else: + expiration = expiration.replace(tzinfo=pytz.utc) + + # Turn the datetime into a timestamp (seconds, not microseconds). + expiration = int(time.mktime(expiration.timetuple())) + + if not isinstance(expiration, (int, long)): + raise ValueError('Expected an integer timestamp, datetime, or timedelta. ' + 'Got %s' % type(expiration)) + + # Generate the string to sign. + signature_string = '\n'.join([ + method, + content_md5 or '', + content_type or '', + str(expiration), + resource]) + + # Take our PKCS12 (.p12) key and make it into a RSA key we can use... + pkcs12 = crypto.load_pkcs12(base64.b64decode(self.credentials.private_key), 'notasecret') + pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey()) + pem_key = RSA.importKey(pem) + + # Sign the string with the RSA key. + signer = PKCS1_v1_5.new(pem_key) + signature_hash = SHA256.new(signature_string) + signature_bytes = signer.sign(signature_hash) + signature = base64.b64encode(signature_bytes) + + # Set the right query parameters. + query_params = {'GoogleAccessId': self.credentials.service_account_name, + 'Expires': str(expiration), + 'Signature': signature} + + # Return the built URL. + return '{endpoint}{resource}?{querystring}'.format( + endpoint=self.API_ACCESS_ENDPOINT, resource=resource, + querystring=urllib.urlencode(query_params))