Index: client/third_party/google/auth/credentials.py |
diff --git a/client/third_party/google/auth/credentials.py b/client/third_party/google/auth/credentials.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..74d6788217f9056e0645a6bb450778a56093d52c |
--- /dev/null |
+++ b/client/third_party/google/auth/credentials.py |
@@ -0,0 +1,251 @@ |
+# Copyright 2016 Google Inc. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+ |
+"""Interfaces for credentials.""" |
+ |
+import abc |
+ |
+import six |
+ |
+from google.auth import _helpers |
+ |
+ |
+@six.add_metaclass(abc.ABCMeta) |
+class Credentials(object): |
+ """Base class for all credentials. |
+ |
+ All credentials have a :attr:`token` that is used for authentication and |
+ may also optionally set an :attr:`expiry` to indicate when the token will |
+ no longer be valid. |
+ |
+ Most credentials will be :attr:`invalid` until :meth:`refresh` is called. |
+ Credentials can do this automatically before the first HTTP request in |
+ :meth:`before_request`. |
+ |
+ Although the token and expiration will change as the credentials are |
+ :meth:`refreshed <refresh>` and used, credentials should be considered |
+ immutable. Various credentials will accept configuration such as private |
+ keys, scopes, and other options. These options are not changeable after |
+ construction. Some classes will provide mechanisms to copy the credentials |
+ with modifications such as :meth:`ScopedCredentials.with_scopes`. |
+ """ |
+ def __init__(self): |
+ self.token = None |
+ """str: The bearer token that can be used in HTTP headers to make |
+ authenticated requests.""" |
+ self.expiry = None |
+ """Optional[datetime]: When the token expires and is no longer valid. |
+ If this is None, the token is assumed to never expire.""" |
+ |
+ @property |
+ def expired(self): |
+ """Checks if the credentials are expired. |
+ |
+ Note that credentials can be invalid but not expired becaue Credentials |
+ with :attr:`expiry` set to None is considered to never expire. |
+ """ |
+ if not self.expiry: |
+ return False |
+ |
+ # Remove 5 minutes from expiry to err on the side of reporting |
+ # expiration early so that we avoid the 401-refresh-retry loop. |
+ skewed_expiry = self.expiry - _helpers.CLOCK_SKEW |
+ return _helpers.utcnow() >= skewed_expiry |
+ |
+ @property |
+ def valid(self): |
+ """Checks the validity of the credentials. |
+ |
+ This is True if the credentials have a :attr:`token` and the token |
+ is not :attr:`expired`. |
+ """ |
+ return self.token is not None and not self.expired |
+ |
+ @abc.abstractmethod |
+ def refresh(self, request): |
+ """Refreshes the access token. |
+ |
+ Args: |
+ request (google.auth.transport.Request): The object used to make |
+ HTTP requests. |
+ |
+ Raises: |
+ google.auth.exceptions.RefreshError: If the credentials could |
+ not be refreshed. |
+ """ |
+ # pylint: disable=missing-raises-doc |
+ # (pylint doesn't recognize that this is abstract) |
+ raise NotImplementedError('Refresh must be implemented') |
+ |
+ def apply(self, headers, token=None): |
+ """Apply the token to the authentication header. |
+ |
+ Args: |
+ headers (Mapping): The HTTP request headers. |
+ token (Optional[str]): If specified, overrides the current access |
+ token. |
+ """ |
+ headers['authorization'] = 'Bearer {}'.format( |
+ _helpers.from_bytes(token or self.token)) |
+ |
+ def before_request(self, request, method, url, headers): |
+ """Performs credential-specific before request logic. |
+ |
+ Refreshes the credentials if necessary, then calls :meth:`apply` to |
+ apply the token to the authentication header. |
+ |
+ Args: |
+ request (google.auth.transport.Request): The object used to make |
+ HTTP requests. |
+ method (str): The request's HTTP method or the RPC method being |
+ invoked. |
+ url (str): The request's URI or the RPC service's URI. |
+ headers (Mapping): The request's headers. |
+ """ |
+ # pylint: disable=unused-argument |
+ # (Subclasses may use these arguments to ascertain information about |
+ # the http request.) |
+ if not self.valid: |
+ self.refresh(request) |
+ self.apply(headers) |
+ |
+ |
+@six.add_metaclass(abc.ABCMeta) |
+class Scoped(object): |
+ """Interface for scoped credentials. |
+ |
+ 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 |
+ """ |
+ def __init__(self): |
+ super(Scoped, self).__init__() |
+ self._scopes = None |
+ |
+ @property |
+ def scopes(self): |
+ """Sequence[str]: the credentials' current set of scopes.""" |
+ return self._scopes |
+ |
+ @abc.abstractproperty |
+ def requires_scopes(self): |
+ """True if these credentials require scopes to obtain an access token. |
+ """ |
+ return False |
+ |
+ @abc.abstractmethod |
+ def with_scopes(self, scopes): |
+ """Create a copy of these credentials with the specified scopes. |
+ |
+ Args: |
+ scopes (Sequence[str]): The list of scopes to request. |
+ |
+ Raises: |
+ NotImplementedError: If the credentials' scopes can not be changed. |
+ This can be avoided by checking :attr:`requires_scopes` before |
+ calling this method. |
+ """ |
+ 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. |
+ |
+ This helper function is useful when you do not know (or care to know) the |
+ specific type of credentials you are using (such as when you use |
+ :func:`google.auth.default`). This function will call |
+ :meth:`Scoped.with_scopes` if the credentials are scoped credentials and if |
+ the credentials require scoping. Otherwise, it will return the credentials |
+ as-is. |
+ |
+ Args: |
+ credentials (google.auth.credentials.Credentials): The credentials to |
+ scope if necessary. |
+ scopes (Sequence[str]): The list of scopes to use. |
+ |
+ Returns: |
+ google.auth.credentials.Credentials: Either a new set of scoped |
+ credentials, or the passed in credentials instance if no scoping |
+ was required. |
+ """ |
+ if isinstance(credentials, Scoped) and credentials.requires_scopes: |
+ return credentials.with_scopes(scopes) |
+ else: |
+ return credentials |
+ |
+ |
+@six.add_metaclass(abc.ABCMeta) |
+class Signing(object): |
+ """Interface for credentials that can cryptographically sign messages.""" |
+ |
+ @abc.abstractmethod |
+ def sign_bytes(self, message): |
+ """Signs the given message. |
+ |
+ Args: |
+ message (bytes): The message to sign. |
+ |
+ Returns: |
+ bytes: The message's cryptographic signature. |
+ """ |
+ # pylint: disable=missing-raises-doc,redundant-returns-doc |
+ # (pylint doesn't recognize that this is abstract) |
+ raise NotImplementedError('Sign bytes must be implemented.') |
+ |
+ @abc.abstractproperty |
+ def signer_email(self): |
+ """Optional[str]: An email address that identifies the signer.""" |
+ # pylint: disable=missing-raises-doc |
+ # (pylint doesn't recognize that this is abstract) |
+ raise NotImplementedError('Signer email must be implemented.') |
+ |
+ @abc.abstractproperty |
+ def signer(self): |
+ """google.auth.crypt.Signer: The signer used to sign bytes.""" |
+ # pylint: disable=missing-raises-doc |
+ # (pylint doesn't recognize that this is abstract) |
+ raise NotImplementedError('Signer must be implemented.') |