Skip to content

Commit 3e69bf9

Browse files
guillaumeblaquieretseaverfrankynFrank Natividad
authored
fix: allow signed url version v4 without signed credentials (#356)
* Fix: signed_url_v4 to accept credentials without private key. Preserve checks for efficiency in case of neither access_token nor service_account_email are provided. Fix: tests v4 with token to take into account not Signing credential class. * fix typo: 2 new lines before new test class * fix doc: Improve docstring to explain the use of the access_token AND the service_account_email, or the signer email. * fix: test coverage with the new IF branch * lint Co-authored-by: Tres Seaver <[email protected]> Co-authored-by: Frank Natividad <[email protected]> Co-authored-by: Frank Natividad <[email protected]>
1 parent f44212b commit 3e69bf9

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
lines changed

google/cloud/storage/_signing.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -457,9 +457,12 @@ def generate_signed_url_v4(
457457
google-cloud-python/issues/922
458458
.. _reference: https://cloud.google.com/storage/docs/reference-headers
459459
460+
460461
:type credentials: :class:`google.auth.credentials.Signing`
461462
:param credentials: Credentials object with an associated private key to
462-
sign text.
463+
sign text. That credentials must provide signer_email
464+
only if service_account_email and access_token are not
465+
passed.
463466
464467
:type resource: str
465468
:param resource: A pointer to a specific resource
@@ -533,7 +536,6 @@ def generate_signed_url_v4(
533536
:returns: A signed URL you can use to access the resource
534537
until expiration.
535538
"""
536-
ensure_signed_credentials(credentials)
537539
expiration_seconds = get_expiration_seconds_v4(expiration)
538540

539541
if _request_timestamp is None:
@@ -542,7 +544,11 @@ def generate_signed_url_v4(
542544
request_timestamp = _request_timestamp
543545
datestamp = _request_timestamp[:8]
544546

545-
client_email = credentials.signer_email
547+
client_email = service_account_email
548+
if not access_token or not service_account_email:
549+
ensure_signed_credentials(credentials)
550+
client_email = credentials.signer_email
551+
546552
credential_scope = "{}/auto/storage/goog4_request".format(datestamp)
547553
credential = "{}/{}".format(client_email, credential_scope)
548554

tests/unit/test__signing.py

+49-1
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,22 @@ def test_w_custom_query_parameters_w_string_value(self):
653653
def test_w_custom_query_parameters_w_none_value(self):
654654
self._generate_helper(query_parameters={"qux": None})
655655

656-
def test_with_access_token(self):
656+
def test_with_access_token_and_service_account_email(self):
657+
resource = "/name/path"
658+
credentials = _make_credentials()
659+
email = mock.sentinel.service_account_email
660+
with mock.patch(
661+
"google.cloud.storage._signing._sign_message", return_value=b"DEADBEEF"
662+
):
663+
self._call_fut(
664+
credentials,
665+
resource=resource,
666+
expiration=datetime.timedelta(days=5),
667+
service_account_email=email,
668+
access_token="token",
669+
)
670+
671+
def test_with_access_token_and_service_account_email_and_signer_email(self):
657672
resource = "/name/path"
658673
signer_email = "[email protected]"
659674
credentials = _make_credentials(signer_email=signer_email)
@@ -668,6 +683,39 @@ def test_with_access_token(self):
668683
access_token="token",
669684
)
670685

686+
def test_with_signer_email(self):
687+
resource = "/name/path"
688+
signer_email = "[email protected]"
689+
credentials = _make_credentials(signer_email=signer_email)
690+
credentials.sign_bytes.return_value = b"DEADBEEF"
691+
self._call_fut(
692+
credentials, resource=resource, expiration=datetime.timedelta(days=5),
693+
)
694+
695+
def test_with_service_account_email_and_signer_email(self):
696+
resource = "/name/path"
697+
signer_email = "[email protected]"
698+
credentials = _make_credentials(signer_email=signer_email)
699+
credentials.sign_bytes.return_value = b"DEADBEEF"
700+
self._call_fut(
701+
credentials,
702+
resource=resource,
703+
expiration=datetime.timedelta(days=5),
704+
service_account_email=signer_email,
705+
)
706+
707+
def test_with_token_and_signer_email(self):
708+
resource = "/name/path"
709+
signer_email = "[email protected]"
710+
credentials = _make_credentials(signer_email=signer_email)
711+
credentials.sign_bytes.return_value = b"DEADBEEF"
712+
self._call_fut(
713+
credentials,
714+
resource=resource,
715+
expiration=datetime.timedelta(days=5),
716+
access_token="token",
717+
)
718+
671719

672720
class Test_sign_message(unittest.TestCase):
673721
@staticmethod

0 commit comments

Comments
 (0)