# Copyright 2012 OpenStack Foundation # # 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. import testtools from keystoneclient.contrib.ec2 import utils from keystoneclient.tests.unit import client_fixtures class Ec2SignerTest(testtools.TestCase): def setUp(self): super(Ec2SignerTest, self).setUp() self.useFixture(client_fixtures.Deprecations()) self.access = '966afbde20b84200ae4e62e09acf46b2' self.secret = '89cdf9e94e2643cab35b8b8ac5a51f83' self.signer = utils.Ec2Signer(self.secret) def test_v4_creds_header(self): auth_str = 'AWS4-HMAC-SHA256 blah' credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {}, 'headers': {'Authorization': auth_str}} self.assertTrue(self.signer._v4_creds(credentials)) def test_v4_creds_param(self): credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'X-Amz-Algorithm': 'AWS4-HMAC-SHA256'}, 'headers': {}} self.assertTrue(self.signer._v4_creds(credentials)) def test_v4_creds_false(self): credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '0', 'AWSAccessKeyId': self.access, 'Timestamp': '2012-11-27T11:47:02Z', 'Action': 'Foo'}} self.assertFalse(self.signer._v4_creds(credentials)) def test_generate_0(self): """Test generate function for v0 signature.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '0', 'AWSAccessKeyId': self.access, 'Timestamp': '2012-11-27T11:47:02Z', 'Action': 'Foo'}} signature = self.signer.generate(credentials) expected = 'SmXQEZAUdQw5glv5mX8mmixBtas=' self.assertEqual(signature, expected) def test_generate_1(self): """Test generate function for v1 signature.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '1', 'AWSAccessKeyId': self.access}} signature = self.signer.generate(credentials) expected = 'VRnoQH/EhVTTLhwRLfuL7jmFW9c=' self.assertEqual(signature, expected) def test_generate_v2_SHA256(self): """Test generate function for v2 signature, SHA256.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '2', 'AWSAccessKeyId': self.access}} signature = self.signer.generate(credentials) expected = 'odsGmT811GffUO0Eu13Pq+xTzKNIjJ6NhgZU74tYX/w=' self.assertEqual(signature, expected) def test_generate_v2_SHA1(self): """Test generate function for v2 signature, SHA1.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '2', 'AWSAccessKeyId': self.access}} self.signer.hmac_256 = None signature = self.signer.generate(credentials) expected = 'ZqCxMI4ZtTXWI175743mJ0hy/Gc=' self.assertEqual(signature, expected) def test_generate_v4(self): """Test v4 generator with data from AWS docs example. see: http://docs.aws.amazon.com/general/latest/gr/ sigv4-create-canonical-request.html and http://docs.aws.amazon.com/general/latest/gr/ sigv4-signed-request-examples.html """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'iam.amazonaws.com', 'Authorization': auth_str} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {'Action': 'CreateUser', 'UserName': 'NewUser', 'Version': '2010-05-08', 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', 'X-Amz-Credential': 'AKIAEXAMPLE/20140611/' 'us-east-1/iam/aws4_request', 'X-Amz-Date': '20140611T231318Z', 'X-Amz-Expires': '30', 'X-Amz-SignedHeaders': 'host', 'X-Amz-Signature': 'ced6826de92d2bdeed8f846f0bf508e8' '559e98e4b0199114b84c54174deb456c'} credentials = {'host': 'iam.amazonaws.com', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('ced6826de92d2bdeed8f846f0bf508e8' '559e98e4b0199114b84c54174deb456c') self.assertEqual(signature, expected) def test_generate_v4_port(self): """Test v4 generator with host:port format.""" # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'foo:8000', 'Authorization': auth_str} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'foo:8000', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('26dd92ea79aaa49f533d13b1055acdc' 'd7d7321460d64621f96cc79c4f4d4ab2b') self.assertEqual(signature, expected) def test_generate_v4_port_strip(self): """Test v4 generator with host:port format for old boto version. Validate for old (<2.9.3) version of boto, where the port should be stripped to match boto behavior. """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'foo:8000', 'Authorization': auth_str, 'User-Agent': 'Boto/2.9.2 (linux2)'} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'foo:8000', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('9a4b2276a5039ada3b90f72ea8ec1745' '14b92b909fb106b22ad910c5d75a54f4') self.assertEqual(expected, signature) def test_generate_v4_port_nostrip(self): """Test v4 generator with host:port format for new boto version. Validate for new (>=2.9.3) version of boto, where the port should not be stripped. """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'foo:8000', 'Authorization': auth_str, 'User-Agent': 'Boto/2.9.3 (linux2)'} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'foo:8000', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('26dd92ea79aaa49f533d13b1055acdc' 'd7d7321460d64621f96cc79c4f4d4ab2b') self.assertEqual(expected, signature) def test_generate_v4_port_malformed_version(self): """Test v4 generator with host:port format for malformed boto version. Validate for malformed version of boto, where the port should not be stripped. """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'foo:8000', 'Authorization': auth_str, 'User-Agent': 'Boto/2.922 (linux2)'} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'foo:8000', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('26dd92ea79aaa49f533d13b1055acdc' 'd7d7321460d64621f96cc79c4f4d4ab2b') self.assertEqual(expected, signature)