diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py index 572995690..f2a465662 100644 --- a/google/auth/compute_engine/credentials.py +++ b/google/auth/compute_engine/credentials.py @@ -24,7 +24,7 @@ from google.auth.compute_engine import _metadata -class Credentials(credentials.Scoped, credentials.Credentials): +class Credentials(credentials.ReadOnnlyScoped, credentials.Credentials): """Compute Engine Credentials. These credentials use the Google Compute Engine metadata server to obtain @@ -105,17 +105,3 @@ def service_account_email(self): def requires_scopes(self): """False: Compute Engine credentials can not be scoped.""" return False - - def with_scopes(self, scopes): - """Unavailable, Compute Engine credentials can not be scoped. - - Scopes can only be set at Compute Engine instance creation time. - See the `Compute Engine authentication documentation`_ for details on - how to configure instance scopes. - - .. _Compute Engine authentication documentation: - https://cloud.google.com/compute/docs/authentication#using - """ - raise NotImplementedError( - 'Compute Engine credentials can not set scopes. Scopes must be ' - 'set when the Compute Engine instance is created.') diff --git a/google/auth/credentials.py b/google/auth/credentials.py index 74d678821..83683eb7a 100644 --- a/google/auth/credentials.py +++ b/google/auth/credentials.py @@ -123,8 +123,8 @@ def before_request(self, request, method, url, headers): @six.add_metaclass(abc.ABCMeta) -class Scoped(object): - """Interface for scoped credentials. +class ReadOnnlyScoped(object): + """Interface for credentials whose scopes can be queried. OAuth 2.0-based credentials allow limiting access using scopes as described in `RFC6749 Section 3.3`_. @@ -152,7 +152,7 @@ class Scoped(object): .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3 """ def __init__(self): - super(Scoped, self).__init__() + super(ReadOnnlyScoped, self).__init__() self._scopes = None @property @@ -166,6 +166,46 @@ def requires_scopes(self): """ return False + def has_scopes(self, scopes): + """Checks if the credentials have the given scopes. + + .. warning: This method is not guaranteed to be accurate if the + credentials are :attr:`~Credentials.invalid`. + + Returns: + bool: True if the credentials have the given scopes. + """ + return set(scopes).issubset(set(self._scopes or [])) + + +class Scoped(ReadOnnlyScoped): + """Interface for credentials whose scopes can be replaced while copying. + + OAuth 2.0-based credentials allow limiting access using scopes as described + in `RFC6749 Section 3.3`_. + If a credential class implements this interface then the credentials either + use scopes in their implementation. + + Some credentials require scopes in order to obtain a token. You can check + if scoping is necessary with :attr:`requires_scopes`:: + + if credentials.requires_scopes: + # Scoping is required. + credentials = credentials.create_scoped(['one', 'two']) + + Credentials that require scopes must either be constructed with scopes:: + + credentials = SomeScopedCredentials(scopes=['one', 'two']) + + Or must copy an existing instance using :meth:`with_scopes`:: + + scoped_credentials = credentials.with_scopes(scopes=['one', 'two']) + + Some credentials have scopes but do not allow or require scopes to be set, + these credentials can be used as-is. + + .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3 + """ @abc.abstractmethod def with_scopes(self, scopes): """Create a copy of these credentials with the specified scopes. @@ -180,17 +220,6 @@ def with_scopes(self, scopes): """ raise NotImplementedError('This class does not require scoping.') - def has_scopes(self, scopes): - """Checks if the credentials have the given scopes. - - .. warning: This method is not guaranteed to be accurate if the - credentials are :attr:`~Credentials.invalid`. - - Returns: - bool: True if the credentials have the given scopes. - """ - return set(scopes).issubset(set(self._scopes or [])) - def with_scopes_if_required(credentials, scopes): """Creates a copy of the credentials with scopes if scoping is required. diff --git a/tests/compute_engine/test_credentials.py b/tests/compute_engine/test_credentials.py index 732cb419c..ae2597d30 100644 --- a/tests/compute_engine/test_credentials.py +++ b/tests/compute_engine/test_credentials.py @@ -105,7 +105,3 @@ def test_before_request_refreshes(self, get): # Credentials should now be valid. assert self.credentials.valid - - def test_with_scopes(self): - with pytest.raises(NotImplementedError): - self.credentials.with_scopes(['one', 'two']) diff --git a/tests/test_credentials.py b/tests/test_credentials.py index b5a540dd8..ae53cd970 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -77,22 +77,20 @@ def test_before_request(): assert headers['authorization'] == 'Bearer token' -class ScopedCredentialsImpl(credentials.Scoped, CredentialsImpl): +class ReadOnnlyScopedCredentialsImpl(credentials.ReadOnnlyScoped, + CredentialsImpl): @property def requires_scopes(self): - return super(ScopedCredentialsImpl, self).requires_scopes - - def with_scopes(self, scopes): - raise NotImplementedError + return super(ReadOnnlyScopedCredentialsImpl, self).requires_scopes -def test_scoped_credentials_constructor(): - credentials = ScopedCredentialsImpl() +def test_readonly_scoped_credentials_constructor(): + credentials = ReadOnnlyScopedCredentialsImpl() assert credentials._scopes is None -def test_scoped_credentials_scopes(): - credentials = ScopedCredentialsImpl() +def test_readonly_scoped_credentials_scopes(): + credentials = ReadOnnlyScopedCredentialsImpl() credentials._scopes = ['one', 'two'] assert credentials.scopes == ['one', 'two'] assert credentials.has_scopes(['one']) @@ -101,8 +99,8 @@ def test_scoped_credentials_scopes(): assert not credentials.has_scopes(['three']) -def test_scoped_credentials_requires_scopes(): - credentials = ScopedCredentialsImpl() +def test_readonly_scoped_credentials_requires_scopes(): + credentials = ReadOnnlyScopedCredentialsImpl() assert not credentials.requires_scopes