def test_s3_sigv4_presign(self): auth = botocore.auth.S3SigV4QueryAuth(self.credentials, self.service_name, self.region_name, expires=60) request = AWSRequest() request.url = ( 'https://s3.us-west-2.amazonaws.com/mybucket/keyname/.bar') auth.add_auth(request) query_string = self.get_parsed_query_string(request) # We use a different payload: self.assertEqual(auth.payload(request), 'UNSIGNED-PAYLOAD') # which will result in a different X-Amz-Signature: self.assertEqual( query_string, { 'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', 'X-Amz-Credential': ('access_key/20140101/myregion/' 'myservice/aws4_request'), 'X-Amz-Date': '20140101T000000Z', 'X-Amz-Expires': '60', 'X-Amz-Signature': ('ac1b8b9e47e8685c5c963d75e35e8741d55251' 'cd955239cc1efad4dc7201db66'), 'X-Amz-SignedHeaders': 'host' })
def sign(self, operation_name, request, region_name=None, signing_type='standard', expires_in=None): """Sign a request before it goes out over the wire. :type operation_name: string :param operation_name: The name of the current operation, e.g. ``ListBuckets``. :type request: AWSRequest :param request: The request object to be sent over the wire. :type region_name: str :param region_name: The region to sign the request for. :type signing_type: str :param signing_type: The type of signing to perform. This can be one of three possible values: * 'standard' - This should be used for most requests. * 'presign-url' - This should be used when pre-signing a request. * 'presign-post' - This should be used when pre-signing an S3 post. :type expires_in: int :param expires_in: The number of seconds the presigned url is valid for. This parameter is only valid for signing type 'presign-url'. """ if region_name is None: region_name = self._region_name signature_version = self._choose_signer(operation_name, signing_type) # Allow mutating request before signing self._event_emitter.emit( 'before-sign.{0}.{1}'.format(self._service_name, operation_name), request=request, signing_name=self._signing_name, region_name=self._region_name, signature_version=signature_version, request_signer=self) if signature_version != botocore.UNSIGNED: kwargs = { 'signing_name': self._signing_name, 'region_name': region_name, 'signature_version': signature_version } if expires_in is not None: kwargs['expires'] = expires_in try: auth = self.get_auth_instance(**kwargs) except UnknownSignatureVersionError as e: if signing_type != 'standard': raise UnsupportedSignatureVersionError( signature_version=signature_version) else: raise e auth.add_auth(request)
def _test_blacklist_header(self, header, value): request = AWSRequest() request.url = 'https://s3.amazonaws.com/bucket/foo' request.method = 'PUT' request.headers[header] = value credentials = botocore.credentials.Credentials('access_key', 'secret_key') auth = botocore.auth.S3SigV4Auth(credentials, 's3', 'us-east-1') auth.add_auth(request) self.assertNotIn(header, request.headers['Authorization'])
def test_content_sha256_set_if_payload_signing_disabled(self): request = AWSRequest() request.data = six.BytesIO(u'\u2713'.encode('utf-8')) request.url = 'https://amazonaws.com' request.context['payload_signing_enabled'] = False request.method = 'PUT' auth = self.create_signer() auth.add_auth(request) sha_header = request.headers['X-Amz-Content-SHA256'] self.assertEqual(sha_header, 'UNSIGNED-PAYLOAD')
def test_presign_with_security_token(self): self.credentials.token = 'security-token' auth = botocore.auth.S3SigV4QueryAuth( self.credentials, self.service_name, self.region_name, expires=60) request = AWSRequest() request.url = 'https://ec2.us-east-1.amazonaws.com/' auth.add_auth(request) query_string = self.get_parsed_query_string(request) self.assertEqual( query_string['X-Amz-Security-Token'], 'security-token')
def test_signature_is_not_normalized(self): request = AWSRequest() request.url = 'https://s3.amazonaws.com/bucket/foo/./bar/../bar' request.method = 'GET' credentials = botocore.credentials.Credentials('access_key', 'secret_key') auth = botocore.auth.S3SigV4Auth(credentials, 's3', 'us-east-1') auth.add_auth(request) self.assertTrue( request.headers['Authorization'].startswith('AWS4-HMAC-SHA256'))
def test_blacklist_expect_headers(self): request = AWSRequest() request.url = 'https://s3.amazonaws.com/bucket/foo' request.method = 'PUT' request.headers['expect'] = '100-Continue' credentials = botocore.credentials.Credentials('access_key', 'secret_key') auth = botocore.auth.S3SigV4Auth(credentials, 's3', 'us-east-1') auth.add_auth(request) self.assertNotIn('expect', request.headers['Authorization'])
def test_presign_with_security_token(self): self.credentials.token = 'security-token' auth = botocore.auth.S3SigV4QueryAuth( self.credentials, self.service_name, self.region_name, expires=60) request = AWSRequest() request.method = 'GET' request.url = 'https://ec2.us-east-1.amazonaws.com/' auth.add_auth(request) query_string = self.get_parsed_query_string(request) self.assertEqual( query_string['X-Amz-Security-Token'], 'security-token')
def _test_crt_signature_version_4(test_case): test_case = _SignatureTestCase(test_case) request = create_request_from_raw_request(test_case.raw_request) # Use CRT logging to diagnose interim steps (canonical request, etc) # import awscrt.io # awscrt.io.init_logging(awscrt.io.LogLevel.Trace, 'stdout') auth = botocore.crt.auth.CrtSigV4Auth(test_case.credentials, SERVICE, REGION) auth.add_auth(request) actual_auth_header = request.headers['Authorization'] assert_equal(actual_auth_header, test_case.authorization_header, test_case.raw_request, 'authheader')
async def sign(self, operation_name, request, region_name=None, signing_type='standard', expires_in=None, signing_name=None): explicit_region_name = region_name if region_name is None: region_name = self._region_name if signing_name is None: signing_name = self._signing_name signature_version = await self._choose_signer(operation_name, signing_type, request.context) # Allow mutating request before signing await self._event_emitter.emit('before-sign.{0}.{1}'.format( self._service_id.hyphenize(), operation_name), request=request, signing_name=signing_name, region_name=self._region_name, signature_version=signature_version, request_signer=self, operation_name=operation_name) if signature_version != botocore.UNSIGNED: kwargs = { 'signing_name': signing_name, 'region_name': region_name, 'signature_version': signature_version } if expires_in is not None: kwargs['expires'] = expires_in signing_context = request.context.get('signing', {}) if not explicit_region_name and signing_context.get('region'): kwargs['region_name'] = signing_context['region'] if signing_context.get('signing_name'): kwargs['signing_name'] = signing_context['signing_name'] try: auth = await self.get_auth_instance(**kwargs) except UnknownSignatureVersionError as e: if signing_type != 'standard': raise UnsupportedSignatureVersionError( signature_version=signature_version) else: raise e auth.add_auth(request)
def test_sign_with_token(self): credentials = botocore.credentials.Credentials( access_key='foo', secret_key='bar', token='baz') auth = botocore.auth.HmacV1Auth(credentials) request = AWSRequest() request.headers['Date'] = 'Thu, 17 Nov 2005 18:49:58 GMT' request.headers['Content-Type'] = 'text/html' request.method = 'PUT' request.url = 'https://s3.amazonaws.com/bucket/key' auth.add_auth(request) self.assertIn('Authorization', request.headers) # We're not actually checking the signature here, we're # just making sure the auth header has the right format. self.assertTrue(request.headers['Authorization'].startswith('AWS '))
def generate_presigned_url(self, request_dict, expires_in=3600, region_name=None): """Generates a presigned url :type request_dict: dict :param request_dict: The prepared request dictionary returned by ``botocore.awsrequest.prepare_request_dict()`` :type expires_in: int :param expires_in: The number of seconds the presigned url is valid for. By default it expires in an hour (3600 seconds) :type region_name: string :param region_name: The region name to sign the presigned url. :returns: The presigned url """ if region_name is None: region_name = self._region_name query_prefix = '-query' signature_version = self._signature_version if not signature_version.endswith(query_prefix): signature_version += query_prefix kwargs = { 'signing_name': self._signing_name, 'region_name': region_name, 'signature_version': signature_version, 'expires': expires_in } signature_type = signature_version.split('-', 1)[0] try: auth = self.get_auth_instance(**kwargs) except UnknownSignatureVersionError: raise UnsupportedSignatureVersionError( signature_version=signature_type) request = create_request_object(request_dict) # Fix s3 host for s3 sigv2 bucket names fix_s3_host(request, signature_type, region_name) auth.add_auth(request) request.prepare() return request.url
def _test_signature_version_4(test_case): test_case = _SignatureTestCase(test_case) request = create_request_from_raw_request(test_case.raw_request) auth = botocore.auth.SigV4Auth(test_case.credentials, "host", "us-east-1") actual_canonical_request = auth.canonical_request(request) assert_equal(actual_canonical_request, test_case.canonical_request, test_case.raw_request, "canonical_request") actual_string_to_sign = auth.string_to_sign(request, actual_canonical_request) assert_equal(actual_string_to_sign, test_case.string_to_sign, test_case.raw_request, "string_to_sign") auth.add_auth(request) actual_auth_header = request.headers["Authorization"] assert_equal(actual_auth_header, test_case.authorization_header, test_case.raw_request, "authheader")
def test_resign_with_token(self): credentials = botocore.credentials.Credentials( access_key='foo', secret_key='bar', token='baz') auth = botocore.auth.SigV3Auth(credentials) request = AWSRequest() request.headers['Date'] = 'Thu, 17 Nov 2005 18:49:58 GMT' request.method = 'PUT' request.url = 'https://route53.amazonaws.com/' auth.add_auth(request) original_auth = request.headers['X-Amzn-Authorization'] # Resigning the request shouldn't change the authorization # header. auth.add_auth(request) self.assertEqual(request.headers.get_all('X-Amzn-Authorization'), [original_auth])
def generate_presigned_url(self, request_dict, expires_in=3600, region_name=None): """Generates a presigned url :type request_dict: dict :param request_dict: The prepared request dictionary returned by ``botocore.awsrequest.prepare_request_dict()`` :type expires_in: int :param expires_in: The number of seconds the presigned url is valid for. By default it expires in an hour (3600 seconds) :type region_name: string :param region_name: The region name to sign the presigned url. :returns: The presigned url """ if region_name is None: region_name = self._region_name query_prefix = '-query' signature_version = self._signature_version if not signature_version.endswith(query_prefix): signature_version += query_prefix kwargs = {'signing_name': self._signing_name, 'region_name': region_name, 'signature_version': signature_version, 'expires': expires_in} signature_type = signature_version.split('-', 1)[0] try: auth = self.get_auth(**kwargs) except UnknownSignatureVersionError: raise UnsupportedSignatureVersionError( signature_version=signature_type) request = create_request_object(request_dict) # Fix s3 host for s3 sigv2 bucket names fix_s3_host(request, signature_type, region_name) auth.add_auth(request) request.prepare() return request.url
def test_s3_sigv4_presign(self): auth = botocore.auth.S3SigV4QueryAuth( self.credentials, self.service_name, self.region_name, expires=60) request = AWSRequest() request.url = 'https://s3.us-west-2.amazonaws.com/mybucket/keyname/.bar' auth.add_auth(request) query_string = self.get_parsed_query_string(request) # We use a different payload: self.assertEqual(auth.payload(request), 'UNSIGNED-PAYLOAD') # which will result in a different X-Amz-Signature: self.assertEqual( query_string, {'X-Amz-Algorithm': 'AWS4-HMAC-SHA256', 'X-Amz-Credential': 'access_key/20140101/myregion/myservice/aws4_request', 'X-Amz-Date': '20140101T000000Z', 'X-Amz-Expires': '60', 'X-Amz-Signature': 'ac1b8b9e47e8685c5c963d75e35e8741d55251cd955239cc1efad4dc7201db66', 'X-Amz-SignedHeaders': 'host'})
def _test_signature_version_4(test_case): test_case = _SignatureTestCase(test_case) request = create_request_from_raw_request(test_case.raw_request) auth = botocore.auth.SigV4Auth(test_case.credentials, 'host', 'us-east-1') actual_canonical_request = auth.canonical_request(request) assert_equal(actual_canonical_request, test_case.canonical_request, test_case.raw_request, 'canonical_request') actual_string_to_sign = auth.string_to_sign(request, actual_canonical_request) assert_equal(actual_string_to_sign, test_case.string_to_sign, test_case.raw_request, 'string_to_sign') auth.add_auth(request) actual_auth_header = request.headers['Authorization'] assert_equal(actual_auth_header, test_case.authorization_header, test_case.raw_request, 'authheader')
def test_resign_with_token(self): credentials = botocore.credentials.Credentials( access_key='foo', secret_key='bar', token='baz') auth = botocore.auth.HmacV1Auth(credentials) request = AWSRequest() request.headers['Date'] = 'Thu, 17 Nov 2005 18:49:58 GMT' request.headers['Content-Type'] = 'text/html' request.method = 'PUT' request.url = 'https://s3.amazonaws.com/bucket/key' auth.add_auth(request) original_auth = request.headers['Authorization'] # Resigning the request shouldn't change the authorization # header. We are also ensuring that the date stays the same # because we're mocking out the formatdate() call. There's # another unit test that verifies we use the latest time # when we sign the request. auth.add_auth(request) self.assertEqual(request.headers.get_all('Authorization'), [original_auth])
def _test_signature_version_4(test_case): test_case = _SignatureTestCase(test_case) request = create_request_from_raw_request(test_case.raw_request) auth = botocore.auth.SigV4Auth(test_case.credentials, SERVICE, REGION) actual_canonical_request = auth.canonical_request(request) actual_string_to_sign = auth.string_to_sign(request, actual_canonical_request) auth.add_auth(request) actual_auth_header = request.headers['Authorization'] # Some stuff only works right when you go through auth.add_auth() # So don't assert the interim steps unless the end result was wrong. if actual_auth_header != test_case.authorization_header: assert_equal(actual_canonical_request, test_case.canonical_request, test_case.raw_request, 'canonical_request') assert_equal(actual_string_to_sign, test_case.string_to_sign, test_case.raw_request, 'string_to_sign') assert_equal(actual_auth_header, test_case.authorization_header, test_case.raw_request, 'authheader')
def test_thread_safe_timestamp(self): request = AWSRequest() request.url = ( 'https://search-testdomain1-j67dwxlet67gf7ghwfmik2c67i.us-west-2.' 'cloudsearch.amazonaws.com/' '2013-01-01/search?format=sdk&pretty=true&' 'q.options=%7B%22defaultOperator%22%3A%20%22and%22%2C%20%22' 'fields%22%3A%5B%22directors%5E10%22%5D%7D&q=George%20Lucas' ) request.method = 'GET' auth = botocore.auth.SigV4Auth( self.credentials, 'cloudsearchdomain', 'us-west-2') with mock.patch.object( botocore.auth.datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) as mock_datetime: original_utcnow = datetime.datetime(2014, 1, 1, 0, 0) mock_datetime.utcnow.return_value = original_utcnow # Go through the add_auth process once. This will attach # a timestamp to the request at the beginning of auth. auth.add_auth(request) self.assertEqual(request.context['timestamp'], '20140101T000000Z') # Ensure the date is in the Authorization header self.assertIn('20140101', request.headers['Authorization']) # Now suppose the utc time becomes the next day all of a sudden mock_datetime.utcnow.return_value = datetime.datetime( 2014, 1, 2, 0, 0) # Smaller methods like the canonical request and string_to_sign # should have the timestamp attached to the request in their # body and not what the time is now mocked as. This is to ensure # there is no mismatching in timestamps when signing. cr = auth.canonical_request(request) self.assertIn('x-amz-date:20140101T000000Z', cr) self.assertNotIn('x-amz-date:20140102T000000Z', cr) sts = auth.string_to_sign(request, cr) self.assertIn('20140101T000000Z', sts) self.assertNotIn('20140102T000000Z', sts)
def test_thread_safe_timestamp(self): request = AWSRequest() request.url = ( 'https://search-testdomain1-j67dwxlet67gf7ghwfmik2c67i.us-west-2.' 'cloudsearch.amazonaws.com/' '2013-01-01/search?format=sdk&pretty=true&' 'q.options=%7B%22defaultOperator%22%3A%20%22and%22%2C%20%22' 'fields%22%3A%5B%22directors%5E10%22%5D%7D&q=George%20Lucas' ) request.method = 'GET' auth = self.create_signer('cloudsearchdomain', 'us-west-2') with mock.patch.object( botocore.auth.datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) as mock_datetime: original_utcnow = datetime.datetime(2014, 1, 1, 0, 0) mock_datetime.utcnow.return_value = original_utcnow # Go through the add_auth process once. This will attach # a timestamp to the request at the beginning of auth. auth.add_auth(request) self.assertEqual(request.context['timestamp'], '20140101T000000Z') # Ensure the date is in the Authorization header self.assertIn('20140101', request.headers['Authorization']) # Now suppose the utc time becomes the next day all of a sudden mock_datetime.utcnow.return_value = datetime.datetime( 2014, 1, 2, 0, 0) # Smaller methods like the canonical request and string_to_sign # should have the timestamp attached to the request in their # body and not what the time is now mocked as. This is to ensure # there is no mismatching in timestamps when signing. cr = auth.canonical_request(request) self.assertIn('x-amz-date:20140101T000000Z', cr) self.assertNotIn('x-amz-date:20140102T000000Z', cr) sts = auth.string_to_sign(request, cr) self.assertIn('20140101T000000Z', sts) self.assertNotIn('20140102T000000Z', sts)
def generate_presigned_post(self, request_dict, fields=None, conditions=None, expires_in=3600, region_name=None): """Generates the url and the form fields used for a presigned s3 post :type request_dict: dict :param request_dict: The prepared request dictionary returned by ``botocore.awsrequest.prepare_request_dict()`` :type fields: dict :param fields: A dictionary of prefilled form fields to build on top of. :type conditions: list :param conditions: A list of conditions to include in the policy. Each element can be either a list or a structure. For example: [ {"acl": "public-read"}, {"bucket": "mybucket"}, ["starts-with", "$key", "mykey"] ] :type expires_in: int :param expires_in: The number of seconds the presigned post is valid for. :type region_name: string :param region_name: The region name to sign the presigned post to. :rtype: dict :returns: A dictionary with two elements: ``url`` and ``fields``. Url is the url to post to. Fields is a dictionary filled with the form fields and respective values to use when submitting the post. For example: {'url': 'https://mybucket.s3.amazonaws.com 'fields': {'acl': 'public-read', 'key': 'mykey', 'signature': 'mysignature', 'policy': 'mybase64 encoded policy'} } """ if fields is None: fields = {} if conditions is None: conditions = [] if region_name is None: region_name = self._request_signer.region_name # Create the policy for the post. policy = {} # Create an expiration date for the policy datetime_now = datetime.datetime.utcnow() expire_date = datetime_now + datetime.timedelta(seconds=expires_in) policy['expiration'] = expire_date.strftime(botocore.auth.ISO8601) # Append all of the conditions that the user supplied. policy['conditions'] = [] for condition in conditions: policy['conditions'].append(condition) # Obtain the appropriate signer. query_prefix = '-presign-post' signature_version = self._request_signer.signature_version if not signature_version.endswith(query_prefix): signature_version += query_prefix kwargs = { 'signing_name': self._request_signer.signing_name, 'region_name': region_name, 'signature_version': signature_version } signature_type = signature_version.split('-', 1)[0] try: auth = self._request_signer.get_auth_instance(**kwargs) except UnknownSignatureVersionError: raise UnsupportedSignatureVersionError( signature_version=signature_type) # Store the policy and the fields in the request for signing request = create_request_object(request_dict) request.context['s3-presign-post-fields'] = fields request.context['s3-presign-post-policy'] = policy auth.add_auth(request) # Fix s3 host for s3 sigv2 bucket names fix_s3_host(request, signature_type, region_name) # Return the url and the fields for th form to post. return {'url': request.url, 'fields': fields}
def sign(self, operation_name, request, region_name=None, signing_type='standard', expires_in=None): """Sign a request before it goes out over the wire. :type operation_name: string :param operation_name: The name of the current operation, e.g. ``ListBuckets``. :type request: AWSRequest :param request: The request object to be sent over the wire. :type region_name: str :param region_name: The region to sign the request for. :type signing_type: str :param signing_type: The type of signing to perform. This can be one of three possible values: * 'standard' - This should be used for most requests. * 'presign-url' - This should be used when pre-signing a request. * 'presign-post' - This should be used when pre-signing an S3 post. :type expires_in: int :param expires_in: The number of seconds the presigned url is valid for. This parameter is only valid for signing type 'presign-url'. """ if region_name is None: region_name = self._region_name signature_version = self._choose_signer(operation_name, signing_type) # Allow mutating request before signing self._event_emitter.emit('before-sign.{0}.{1}'.format( self._service_name, operation_name), request=request, signing_name=self._signing_name, region_name=self._region_name, signature_version=signature_version, request_signer=self) if signature_version != botocore.UNSIGNED: kwargs = { 'signing_name': self._signing_name, 'region_name': region_name, 'signature_version': signature_version } if expires_in is not None: kwargs['expires'] = expires_in try: auth = self.get_auth_instance(**kwargs) except UnknownSignatureVersionError as e: if signing_type != 'standard': raise UnsupportedSignatureVersionError( signature_version=signature_version) else: raise e auth.add_auth(request)
def generate_presigned_post(self, request_dict, fields=None, conditions=None, expires_in=3600, region_name=None): """Generates the url and the form fields used for a presigned s3 post :type request_dict: dict :param request_dict: The prepared request dictionary returned by ``botocore.awsrequest.prepare_request_dict()`` :type fields: dict :param fields: A dictionary of prefilled form fields to build on top of. :type conditions: list :param conditions: A list of conditions to include in the policy. Each element can be either a list or a structure. For example: [ {"acl": "public-read"}, {"bucket": "mybucket"}, ["starts-with", "$key", "mykey"] ] :type expires_in: int :param expires_in: The number of seconds the presigned post is valid for. :type region_name: string :param region_name: The region name to sign the presigned post to. :rtype: dict :returns: A dictionary with two elements: ``url`` and ``fields``. Url is the url to post to. Fields is a dictionary filled with the form fields and respective values to use when submitting the post. For example: {'url': 'https://mybucket.s3.amazonaws.com 'fields': {'acl': 'public-read', 'key': 'mykey', 'signature': 'mysignature', 'policy': 'mybase64 encoded policy'} } """ if fields is None: fields = {} if conditions is None: conditions = [] if region_name is None: region_name = self._request_signer.region_name # Create the policy for the post. policy = {} # Create an expiration date for the policy datetime_now = datetime.datetime.utcnow() expire_date = datetime_now + datetime.timedelta(seconds=expires_in) policy['expiration'] = expire_date.strftime(botocore.auth.ISO8601) # Append all of the conditions that the user supplied. policy['conditions'] = [] for condition in conditions: policy['conditions'].append(condition) # Obtain the appropriate signer. query_prefix = '-presign-post' signature_version = self._request_signer.signature_version if not signature_version.endswith(query_prefix): signature_version += query_prefix kwargs = {'signing_name': self._request_signer.signing_name, 'region_name': region_name, 'signature_version': signature_version} signature_type = signature_version.split('-', 1)[0] try: auth = self._request_signer.get_auth(**kwargs) except UnknownSignatureVersionError: raise UnsupportedSignatureVersionError( signature_version=signature_type) # Store the policy and the fields in the request for signing request = create_request_object(request_dict) request.context['s3-presign-post-fields'] = fields request.context['s3-presign-post-policy'] = policy auth.add_auth(request) # Fix s3 host for s3 sigv2 bucket names fix_s3_host(request, signature_type, region_name) # Return the url and the fields for th form to post. return {'url': request.url, 'fields': fields}