Move read-only methods of 'Scoped' into new interface, 'ReadOnlyScoped'. (#195)
Not all subclasses of 'Scoped' can sanely implement 'with_scopes' (e.g, on
GCE the scopes are hard-wired in when creating the GCE node).
Make 'Scoped' derive from 'ReadOnlyScoped', adding the 'with_scopes' method.
Make GCE's 'credentials' class derive from 'ReadOnlyScoped'.
Closes #194.
diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py
index 5729956..f2a4656 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 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 74d6788..83683eb 100644
--- a/google/auth/credentials.py
+++ b/google/auth/credentials.py
@@ -123,8 +123,8 @@
@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 @@
.. _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 @@
"""
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 @@
"""
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.