diff --git a/Authors b/Authors index f84155f6..4d9bc5f2 100644 --- a/Authors +++ b/Authors @@ -14,6 +14,7 @@ Arvind Somya Bilal Akhtar Brad Hall Brad McConnell +Brendan Maguire Brian Lamar Brian Schott Brian Waldon diff --git a/nova/auth/signer.py b/nova/auth/signer.py index 744e315d..c0468021 100644 --- a/nova/auth/signer.py +++ b/nova/auth/signer.py @@ -67,6 +67,8 @@ class Signer(object): self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1) if hashlib.sha256: self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256) + else: + self.hmac_256 = None def s3_authorization(self, headers, verb, path): """Generate S3 authorization string.""" @@ -77,7 +79,12 @@ class Signer(object): return b64_hmac def generate(self, params, verb, server_string, path): - """Generate auth string according to what SignatureVersion is given.""" + """Generate auth string according to what SignatureVersion is given. + + The signature method defaults to SHA256 if available, or falls back to + SHA1 if not. + + """ if params['SignatureVersion'] == '0': return self._calc_signature_0(params) if params['SignatureVersion'] == '1': @@ -150,6 +157,5 @@ class Signer(object): if __name__ == '__main__': - print Signer('foo').generate({'SignatureMethod': 'HmacSHA256', - 'SignatureVersion': '2'}, - 'get', 'server', '/foo') + print Signer('foo').generate({'SignatureVersion': '2'}, 'get', 'server', + '/foo') diff --git a/nova/tests/test_signer.py b/nova/tests/test_signer.py new file mode 100644 index 00000000..bbfd09be --- /dev/null +++ b/nova/tests/test_signer.py @@ -0,0 +1,92 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, 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. + +"""Tests for Signer.""" + +from nova import exception +from nova import test +from nova.auth import signer + + +class ClassWithStrRepr(object): + def __repr__(self): + return 'A string representation' + + +class SignerTestCase(test.TestCase): + def setUp(self): + super(SignerTestCase, self).setUp() + self.signer = signer.Signer('uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o') + + def tearDown(self): + super(SignerTestCase, self).tearDown() + + # S3 Authorization Signing input & output examples taken from here: + # http://docs.amazonwebservices.com/AmazonS3/latest/dev/ + def test_s3_authorization_get(self): + self.assertEquals('xXjDGYUmKxnwqr5KXNPGldn5LbA=', + self.signer.s3_authorization( + {'Date': 'Tue, 27 Mar 2007 19:36:42 +0000'}, + 'GET', + '/johnsmith/photos/puppy.jpg')) + + def test_s3_authorization_put(self): + self.assertEquals('hcicpDDvL9SsO6AkvxqmIWkmOuQ=', + self.signer.s3_authorization( + {'Date': 'Tue, 27 Mar 2007 21:15:45 +0000', + 'Content-Length': '94328', + 'Content-Type': 'image/jpeg'}, + 'PUT', + '/johnsmith/photos/puppy.jpg')) + + def test_generate_using_version_2(self): + self.assertEquals('clXalhbLZXxEuI32OoX+OeXsN6Mr2q4jzGyIDAr4RZg=', + self.signer.generate( + {'SignatureMethod': 'HmacSHA256', + 'SignatureVersion': '2'}, + 'GET', 'server', '/foo')) + + def test_generate_force_HmacSHA1(self): + # Stub out haslib.sha256 + import hashlib + self.stubs.Set(hashlib, 'sha256', None) + + # Create Signer again now that hashlib.sha256 is None + self.signer = signer.Signer( + 'uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o') + self.assertEquals('uJTByiDIcgB65STrS5i2egQgd+U=', + self.signer.generate({'SignatureVersion': '2'}, + 'GET', 'server', '/foo')) + + def test_generate_with_unicode_param(self): + self.assertEquals('clXalhbLZXxEuI32OoX+OeXsN6Mr2q4jzGyIDAr4RZg=', + self.signer.generate({'SignatureVersion': u'2'}, + 'GET', 'server', '/foo')) + + def test_generate_with_non_string_or_unicode_param(self): + self.assertEquals('99IAgCkhTR2aMTgRobnzKGuNxVFSdb7vlQRvnj3Urqk=', + self.signer.generate( + {'AnotherParam': ClassWithStrRepr(), + 'SignatureVersion': '2'}, + 'GET', 'server', '/foo')) + + def test_generate_unknown_version(self): + with self.assertRaises(exception.Error): + self.signer.generate({'SignatureMethod': 'HmacSHA256', + 'SignatureVersion': '9'}, + 'GET', 'server', '/foo')