diff --git a/google/auth/app_engine.py b/google/auth/app_engine.py index fae00d0b8..f1d21280e 100644 --- a/google/auth/app_engine.py +++ b/google/auth/app_engine.py @@ -77,7 +77,9 @@ def get_project_id(): return app_identity.get_application_id() -class Credentials(credentials.Scoped, credentials.Signing, credentials.Credentials): +class Credentials( + credentials.Scoped, credentials.Signing, credentials.CredentialsWithQuotaProject +): """App Engine standard environment credentials. These credentials use the App Engine App Identity API to obtain access @@ -145,7 +147,7 @@ def with_scopes(self, scopes): quota_project_id=self.quota_project_id, ) - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( scopes=self._scopes, diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py index e6da238a0..b7fca1832 100644 --- a/google/auth/compute_engine/credentials.py +++ b/google/auth/compute_engine/credentials.py @@ -32,7 +32,7 @@ from google.oauth2 import _client -class Credentials(credentials.ReadOnlyScoped, credentials.Credentials): +class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaProject): """Compute Engine Credentials. These credentials use the Google Compute Engine metadata server to obtain @@ -118,7 +118,7 @@ def requires_scopes(self): """False: Compute Engine credentials can not be scoped.""" return False - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( service_account_email=self._service_account_email, @@ -130,7 +130,7 @@ def with_quota_project(self, quota_project_id): _DEFAULT_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token" -class IDTokenCredentials(credentials.Credentials, credentials.Signing): +class IDTokenCredentials(credentials.CredentialsWithQuotaProject, credentials.Signing): """Open ID Connect ID Token-based service account credentials. These credentials relies on the default service account of a GCE instance. @@ -254,7 +254,7 @@ def with_target_audience(self, target_audience): quota_project_id=self._quota_project_id, ) - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): # since the signer is already instantiated, diff --git a/google/auth/credentials.py b/google/auth/credentials.py index 3f389b171..5ea36a0ba 100644 --- a/google/auth/credentials.py +++ b/google/auth/credentials.py @@ -133,6 +133,10 @@ def before_request(self, request, method, url, headers): self.refresh(request) self.apply(headers) + +class CredentialsWithQuotaProject(Credentials): + """Abstract base for credentials supporting ``with_quota_project`` factory""" + def with_quota_project(self, quota_project_id): """Returns a copy of these credentials with a modified quota project @@ -143,7 +147,7 @@ def with_quota_project(self, quota_project_id): Returns: google.oauth2.credentials.Credentials: A new credentials instance. """ - raise NotImplementedError("This class does not support quota project.") + raise NotImplementedError("This credential does not support quota project.") class AnonymousCredentials(Credentials): @@ -182,9 +186,6 @@ def apply(self, headers, token=None): def before_request(self, request, method, url, headers): """Anonymous credentials do nothing to the request.""" - def with_quota_project(self, quota_project_id): - raise ValueError("Anonymous credentials don't support quota project.") - @six.add_metaclass(abc.ABCMeta) class ReadOnlyScoped(object): diff --git a/google/auth/impersonated_credentials.py b/google/auth/impersonated_credentials.py index dbcb2914e..d2c5ded1c 100644 --- a/google/auth/impersonated_credentials.py +++ b/google/auth/impersonated_credentials.py @@ -115,7 +115,7 @@ def _make_iam_token_request(request, principal, headers, body): six.raise_from(new_exc, caught_exc) -class Credentials(credentials.Credentials, credentials.Signing): +class Credentials(credentials.CredentialsWithQuotaProject, credentials.Signing): """This module defines impersonated credentials which are essentially impersonated identities. @@ -293,7 +293,7 @@ def service_account_email(self): def signer(self): return self - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( self._source_credentials, @@ -305,7 +305,7 @@ def with_quota_project(self, quota_project_id): ) -class IDTokenCredentials(credentials.Credentials): +class IDTokenCredentials(credentials.CredentialsWithQuotaProject): """Open ID Connect ID Token-based service account credentials. """ @@ -359,7 +359,7 @@ def with_include_email(self, include_email): quota_project_id=self._quota_project_id, ) - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( target_credentials=self._target_credentials, diff --git a/google/auth/jwt.py b/google/auth/jwt.py index 35ae03432..a4f04f529 100644 --- a/google/auth/jwt.py +++ b/google/auth/jwt.py @@ -288,7 +288,9 @@ def decode(token, certs=None, verify=True, audience=None): return payload -class Credentials(google.auth.credentials.Signing, google.auth.credentials.Credentials): +class Credentials( + google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject +): """Credentials that use a JWT as the bearer token. These credentials require an "audience" claim. This claim identifies the @@ -493,7 +495,7 @@ def with_claims( quota_project_id=self._quota_project_id, ) - @_helpers.copy_docstring(google.auth.credentials.Credentials) + @_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( self._signer, @@ -554,7 +556,7 @@ def signer(self): class OnDemandCredentials( - google.auth.credentials.Signing, google.auth.credentials.Credentials + google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject ): """On-demand JWT credentials. @@ -721,7 +723,7 @@ def with_claims(self, issuer=None, subject=None, additional_claims=None): quota_project_id=self._quota_project_id, ) - @_helpers.copy_docstring(google.auth.credentials.Credentials) + @_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py index 6f9627572..6e58f630d 100644 --- a/google/oauth2/credentials.py +++ b/google/oauth2/credentials.py @@ -47,7 +47,7 @@ _GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token" -class Credentials(credentials.ReadOnlyScoped, credentials.Credentials): +class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaProject): """Credentials using OAuth 2.0 access and refresh tokens. The credentials are considered immutable. If you want to modify the @@ -161,7 +161,7 @@ def requires_scopes(self): the initial token is requested and can not be changed.""" return False - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( @@ -305,7 +305,7 @@ def to_json(self, strip=None): return json.dumps(prep) -class UserAccessTokenCredentials(credentials.Credentials): +class UserAccessTokenCredentials(credentials.CredentialsWithQuotaProject): """Access token credentials for user account. Obtain the access token for a given user account or the current active @@ -336,7 +336,7 @@ def with_account(self, account): """ return self.__class__(account=account, quota_project_id=self._quota_project_id) - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__(account=self._account, quota_project_id=quota_project_id) diff --git a/google/oauth2/service_account.py b/google/oauth2/service_account.py index 2240631e9..c4898a247 100644 --- a/google/oauth2/service_account.py +++ b/google/oauth2/service_account.py @@ -82,7 +82,9 @@ _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds -class Credentials(credentials.Signing, credentials.Scoped, credentials.Credentials): +class Credentials( + credentials.Signing, credentials.Scoped, credentials.CredentialsWithQuotaProject +): """Service account credentials Usually, you'll create these credentials with one of the helper @@ -306,7 +308,7 @@ def with_claims(self, additional_claims): additional_claims=new_additional_claims, ) - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( @@ -375,7 +377,7 @@ def signer_email(self): return self._service_account_email -class IDTokenCredentials(credentials.Signing, credentials.Credentials): +class IDTokenCredentials(credentials.Signing, credentials.CredentialsWithQuotaProject): """Open ID Connect ID Token-based service account credentials. These credentials are largely similar to :class:`.Credentials`, but instead @@ -533,7 +535,7 @@ def with_target_audience(self, target_audience): quota_project_id=self.quota_project_id, ) - @_helpers.copy_docstring(credentials.Credentials) + @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject) def with_quota_project(self, quota_project_id): return self.__class__( self._signer, diff --git a/tests/test__default.py b/tests/test__default.py index 55a14c207..2738e22bc 100644 --- a/tests/test__default.py +++ b/tests/test__default.py @@ -49,7 +49,7 @@ with open(SERVICE_ACCOUNT_FILE) as fh: SERVICE_ACCOUNT_FILE_DATA = json.load(fh) -MOCK_CREDENTIALS = mock.Mock(spec=credentials.Credentials) +MOCK_CREDENTIALS = mock.Mock(spec=credentials.CredentialsWithQuotaProject) MOCK_CREDENTIALS.with_quota_project.return_value = MOCK_CREDENTIALS LOAD_FILE_PATCH = mock.patch( diff --git a/tests/test_credentials.py b/tests/test_credentials.py index 2023fac1b..0637b01e4 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -115,12 +115,6 @@ def test_anonymous_credentials_before_request(): assert headers == {} -def test_anonymous_credentials_with_quota_project(): - with pytest.raises(ValueError): - anon = credentials.AnonymousCredentials() - anon.with_quota_project("project-foo") - - class ReadOnlyScopedCredentialsImpl(credentials.ReadOnlyScoped, CredentialsImpl): @property def requires_scopes(self):