Sign a request with Signature V4.
| 218 | |
| 219 | |
| 220 | class SigV4Auth(BaseSigner): |
| 221 | """ |
| 222 | Sign a request with Signature V4. |
| 223 | """ |
| 224 | |
| 225 | REQUIRES_REGION = True |
| 226 | |
| 227 | def __init__(self, credentials, service_name, region_name): |
| 228 | self.credentials = credentials |
| 229 | # We initialize these value here so the unit tests can have |
| 230 | # valid values. But these will get overriden in ``add_auth`` |
| 231 | # later for real requests. |
| 232 | self._region_name = region_name |
| 233 | self._service_name = service_name |
| 234 | |
| 235 | def _sign(self, key, msg, hex=False): |
| 236 | if hex: |
| 237 | sig = hmac.new(key, msg.encode('utf-8'), sha256).hexdigest() |
| 238 | else: |
| 239 | sig = hmac.new(key, msg.encode('utf-8'), sha256).digest() |
| 240 | return sig |
| 241 | |
| 242 | def headers_to_sign(self, request): |
| 243 | """ |
| 244 | Select the headers from the request that need to be included |
| 245 | in the StringToSign. |
| 246 | """ |
| 247 | header_map = HTTPHeaders() |
| 248 | for name, value in request.headers.items(): |
| 249 | lname = name.lower() |
| 250 | if lname not in SIGNED_HEADERS_BLACKLIST: |
| 251 | header_map[lname] = value |
| 252 | if 'host' not in header_map: |
| 253 | # TODO: We should set the host ourselves, instead of relying on our |
| 254 | # HTTP client to set it for us. |
| 255 | header_map['host'] = _host_from_url(request.url) |
| 256 | return header_map |
| 257 | |
| 258 | def canonical_query_string(self, request): |
| 259 | # The query string can come from two parts. One is the |
| 260 | # params attribute of the request. The other is from the request |
| 261 | # url (in which case we have to re-split the url into its components |
| 262 | # and parse out the query string component). |
| 263 | if request.params: |
| 264 | return self._canonical_query_string_params(request.params) |
| 265 | else: |
| 266 | return self._canonical_query_string_url(urlsplit(request.url)) |
| 267 | |
| 268 | def _canonical_query_string_params(self, params): |
| 269 | # [(key, value), (key2, value2)] |
| 270 | key_val_pairs = [] |
| 271 | for key in params: |
| 272 | value = str(params[key]) |
| 273 | key_val_pairs.append( |
| 274 | (quote(key, safe='-_.~'), quote(value, safe='-_.~')) |
| 275 | ) |
| 276 | sorted_key_vals = [] |
| 277 | # Sort by the URI-encoded key names, and in the case of |
no outgoing calls