| OLD | NEW |
| 1 # Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved | 1 # Copyright (c) 2013 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 mock |
| 23 import time |
| 24 |
| 22 from tests.unit import unittest | 25 from tests.unit import unittest |
| 23 from tests.unit import AWSMockServiceTestCase | 26 from tests.unit import AWSMockServiceTestCase |
| 27 from tests.unit import MockServiceWithConfigTestCase |
| 24 | 28 |
| 25 from boto.s3.connection import S3Connection | 29 from boto.s3.connection import S3Connection, HostRequiredError |
| 30 from boto.s3.connection import S3ResponseError, Bucket |
| 26 | 31 |
| 27 | 32 |
| 28 class TestSignatureAlteration(AWSMockServiceTestCase): | 33 class TestSignatureAlteration(AWSMockServiceTestCase): |
| 29 connection_class = S3Connection | 34 connection_class = S3Connection |
| 30 | 35 |
| 31 def test_unchanged(self): | 36 def test_unchanged(self): |
| 32 self.assertEqual( | 37 self.assertEqual( |
| 33 self.service_connection._required_auth_capability(), | 38 self.service_connection._required_auth_capability(), |
| 34 ['s3'] | 39 ['s3'] |
| 35 ) | 40 ) |
| 36 | 41 |
| 37 def test_switched(self): | 42 def test_switched(self): |
| 38 conn = self.connection_class( | 43 conn = self.connection_class( |
| 39 aws_access_key_id='less', | 44 aws_access_key_id='less', |
| 40 aws_secret_access_key='more', | 45 aws_secret_access_key='more', |
| 41 host='s3.cn-north-1.amazonaws.com.cn' | 46 host='s3.cn-north-1.amazonaws.com.cn' |
| 42 ) | 47 ) |
| 43 self.assertEqual( | 48 self.assertEqual( |
| 44 conn._required_auth_capability(), | 49 conn._required_auth_capability(), |
| 45 ['hmac-v4-s3'] | 50 ['hmac-v4-s3'] |
| 46 ) | 51 ) |
| 47 | 52 |
| 48 | 53 |
| 54 class TestSigV4HostError(MockServiceWithConfigTestCase): |
| 55 connection_class = S3Connection |
| 56 |
| 57 def test_historical_behavior(self): |
| 58 self.assertEqual( |
| 59 self.service_connection._required_auth_capability(), |
| 60 ['s3'] |
| 61 ) |
| 62 self.assertEqual(self.service_connection.host, 's3.amazonaws.com') |
| 63 |
| 64 def test_sigv4_opt_in(self): |
| 65 # Switch it at the config, so we can check to see how the host is |
| 66 # handled. |
| 67 self.config = { |
| 68 's3': { |
| 69 'use-sigv4': True, |
| 70 } |
| 71 } |
| 72 |
| 73 with self.assertRaises(HostRequiredError): |
| 74 # No host+SigV4 == KABOOM |
| 75 self.connection_class( |
| 76 aws_access_key_id='less', |
| 77 aws_secret_access_key='more' |
| 78 ) |
| 79 |
| 80 # Ensure passing a ``host`` still works. |
| 81 conn = self.connection_class( |
| 82 aws_access_key_id='less', |
| 83 aws_secret_access_key='more', |
| 84 host='s3.cn-north-1.amazonaws.com.cn' |
| 85 ) |
| 86 self.assertEqual( |
| 87 conn._required_auth_capability(), |
| 88 ['hmac-v4-s3'] |
| 89 ) |
| 90 self.assertEqual( |
| 91 conn.host, |
| 92 's3.cn-north-1.amazonaws.com.cn' |
| 93 ) |
| 94 |
| 95 |
| 96 class TestSigV4Presigned(MockServiceWithConfigTestCase): |
| 97 connection_class = S3Connection |
| 98 |
| 99 def test_sigv4_presign(self): |
| 100 self.config = { |
| 101 's3': { |
| 102 'use-sigv4': True, |
| 103 } |
| 104 } |
| 105 |
| 106 conn = self.connection_class( |
| 107 aws_access_key_id='less', |
| 108 aws_secret_access_key='more', |
| 109 host='s3.amazonaws.com' |
| 110 ) |
| 111 |
| 112 # Here we force an input iso_date to ensure we always get the |
| 113 # same signature. |
| 114 url = conn.generate_url_sigv4(86400, 'GET', bucket='examplebucket', |
| 115 key='test.txt', iso_date='20140625T000000Z') |
| 116 |
| 117 self.assertIn('a937f5fbc125d98ac8f04c49e0204ea1526a7b8ca058000a54c192457
be05b7d', url) |
| 118 |
| 119 def test_sigv4_presign_optional_params(self): |
| 120 self.config = { |
| 121 's3': { |
| 122 'use-sigv4': True, |
| 123 } |
| 124 } |
| 125 |
| 126 conn = self.connection_class( |
| 127 aws_access_key_id='less', |
| 128 aws_secret_access_key='more', |
| 129 security_token='token', |
| 130 host='s3.amazonaws.com' |
| 131 ) |
| 132 |
| 133 url = conn.generate_url_sigv4(86400, 'GET', bucket='examplebucket', |
| 134 key='test.txt', version_id=2) |
| 135 |
| 136 self.assertIn('VersionId=2', url) |
| 137 self.assertIn('X-Amz-Security-Token=token', url) |
| 138 |
| 139 |
| 49 class TestUnicodeCallingFormat(AWSMockServiceTestCase): | 140 class TestUnicodeCallingFormat(AWSMockServiceTestCase): |
| 50 connection_class = S3Connection | 141 connection_class = S3Connection |
| 51 | 142 |
| 52 def default_body(self): | 143 def default_body(self): |
| 53 return """<?xml version="1.0" encoding="UTF-8"?> | 144 return """<?xml version="1.0" encoding="UTF-8"?> |
| 54 <ListAllMyBucketsResult xmlns="http://doc.s3.amazonaws.com/2006-03-01"> | 145 <ListAllMyBucketsResult xmlns="http://doc.s3.amazonaws.com/2006-03-01"> |
| 55 <Owner> | 146 <Owner> |
| 56 <ID>bcaf1ffd86f461ca5fb16fd081034f</ID> | 147 <ID>bcaf1ffd86f461ca5fb16fd081034f</ID> |
| 57 <DisplayName>webfile</DisplayName> | 148 <DisplayName>webfile</DisplayName> |
| 58 </Owner> | 149 </Owner> |
| (...skipping 12 matching lines...) Expand all Loading... |
| 71 def create_service_connection(self, **kwargs): | 162 def create_service_connection(self, **kwargs): |
| 72 kwargs['calling_format'] = u'boto.s3.connection.OrdinaryCallingFormat' | 163 kwargs['calling_format'] = u'boto.s3.connection.OrdinaryCallingFormat' |
| 73 return super(TestUnicodeCallingFormat, | 164 return super(TestUnicodeCallingFormat, |
| 74 self).create_service_connection(**kwargs) | 165 self).create_service_connection(**kwargs) |
| 75 | 166 |
| 76 def test_unicode_calling_format(self): | 167 def test_unicode_calling_format(self): |
| 77 self.set_http_response(status_code=200) | 168 self.set_http_response(status_code=200) |
| 78 self.service_connection.get_all_buckets() | 169 self.service_connection.get_all_buckets() |
| 79 | 170 |
| 80 | 171 |
| 172 class TestHeadBucket(AWSMockServiceTestCase): |
| 173 connection_class = S3Connection |
| 174 |
| 175 def default_body(self): |
| 176 # HEAD requests always have an empty body. |
| 177 return "" |
| 178 |
| 179 def test_head_bucket_success(self): |
| 180 self.set_http_response(status_code=200) |
| 181 buck = self.service_connection.head_bucket('my-test-bucket') |
| 182 self.assertTrue(isinstance(buck, Bucket)) |
| 183 self.assertEqual(buck.name, 'my-test-bucket') |
| 184 |
| 185 def test_head_bucket_forbidden(self): |
| 186 self.set_http_response(status_code=403) |
| 187 |
| 188 with self.assertRaises(S3ResponseError) as cm: |
| 189 self.service_connection.head_bucket('cant-touch-this') |
| 190 |
| 191 err = cm.exception |
| 192 self.assertEqual(err.status, 403) |
| 193 self.assertEqual(err.error_code, 'AccessDenied') |
| 194 self.assertEqual(err.message, 'Access Denied') |
| 195 |
| 196 def test_head_bucket_notfound(self): |
| 197 self.set_http_response(status_code=404) |
| 198 |
| 199 with self.assertRaises(S3ResponseError) as cm: |
| 200 self.service_connection.head_bucket('totally-doesnt-exist') |
| 201 |
| 202 err = cm.exception |
| 203 self.assertEqual(err.status, 404) |
| 204 self.assertEqual(err.error_code, 'NoSuchBucket') |
| 205 self.assertEqual(err.message, 'The specified bucket does not exist') |
| 206 |
| 207 def test_head_bucket_other(self): |
| 208 self.set_http_response(status_code=405) |
| 209 |
| 210 with self.assertRaises(S3ResponseError) as cm: |
| 211 self.service_connection.head_bucket('you-broke-it') |
| 212 |
| 213 err = cm.exception |
| 214 self.assertEqual(err.status, 405) |
| 215 # We don't have special-cases for this error status. |
| 216 self.assertEqual(err.error_code, None) |
| 217 self.assertEqual(err.message, '') |
| 218 |
| 219 |
| 81 if __name__ == "__main__": | 220 if __name__ == "__main__": |
| 82 unittest.main() | 221 unittest.main() |
| OLD | NEW |