CloudVE/cloudbridge

'AccessTokenCredentials' object has no attribute 'sign_blob'

VJalili opened this issue · 10 comments

Use the following to reproduce:

from cloudbridge.factory import CloudProviderFactory, ProviderList
config = {"gcp_credentials_obj": credentials, "gcp_service_creds_dict": {"project_id": "..."}}
provider = CloudProviderFactory().create_provider(ProviderList.GCP, config)
bucket = provider.storage.buckets.get("...")
key = bucket.objects.get("...")
signed_url = key.generate_url(expires_in=3600)

where the credentials object is of type oauth2client.client.AccessTokenCredentials. Calling the generate_url method raises an exception with the following traceback:

Traceback (most recent call last):
  File "gcp.py", line 42, in <module>
    signed_url = key.generate_url(expires_in=3600)
  File "../cloudbridge/providers/gcp/resources.py", line 1985, in generate_url
    (expiration, self._obj['bucket'], self.name))
  File "../cloudbridge/providers/gcp/provider.py", line 314, in sign_blob
    return self._credentials.sign_blob(string_to_sign)[1]
AttributeError: 'AccessTokenCredentials' object has no attribute 'sign_blob'

@nuwang what is the expected type of self._credentials here? AssertionCredentials?!

If so, it seems both AssertionCredentials and AccessTokenCredentials inherit from OAuth2Credentials:

Object -> Credentials -> OAuth2Credentials -> GoogleCredentials -> AssertionCredentials

Object -> Credentials -> OAuth2Credentials -> AccessTokenCredentials

Type used by CloudBridge currently: oauth2client.service_account.ServiceAccountCredentials

  • ServiceAccountCredentials type inherits from AssertionCredentials;
  • signing access to X using this method requires credentials files of a service account that has access to X, but we do not such a credentials file with the authorization flow we're using. Hence, with the blob signed using the linked method, you get Access denied error. In other words, that approach requires RSA private key of a service account that has access to X, we the authorization flow we're using, we do not have such a RSA private key.

What do you think of this?
https://google-auth.readthedocs.io/en/latest/_modules/google/auth/iam.html

As the docs say in that file say: "This is useful when you need to sign bytes but do not have access to the credential's private key file."

It calls a rest api endpoint to sign the string:
https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/signBlob

Would this be an option?

Do you have an example of how this can be used?

Additionally, looking into related libraries, it seems if we can initialize a signer using access token, then the rest should be straightforward. The question is how to initialize a signer using access token? I found the following method but its not clear to me how it works:

https://gist.github.com/frankyn/1a537900c0689903f157740a0a9e36aa#file-v4_signed_urls-rb-L60

How did you make that work? I don't see how access token is passed there, are you using an environment variable?

Also, I suspect this is using default credentials types (similar to the default method of initializing GCP backend of cloudbridge) and not access token or AccessTokenCredentials.

oauth2client was recently deprecated in favor of this [google-auth] library
REF

Both ServiceAccountCredentials (that CloudBridge uses) and AccessTokenCredentials (that CloudAuthz generates) are part of this now deprecated library. I guess with that we need to revisit both cloudauthz and cloudbridge and update them to use google-auth.

Switching to the new library, we can use google.oauth2.service_account.Credentials type instead of oauth2client.client.AccessTokenCredentials, which implements a signer method that returns a singer that can be used for signing blobs (how to sign blobs using a signer is another question, but it is one step in the right direction).

This is sorted I guess, so closing.