| OLD | NEW |
| 1 # Copyright (c) 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved | 1 # Copyright (c) 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved |
| 2 # | 2 # |
| 3 # Permission is hereby granted, free of charge, to any person obtaining a | 3 # Permission is hereby granted, free of charge, to any person obtaining a |
| 4 # copy of this software and associated documentation files (the | 4 # copy of this software and associated documentation files (the |
| 5 # "Software"), to deal in the Software without restriction, including | 5 # "Software"), to deal in the Software without restriction, including |
| 6 # without limitation the rights to use, copy, modify, merge, publish, dis- | 6 # without limitation the rights to use, copy, modify, merge, publish, dis- |
| 7 # tribute, sublicense, and/or sell copies of the Software, and to permit | 7 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 8 # persons to whom the Software is furnished to do so, subject to the fol- | 8 # persons to whom the Software is furnished to do so, subject to the fol- |
| 9 # lowing conditions: | 9 # lowing conditions: |
| 10 # | 10 # |
| 11 # The above copyright notice and this permission notice shall be included | 11 # The above copyright notice and this permission notice shall be included |
| 12 # in all copies or substantial portions of the Software. | 12 # in all copies or substantial portions of the Software. |
| 13 # | 13 # |
| 14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | 14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | 15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| 16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | 16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| 17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | 17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | 19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 20 # IN THE SOFTWARE. | 20 # IN THE SOFTWARE. |
| 21 # | 21 # |
| 22 import copy | 22 import copy |
| 23 import mock |
| 23 from mock import Mock | 24 from mock import Mock |
| 24 from tests.unit import unittest | 25 import os |
| 26 from tests.unit import unittest, MockServiceWithConfigTestCase |
| 25 | 27 |
| 26 from boto.auth import HmacAuthV4Handler | 28 from boto.auth import HmacAuthV4Handler |
| 27 from boto.auth import S3HmacAuthV4Handler | 29 from boto.auth import S3HmacAuthV4Handler |
| 30 from boto.auth import detect_potential_s3sigv4 |
| 31 from boto.auth import detect_potential_sigv4 |
| 28 from boto.connection import HTTPRequest | 32 from boto.connection import HTTPRequest |
| 33 from boto.regioninfo import RegionInfo |
| 29 | 34 |
| 30 | 35 |
| 31 class TestSigV4Handler(unittest.TestCase): | 36 class TestSigV4Handler(unittest.TestCase): |
| 32 def setUp(self): | 37 def setUp(self): |
| 33 self.provider = Mock() | 38 self.provider = Mock() |
| 34 self.provider.access_key = 'access_key' | 39 self.provider.access_key = 'access_key' |
| 35 self.provider.secret_key = 'secret_key' | 40 self.provider.secret_key = 'secret_key' |
| 36 self.request = HTTPRequest( | 41 self.request = HTTPRequest( |
| 37 'POST', 'https', 'glacier.us-east-1.amazonaws.com', 443, | 42 'POST', 'https', 'glacier.us-east-1.amazonaws.com', 443, |
| 38 '/-/vaults/foo/archives', None, {}, | 43 '/-/vaults/foo/archives', None, {}, |
| 39 {'x-amz-glacier-version': '2012-06-01'}, '') | 44 {'x-amz-glacier-version': '2012-06-01'}, '') |
| 40 | 45 |
| 46 def test_not_adding_empty_qs(self): |
| 47 self.provider.security_token = None |
| 48 auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com', Mock(), self
.provider) |
| 49 req = copy.copy(self.request) |
| 50 auth.add_auth(req) |
| 51 self.assertEqual(req.path, '/-/vaults/foo/archives') |
| 52 |
| 41 def test_inner_whitespace_is_collapsed(self): | 53 def test_inner_whitespace_is_collapsed(self): |
| 42 auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com', | 54 auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com', |
| 43 Mock(), self.provider) | 55 Mock(), self.provider) |
| 44 self.request.headers['x-amz-archive-description'] = 'two spaces' | 56 self.request.headers['x-amz-archive-description'] = 'two spaces' |
| 57 self.request.headers['x-amz-quoted-string'] = ' "a b c" ' |
| 45 headers = auth.headers_to_sign(self.request) | 58 headers = auth.headers_to_sign(self.request) |
| 46 self.assertEqual(headers, {'Host': 'glacier.us-east-1.amazonaws.com', | 59 self.assertEqual(headers, {'Host': 'glacier.us-east-1.amazonaws.com', |
| 47 'x-amz-archive-description': 'two spaces', | 60 'x-amz-archive-description': 'two spaces', |
| 48 'x-amz-glacier-version': '2012-06-01'}) | 61 'x-amz-glacier-version': '2012-06-01', |
| 62 'x-amz-quoted-string': ' "a b c" '}) |
| 49 # Note the single space between the "two spaces". | 63 # Note the single space between the "two spaces". |
| 50 self.assertEqual(auth.canonical_headers(headers), | 64 self.assertEqual(auth.canonical_headers(headers), |
| 51 'host:glacier.us-east-1.amazonaws.com\n' | 65 'host:glacier.us-east-1.amazonaws.com\n' |
| 52 'x-amz-archive-description:two spaces\n' | 66 'x-amz-archive-description:two spaces\n' |
| 53 'x-amz-glacier-version:2012-06-01') | 67 'x-amz-glacier-version:2012-06-01\n' |
| 68 'x-amz-quoted-string:"a b c"') |
| 54 | 69 |
| 55 def test_canonical_query_string(self): | 70 def test_canonical_query_string(self): |
| 56 auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com', | 71 auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com', |
| 57 Mock(), self.provider) | 72 Mock(), self.provider) |
| 58 request = HTTPRequest( | 73 request = HTTPRequest( |
| 59 'GET', 'https', 'glacier.us-east-1.amazonaws.com', 443, | 74 'GET', 'https', 'glacier.us-east-1.amazonaws.com', 443, |
| 60 '/-/vaults/foo/archives', None, {}, | 75 '/-/vaults/foo/archives', None, {}, |
| 61 {'x-amz-glacier-version': '2012-06-01'}, '') | 76 {'x-amz-glacier-version': '2012-06-01'}, '') |
| 62 request.params['Foo.1'] = 'aaa' | 77 request.params['Foo.1'] = 'aaa' |
| 63 request.params['Foo.10'] = 'zzz' | 78 request.params['Foo.10'] = 'zzz' |
| 64 query_string = auth.canonical_query_string(request) | 79 query_string = auth.canonical_query_string(request) |
| 65 self.assertEqual(query_string, 'Foo.1=aaa&Foo.10=zzz') | 80 self.assertEqual(query_string, 'Foo.1=aaa&Foo.10=zzz') |
| 66 | 81 |
| 82 def test_query_string(self): |
| 83 auth = HmacAuthV4Handler('sns.us-east-1.amazonaws.com', |
| 84 Mock(), self.provider) |
| 85 params = { |
| 86 'Message': u'We \u2665 utf-8'.encode('utf-8'), |
| 87 } |
| 88 request = HTTPRequest( |
| 89 'POST', 'https', 'sns.us-east-1.amazonaws.com', 443, |
| 90 '/', None, params, {}, '') |
| 91 query_string = auth.query_string(request) |
| 92 self.assertEqual(query_string, 'Message=We%20%E2%99%A5%20utf-8') |
| 93 |
| 67 def test_canonical_uri(self): | 94 def test_canonical_uri(self): |
| 68 auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com', | 95 auth = HmacAuthV4Handler('glacier.us-east-1.amazonaws.com', |
| 69 Mock(), self.provider) | 96 Mock(), self.provider) |
| 70 request = HTTPRequest( | 97 request = HTTPRequest( |
| 71 'GET', 'https', 'glacier.us-east-1.amazonaws.com', 443, | 98 'GET', 'https', 'glacier.us-east-1.amazonaws.com', 443, |
| 72 'x/./././x .html', None, {}, | 99 'x/./././x .html', None, {}, |
| 73 {'x-amz-glacier-version': '2012-06-01'}, '') | 100 {'x-amz-glacier-version': '2012-06-01'}, '') |
| 74 canonical_uri = auth.canonical_uri(request) | 101 canonical_uri = auth.canonical_uri(request) |
| 75 # This should be both normalized & urlencoded. | 102 # This should be both normalized & urlencoded. |
| 76 self.assertEqual(canonical_uri, 'x/x%20.html') | 103 self.assertEqual(canonical_uri, 'x/x%20.html') |
| (...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 424 x-amz-date:20130605T193245Z | 451 x-amz-date:20130605T193245Z |
| 425 | 452 |
| 426 host;user-agent;x-amz-content-sha256;x-amz-date | 453 host;user-agent;x-amz-content-sha256;x-amz-date |
| 427 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855""" | 454 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855""" |
| 428 | 455 |
| 429 # Pre-mangle it. In practice, this happens as part of ``add_auth``, | 456 # Pre-mangle it. In practice, this happens as part of ``add_auth``, |
| 430 # but that's a side-effect that's hard to test. | 457 # but that's a side-effect that's hard to test. |
| 431 request = self.auth.mangle_path_and_params(request) | 458 request = self.auth.mangle_path_and_params(request) |
| 432 authed_req = self.auth.canonical_request(request) | 459 authed_req = self.auth.canonical_request(request) |
| 433 self.assertEqual(authed_req, expected) | 460 self.assertEqual(authed_req, expected) |
| 461 |
| 462 |
| 463 class FakeS3Connection(object): |
| 464 def __init__(self, *args, **kwargs): |
| 465 self.host = kwargs.pop('host', None) |
| 466 |
| 467 @detect_potential_s3sigv4 |
| 468 def _required_auth_capability(self): |
| 469 return ['nope'] |
| 470 |
| 471 def _mexe(self, *args, **kwargs): |
| 472 pass |
| 473 |
| 474 |
| 475 class FakeEC2Connection(object): |
| 476 def __init__(self, *args, **kwargs): |
| 477 self.region = kwargs.pop('region', None) |
| 478 |
| 479 @detect_potential_sigv4 |
| 480 def _required_auth_capability(self): |
| 481 return ['nope'] |
| 482 |
| 483 def _mexe(self, *args, **kwargs): |
| 484 pass |
| 485 |
| 486 |
| 487 class TestS3SigV4OptIn(MockServiceWithConfigTestCase): |
| 488 connection_class = FakeS3Connection |
| 489 |
| 490 def test_sigv4_opt_out(self): |
| 491 # Default is opt-out. |
| 492 fake = FakeS3Connection(host='s3.amazonaws.com') |
| 493 self.assertEqual(fake._required_auth_capability(), ['nope']) |
| 494 |
| 495 def test_sigv4_non_optional(self): |
| 496 # Requires SigV4. |
| 497 fake = FakeS3Connection(host='s3.cn-north-1.amazonaws.com.cn') |
| 498 self.assertEqual(fake._required_auth_capability(), ['hmac-v4-s3']) |
| 499 |
| 500 def test_sigv4_opt_in_config(self): |
| 501 # Opt-in via the config. |
| 502 self.config = { |
| 503 's3': { |
| 504 'use-sigv4': True, |
| 505 }, |
| 506 } |
| 507 fake = FakeS3Connection() |
| 508 self.assertEqual(fake._required_auth_capability(), ['hmac-v4-s3']) |
| 509 |
| 510 def test_sigv4_opt_in_env(self): |
| 511 # Opt-in via the ENV. |
| 512 self.environ['S3_USE_SIGV4'] = True |
| 513 fake = FakeS3Connection(host='s3.amazonaws.com') |
| 514 self.assertEqual(fake._required_auth_capability(), ['hmac-v4-s3']) |
| 515 |
| 516 |
| 517 class TestSigV4OptIn(MockServiceWithConfigTestCase): |
| 518 connection_class = FakeEC2Connection |
| 519 |
| 520 def setUp(self): |
| 521 super(TestSigV4OptIn, self).setUp() |
| 522 self.standard_region = RegionInfo( |
| 523 name='us-west-2', |
| 524 endpoint='ec2.us-west-2.amazonaws.com' |
| 525 ) |
| 526 self.sigv4_region = RegionInfo( |
| 527 name='cn-north-1', |
| 528 endpoint='ec2.cn-north-1.amazonaws.com.cn' |
| 529 ) |
| 530 |
| 531 def test_sigv4_opt_out(self): |
| 532 # Default is opt-out. |
| 533 fake = FakeEC2Connection(region=self.standard_region) |
| 534 self.assertEqual(fake._required_auth_capability(), ['nope']) |
| 535 |
| 536 def test_sigv4_non_optional(self): |
| 537 # Requires SigV4. |
| 538 fake = FakeEC2Connection(region=self.sigv4_region) |
| 539 self.assertEqual(fake._required_auth_capability(), ['hmac-v4']) |
| 540 |
| 541 def test_sigv4_opt_in_config(self): |
| 542 # Opt-in via the config. |
| 543 self.config = { |
| 544 'ec2': { |
| 545 'use-sigv4': True, |
| 546 }, |
| 547 } |
| 548 fake = FakeEC2Connection(region=self.standard_region) |
| 549 self.assertEqual(fake._required_auth_capability(), ['hmac-v4']) |
| 550 |
| 551 def test_sigv4_opt_in_env(self): |
| 552 # Opt-in via the ENV. |
| 553 self.environ['EC2_USE_SIGV4'] = True |
| 554 fake = FakeEC2Connection(region=self.standard_region) |
| 555 self.assertEqual(fake._required_auth_capability(), ['hmac-v4']) |
| OLD | NEW |