Skip to content

Commit

Permalink
Moving MD5 hash method from regression to storage helpers.
Browse files Browse the repository at this point in the history
May make sense to swap out for `hashlib.md5` (though they seem to
be equivalent).

In preparation for googleapis#547.
  • Loading branch information
dhermes committed Feb 12, 2015
1 parent df4c625 commit e062c89
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 10 deletions.
14 changes: 14 additions & 0 deletions gcloud/storage/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
These are *not* part of the API.
"""

from Crypto.Hash import MD5
import base64


class _PropertyMixin(object):
"""Abstract mixin for cloud storage classes with associated propertties.
Expand Down Expand Up @@ -187,3 +190,14 @@ def _setter(self, value):
self._patch_properties({fieldname: value})

return property(_getter, _setter)


def _base64_md5hash(bytes_to_sign):
"""Get MD5 hash of bytes (as base64).
:type bytes_to_sign: bytes
:param bytes_to_sign: Bytes used to compute an MD5 hash (as base64).
"""
hash = MD5.new(data=bytes_to_sign)
digest_bytes = hash.digest()
return base64.b64encode(digest_bytes)
60 changes: 60 additions & 0 deletions gcloud/storage/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,35 @@ def _patch_properties(self, mapping):
self.assertEqual(test._patched, {'solfege': 'Latido'})


class Test__base64_md5hash(unittest2.TestCase):

def _callFUT(self, bytes_to_sign):
from gcloud.storage._helpers import _base64_md5hash
return _base64_md5hash(bytes_to_sign)

def test_it(self):
BYTES_TO_SIGN = b'FOO'
SIGNED_CONTENT = self._callFUT(BYTES_TO_SIGN)
self.assertEqual(SIGNED_CONTENT, b'kBiQqOnIz21aGlQrIp/r/w==')

def test_it_with_stubs(self):
from gcloud._testing import _Monkey
from gcloud.storage import _helpers as MUT

BASE64 = _Base64()
DIGEST_VAL = object()
BYTES_TO_SIGN = object()
MD5 = _MD5(DIGEST_VAL)

with _Monkey(MUT, base64=BASE64, MD5=MD5):
SIGNED_CONTENT = self._callFUT(BYTES_TO_SIGN)

self.assertTrue(SIGNED_CONTENT is DIGEST_VAL)
self.assertEqual(BASE64._called_b64encode, [DIGEST_VAL])
self.assertEqual(MD5._new_called, [BYTES_TO_SIGN])
self.assertEqual(MD5.hash_obj.num_digest_calls, 1)


class _Connection(object):

def __init__(self, *responses):
Expand All @@ -247,3 +276,34 @@ def api_request(self, **kw):
self._requested.append(kw)
response, self._responses = self._responses[0], self._responses[1:]
return response


class _MD5Hash(object):

def __init__(self, digest_val):
self.digest_val = digest_val
self.num_digest_calls = 0

def digest(self):
self.num_digest_calls += 1
return self.digest_val

class _MD5(object):

def __init__(self, digest_val):
self.hash_obj = _MD5Hash(digest_val)
self._new_called = []

def new(self, data=None):
self._new_called.append(data)
return self.hash_obj


class _Base64(object):

def __init__(self):
self._called_b64encode = []

def b64encode(self, value):
self._called_b64encode.append(value)
return value
13 changes: 3 additions & 10 deletions regression/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from Crypto.Hash import MD5
import base64
import httplib2
import tempfile
import time
import unittest2

from gcloud import exceptions
from gcloud import storage
from gcloud.storage._helpers import _base64_md5hash
from gcloud.storage import _implicit_environ


Expand Down Expand Up @@ -96,18 +95,12 @@ class TestStorageFiles(unittest2.TestCase):
}
}

@staticmethod
def _get_base64_md5hash(filename):
with open(filename, 'rb') as file_obj:
hash = MD5.new(data=file_obj.read())
digest_bytes = hash.digest()
return base64.b64encode(digest_bytes)

@classmethod
def setUpClass(cls):
super(TestStorageFiles, cls).setUpClass()
for file_data in cls.FILES.values():
file_data['hash'] = cls._get_base64_md5hash(file_data['path'])
with open(file_data['path'], 'rb') as file_obj:
file_data['hash'] = _base64_md5hash(file_obj.read())
cls.bucket = SHARED_BUCKETS['test_bucket']

def setUp(self):
Expand Down

0 comments on commit e062c89

Please sign in to comment.