1818import collections
1919import datetime
2020import hashlib
21- import re
2221import json
2322
2423import six
3130
3231
3332NOW = datetime .datetime .utcnow # To be replaced by tests.
34- MULTIPLE_SPACES_RE = r"\s+"
35- MULTIPLE_SPACES = re .compile (MULTIPLE_SPACES_RE )
3633
3734SERVICE_ACCOUNT_URL = (
3835 "https://googleapis.dev/python/google-api-core/latest/"
@@ -192,7 +189,7 @@ def get_canonical_headers(headers):
192189 normalized = collections .defaultdict (list )
193190 for key , val in headers :
194191 key = key .lower ().strip ()
195- val = MULTIPLE_SPACES . sub ( " " , val .strip ())
192+ val = " " . join ( val .split ())
196193 normalized [key ].append (val )
197194
198195 ordered_headers = sorted ((key , "," .join (val )) for key , val in normalized .items ())
@@ -206,8 +203,8 @@ def get_canonical_headers(headers):
206203)
207204
208205
209- def canonicalize (method , resource , query_parameters , headers ):
210- """Canonicalize method, resource
206+ def canonicalize_v2 (method , resource , query_parameters , headers ):
207+ """Canonicalize method, resource per the V2 spec.
211208
212209 :type method: str
213210 :param method: The HTTP verb that will be used when requesting the URL.
@@ -301,6 +298,7 @@ def generate_signed_url_v2(
301298 :type resource: str
302299 :param resource: A pointer to a specific resource
303300 (typically, ``/bucket-name/path/to/blob.txt``).
301+ Caller should have already URL-encoded the value.
304302
305303 :type expiration: Union[Integer, datetime.datetime, datetime.timedelta]
306304 :param expiration: Point in time when the signed URL should expire.
@@ -368,7 +366,7 @@ def generate_signed_url_v2(
368366 """
369367 expiration_stamp = get_expiration_seconds_v2 (expiration )
370368
371- canonical = canonicalize (method , resource , query_parameters , headers )
369+ canonical = canonicalize_v2 (method , resource , query_parameters , headers )
372370
373371 # Generate the string to sign.
374372 elements_to_sign = [
@@ -462,6 +460,7 @@ def generate_signed_url_v4(
462460 :type resource: str
463461 :param resource: A pointer to a specific resource
464462 (typically, ``/bucket-name/path/to/blob.txt``).
463+ Caller should have already URL-encoded the value.
465464
466465 :type expiration: Union[Integer, datetime.datetime, datetime.timedelta]
467466 :param expiration: Point in time when the signed URL should expire.
@@ -589,13 +588,20 @@ def generate_signed_url_v4(
589588 ordered_query_parameters = sorted (query_parameters .items ())
590589 canonical_query_string = six .moves .urllib .parse .urlencode (ordered_query_parameters )
591590
591+ lowercased_headers = dict (ordered_headers )
592+
593+ if "x-goog-content-sha256" in lowercased_headers :
594+ payload = lowercased_headers ["x-goog-content-sha256" ]
595+ else :
596+ payload = "UNSIGNED-PAYLOAD"
597+
592598 canonical_elements = [
593599 method ,
594600 resource ,
595601 canonical_query_string ,
596602 canonical_header_string ,
597603 signed_headers ,
598- "UNSIGNED-PAYLOAD" ,
604+ payload ,
599605 ]
600606 canonical_request = "\n " .join (canonical_elements )
601607
0 commit comments