| 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.')
|
|
|