class BotoRequestSigner(object): def __init__(self, index_url, access_key, secret_access_key): url = urlparse.urlparse(index_url) self.boto_connection = DynamoDBConnection( host=url.hostname, port=url.port, aws_access_key_id=access_key, aws_secret_access_key=secret_access_key, is_secure=False) @staticmethod def canonical_date(headers): """Derive canonical date (ISO 8601 string) from headers if possible, or synthesize it if no usable header exists.""" iso_format = u'%Y%m%dT%H%M%SZ' http_format = u'%a, %d %b %Y %H:%M:%S GMT' def try_parse(date_string, format): if date_string is None: return None try: return datetime.strptime(date_string, format) except ValueError: return None amz_date = try_parse(headers.get(u'X-Amz-Date'), iso_format) http_date = try_parse(headers.get(u'Date'), http_format) fallback_date = datetime.utcnow() date = next(d for d in [amz_date, http_date, fallback_date] if d is not None) return date.strftime(iso_format) def build_auth_header(self, method, path, headers, body, params=None): """Construct an Authorization header, using boto.""" request = self.boto_connection.build_base_http_request(method=method, path=path, auth_path=path, headers=headers, data=body, params=params or {}) auth_handler = self.boto_connection._auth_handler timestamp = BotoRequestSigner.canonical_date(headers) request.timestamp = timestamp[0:8] request.region_name = u'us-east-1' request.service_name = u'VinylDNS' credential_scope = u'/'.join([ request.timestamp, request.region_name, request.service_name, u'aws4_request' ]) canonical_request = auth_handler.canonical_request(request) split_request = canonical_request.split('\n') if params != {} and split_request[2] == '': split_request[2] = self.generate_canonical_query_string(params) canonical_request = '\n'.join(split_request) hashed_request = sha256(canonical_request.encode(u'utf-8')).hexdigest() string_to_sign = u'\n'.join( [u'AWS4-HMAC-SHA256', timestamp, credential_scope, hashed_request]) signature = auth_handler.signature(request, string_to_sign) headers_to_sign = auth_handler.headers_to_sign(request) auth_header = u','.join([ u'AWS4-HMAC-SHA256 Credential=%s' % auth_handler.scope(request), u'SignedHeaders=%s' % auth_handler.signed_headers(headers_to_sign), u'Signature=%s' % signature ]) return auth_header @staticmethod def generate_canonical_query_string(params): """ Using in place of canonical_query_string from boto/auth.py to support POST requests with query parameters """ post_params = [] for param in sorted(params): value = params[param].encode('utf-8') import urllib try: post_params.append('%s=%s' % (urllib.parse.quote(param, safe='-_.~'), urllib.parse.quote(value, safe='-_.~'))) except: post_params.append('%s=%s' % (urllib.quote( param, safe='-_.~'), urllib.quote(value, safe='-_.~'))) return '&'.join(post_params)
class BotoRequestSigner(object): """TODO: Add class docstring.""" def __init__(self, index_url, access_key, secret_access_key): """TODO: Add method docstring.""" url = urlparse.urlparse(index_url) self.boto_connection = DynamoDBConnection( host=url.hostname, port=url.port, aws_access_key_id=access_key, aws_secret_access_key=secret_access_key, is_secure=False) @staticmethod def canonical_date(headers): """ Derive canonical date (ISO 8601 string). Either from headers (if possible) or synthesize it if no usable header exists. """ iso_format = u'%Y%m%dT%H%M%SZ' http_format = u'%a, %d %b %Y %H:%M:%S GMT' def try_parse(date_string, format): if date_string is None: return None try: return datetime.strptime(date_string, format) except ValueError: return None amz_date = try_parse(headers.get(u'X-Amz-Date'), iso_format) http_date = try_parse(headers.get(u'Date'), http_format) fallback_date = datetime.utcnow() date = next(d for d in [amz_date, http_date, fallback_date] if d is not None) return date.strftime(iso_format) def build_auth_header(self, method, path, headers, body, params=None): """Construct an Authorization header, using boto.""" request = self.boto_connection.build_base_http_request(method=method, path=path, auth_path=path, headers=headers, data=body, params=params or {}) auth_handler = self.boto_connection._auth_handler timestamp = BotoRequestSigner.canonical_date(headers) request.timestamp = timestamp[0:8] request.region_name = u'us-east-1' request.service_name = u'VinylDNS' credential_scope = u'/'.join([ request.timestamp, request.region_name, request.service_name, u'aws4_request' ]) canonical_request = auth_handler.canonical_request(request) hashed_request = sha256(canonical_request.encode(u'utf-8')).hexdigest() string_to_sign = u'\n'.join( [u'AWS4-HMAC-SHA256', timestamp, credential_scope, hashed_request]) signature = auth_handler.signature(request, string_to_sign) headers_to_sign = auth_handler.headers_to_sign(request) auth_header = u','.join([ u'AWS4-HMAC-SHA256 Credential=%s' % auth_handler.scope(request), u'SignedHeaders=%s' % auth_handler.signed_headers(headers_to_sign), u'Signature=%s' % signature ]) return auth_header