From ac0d0698b9487e1c5c659efe0ad1f6b4e7ca8fb9 Mon Sep 17 00:00:00 2001 From: Yohannes-B Date: Sat, 20 Jul 2024 15:00:53 -0400 Subject: [PATCH] Additional clean up and doc update --- docs/backends/gcloud.rst | 2 ++ storages/backends/gcloud.py | 44 +++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/docs/backends/gcloud.rst b/docs/backends/gcloud.rst index 83ce9cf2..328d3b9d 100644 --- a/docs/backends/gcloud.rst +++ b/docs/backends/gcloud.rst @@ -61,6 +61,8 @@ you **MUST** use Cloud IAM sign function (SignBlob) to sign data and directly si Luckily this can be worked around by passing `service_account_email` and `access_token` to the generate_signed_url function. When both of those args are provided, generate_signed_url will use the IAM SignBlob API to sign the url and no private key file is needed. +In order to enable this, use setting `iam_blob_sign` and the optional `sa_email` (if providing a service account email different than the one attached +to GCP Environment). Last resort you can still use the service account key file for authentication (not recommended by Google): diff --git a/storages/backends/gcloud.py b/storages/backends/gcloud.py index 3e4efdf3..b00a3cbb 100644 --- a/storages/backends/gcloud.py +++ b/storages/backends/gcloud.py @@ -153,13 +153,7 @@ def get_default_settings(self): @property def client(self): if self._client is None: - if self.project_id is None or self.credentials is None: - self.credentials, self.project_id = auth.default( - scopes=['https://www.googleapis.com/auth/cloud-platform'] - ) self._client = Client(project=self.project_id, credentials=self.credentials) - if self.credentials and self.credentials.token_state != TokenState.FRESH: - self.credentials.refresh(requests.Request()) return self._client @property @@ -344,20 +338,36 @@ def url(self, name, parameters=None): params = parameters or {} if self.iam_sign_blob: - if not hasattr(self.credentials, "service_account_email") and not self.sa_email: - raise AttributeError( - "Sign Blob API requires service_account_email to be available " - "through ADC or setting `sa_email`" - ) - if hasattr(self.credentials, "service_account_email"): - default_params["service_account_email"] = self.credentials.service_account_email - # sa_email has the final say of which service_account_email to be used for signing if provided - if self.sa_email: - default_params["service_account_email"] = self.sa_email - default_params["access_token"] = self.credentials.token + service_account_email, access_token = self._get_iam_sign_blob_params() + default_params["service_account_email"] = service_account_email + default_params["access_token"] = access_token for key, value in default_params.items(): if value and key not in params: params[key] = value return blob.generate_signed_url(**params) + + def _get_iam_sign_blob_params(self): + credentials, _ = auth.default( + scopes=['https://www.googleapis.com/auth/cloud-platform'] + ) + if credentials and credentials.token_state != TokenState.FRESH: + credentials.refresh(requests.Request()) + + try: + service_account_email = credentials.service_account_email + except AttributeError: + service_account_email = None + + # sa_email has the final say of which service_account_email to be used for signing if provided + if self.sa_email: + service_account_email = self.sa_email + + if not service_account_email: + raise AttributeError( + "Sign Blob API requires service_account_email to be available " + "through ADC or setting `sa_email`" + ) + + return service_account_email, credentials.token