Created
December 9, 2014 08:54
-
-
Save dhilipsiva/3e51ccceebf4adbd535f to your computer and use it in GitHub Desktop.
A Google Cloud Storage utility - To upload a file, get a file and get a signed URL to post from browser.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# vim:fenc=utf-8 | |
# | |
# Copyright © 2014 dhilipsiva <[email protected]> | |
# | |
# Distributed under terms of the MIT license. | |
""" | |
Requirements: | |
pycrypto | |
requests | |
""" | |
import base64 | |
import datetime | |
import md5 | |
import time | |
import requests | |
from Crypto.Hash import SHA256 | |
from Crypto.PublicKey import RSA | |
from Crypto.Signature import PKCS1_v1_5 | |
# Set this to the path of your service account private key file, in DER format. | |
# | |
# PyCrypto requires using the DER key format, but GCS provides key files in | |
# pkcs12 format. To convert between formats, you can use the provided commands | |
# below. | |
# | |
# The default password for p12 file is `notasecret` | |
# | |
# Given a GCS key in pkcs12 format, convert it to PEM using this command: | |
# openssl pkcs12 -in path/to/key.p12 -nodes -nocerts > path/to/key.pem | |
# Given a GCS key in PEM format, convert it to DER format using this command: | |
# openssl rsa -in privatekey.pem -inform PEM -out privatekey.der -outform DER | |
GC_PRIVATE_KEY_PATH = "privatekey.der" | |
GC_BUCKET_NAME = 'bucket' | |
GC_STORAGE_ENDPOINT = '//storage.googleapis.com' | |
GC_EMAIL_ADDRESS = '[email protected]' | |
keytext = open(GC_PRIVATE_KEY_PATH, 'rb').read() | |
key = RSA.importKey(keytext) | |
# Sample Usage: | |
# put_data('blah.txt', 'blah blah') | |
# get_data('blah.txt') | |
def _base64Sign(plaintext): | |
""" | |
Signs and returns a base64-encoded SHA256 digest. | |
""" | |
shahash = SHA256.new(plaintext) | |
signer = PKCS1_v1_5.new(key) | |
signature_bytes = signer.sign(shahash) | |
return base64.b64encode(signature_bytes) | |
def _makeSignatureString(verb, path, content_md5, content_type, expiration): | |
""" | |
Creates the signature string for signing according to GCS docs. | |
""" | |
signature_string = ( | |
'{verb}\n' | |
'{content_md5}\n' | |
'{content_type}\n' | |
'{expiration}\n' | |
'{resource}' | |
) | |
return signature_string.format( | |
verb=verb, content_md5=content_md5, content_type=content_type, | |
expiration=expiration, resource=path) | |
def _makeUrl(verb, path, content_type='', content_md5=''): | |
""" | |
Forms and returns the full signed URL to access GCS. | |
""" | |
path = '/%s/%s' % (GC_BUCKET_NAME, path) | |
base_url = '%s%s' % (GC_STORAGE_ENDPOINT, path) | |
expiration = datetime.datetime.now() + datetime.timedelta(hours=1) | |
expiration = int(time.mktime(expiration.timetuple())) | |
signature_string = _makeSignatureString( | |
verb, path, content_md5, content_type, expiration) | |
signature_signed = _base64Sign(signature_string) | |
query_params = { | |
'GoogleAccessId': GC_EMAIL_ADDRESS, | |
'Expires': str(expiration), | |
'Signature': signature_signed, | |
} | |
return base_url, query_params | |
def get_data(file_key): | |
""" | |
Performs a GET request. | |
Args: | |
file_key: Key of the file you want to access as found in | |
`<bucket-name>/<file_key>` | |
Returns: | |
An instance of requests.Response containing the HTTP response. | |
""" | |
base_url, query_params = _makeUrl('GET', file_key) | |
return requests.get('https:' + base_url, params=query_params) | |
def put_data(file_key, data, content_type='application/octet-stream'): | |
""" | |
Performs a PUT request. | |
Args: | |
file_key: Key of the file you want to access as found in | |
`<bucket-name>/<file_key>` | |
content_type: The content type to assign to the upload. | |
data: The file data to upload to the new file. | |
Returns: | |
An instance of requests.Response containing the HTTP response. | |
""" | |
md5_digest = base64.b64encode(md5.new(data).digest()) | |
base_url, query_params = _makeUrl( | |
'PUT', file_key, content_type, md5_digest) | |
headers = { | |
'Content-Type': content_type, | |
'Content-MD5': md5_digest, | |
} | |
return requests.put( | |
'https:' + base_url, params=query_params, headers=headers, data=data) | |
def get_put_params(file_key, content_type, md5_digest): | |
""" | |
Args: | |
file_key: Key of the file you want to access as found in | |
`<bucket-name>/<file_key>` | |
content_type: The content type to assign to the upload. | |
md5_digest: MD5 digest of file | |
Returns: | |
base_url, query_params and headers for signing the request | |
""" | |
base_url, query_params = _makeUrl( | |
'PUT', file_key, content_type, md5_digest) | |
headers = { | |
'Content-Type': content_type, | |
'Content-MD5': md5_digest, | |
} | |
return base_url, query_params, headers |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment