google-auth-1.5.1/0000755110576702575230000000000013330167570017074 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/PKG-INFO0000644110576702575230000000641013330167570020172 0ustar theaflowersprimarygroup00000000000000Metadata-Version: 1.1 Name: google-auth Version: 1.5.1 Summary: Google Authentication Library Home-page: https://github.com/GoogleCloudPlatform/google-auth-library-python Author: Google Cloud Platform Author-email: jonwayne+google-auth@google.com License: Apache 2.0 Description: Google Auth Python Library ========================== |build| |docs| |pypi| This library simplifies using Google's various server-to-server authentication mechanisms to access Google APIs. .. |build| image:: https://travis-ci.org/GoogleCloudPlatform/google-auth-library-python.svg?branch=master :target: https://travis-ci.org/GoogleCloudPlatform/google-auth-library-python .. |docs| image:: https://readthedocs.org/projects/google-auth/badge/?version=latest :target: https://google-auth.readthedocs.io/en/latest/ .. |pypi| image:: https://img.shields.io/pypi/v/google-auth.svg :target: https://pypi.python.org/pypi/google-auth Installing ---------- You can install using `pip`_:: $ pip install google-auth .. _pip: https://pip.pypa.io/en/stable/ For more information on setting up your Python development environment, please refer to `Python Development Environment Setup Guide`_ for Google Cloud Platform. .. _`Python Development Environment Setup Guide`: https://cloud.google.com/python/setup Documentation ------------- Google Auth Python Library has usage and reference documentation at `google-auth.readthedocs.io `_. Maintainers ----------- - `@jonparrott `_ (Jon Wayne Parrott) - `@dhermes `_ (Danny Hermes) - `@lukesneeringer `_ (Luke Sneeringer) Contributing ------------ Contributions to this library are always welcome and highly encouraged. See `CONTRIBUTING.rst`_ for more information on how to get started. .. _CONTRIBUTING.rst: https://github.com/GoogleCloudPlatform/google-auth-library-python/blob/master/CONTRIBUTING.rst License ------- Apache 2.0 - See `the LICENSE`_ for more information. .. _the LICENSE: https://github.com/GoogleCloudPlatform/google-auth-library-python/blob/master/LICENSE Keywords: google auth oauth client Platform: UNKNOWN Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP google-auth-1.5.1/LICENSE0000644110576702575230000002613513277071027020111 0ustar theaflowersprimarygroup00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. google-auth-1.5.1/google/0000755110576702575230000000000013330167570020350 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google/auth/0000755110576702575230000000000013330167570021311 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google/auth/transport/0000755110576702575230000000000013330167570023345 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google/auth/transport/grpc.py0000644110576702575230000001163413277071027024660 0ustar theaflowersprimarygroup00000000000000# 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. """Authorization support for gRPC.""" from __future__ import absolute_import import six try: import grpc except ImportError as caught_exc: # pragma: NO COVER six.raise_from( ImportError( 'gRPC is not installed, please install the grpcio package ' 'to use the gRPC transport.' ), caught_exc, ) class AuthMetadataPlugin(grpc.AuthMetadataPlugin): """A `gRPC AuthMetadataPlugin`_ that inserts the credentials into each request. .. _gRPC AuthMetadataPlugin: http://www.grpc.io/grpc/python/grpc.html#grpc.AuthMetadataPlugin Args: credentials (google.auth.credentials.Credentials): The credentials to add to requests. request (google.auth.transport.Request): A HTTP transport request object used to refresh credentials as needed. """ def __init__(self, credentials, request): # pylint: disable=no-value-for-parameter # pylint doesn't realize that the super method takes no arguments # because this class is the same name as the superclass. super(AuthMetadataPlugin, self).__init__() self._credentials = credentials self._request = request def _get_authorization_headers(self, context): """Gets the authorization headers for a request. Returns: Sequence[Tuple[str, str]]: A list of request headers (key, value) to add to the request. """ headers = {} self._credentials.before_request( self._request, context.method_name, context.service_url, headers) return list(six.iteritems(headers)) def __call__(self, context, callback): """Passes authorization metadata into the given callback. Args: context (grpc.AuthMetadataContext): The RPC context. callback (grpc.AuthMetadataPluginCallback): The callback that will be invoked to pass in the authorization metadata. """ callback(self._get_authorization_headers(context), None) def secure_authorized_channel( credentials, request, target, ssl_credentials=None, **kwargs): """Creates a secure authorized gRPC channel. This creates a channel with SSL and :class:`AuthMetadataPlugin`. This channel can be used to create a stub that can make authorized requests. Example:: import google.auth import google.auth.transport.grpc import google.auth.transport.requests from google.cloud.speech.v1 import cloud_speech_pb2 # Get credentials. credentials, _ = google.auth.default() # Get an HTTP request function to refresh credentials. request = google.auth.transport.requests.Request() # Create a channel. channel = google.auth.transport.grpc.secure_authorized_channel( credentials, 'speech.googleapis.com:443', request) # Use the channel to create a stub. cloud_speech.create_Speech_stub(channel) Args: credentials (google.auth.credentials.Credentials): The credentials to add to requests. request (google.auth.transport.Request): A HTTP transport request object used to refresh credentials as needed. Even though gRPC is a separate transport, there's no way to refresh the credentials without using a standard http transport. target (str): The host and port of the service. ssl_credentials (grpc.ChannelCredentials): Optional SSL channel credentials. This can be used to specify different certificates. kwargs: Additional arguments to pass to :func:`grpc.secure_channel`. Returns: grpc.Channel: The created gRPC channel. """ # Create the metadata plugin for inserting the authorization header. metadata_plugin = AuthMetadataPlugin(credentials, request) # Create a set of grpc.CallCredentials using the metadata plugin. google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) if ssl_credentials is None: ssl_credentials = grpc.ssl_channel_credentials() # Combine the ssl credentials and the authorization credentials. composite_credentials = grpc.composite_channel_credentials( ssl_credentials, google_auth_credentials) return grpc.secure_channel(target, composite_credentials, **kwargs) google-auth-1.5.1/google/auth/transport/urllib3.py0000644110576702575230000002216713277071027025304 0ustar theaflowersprimarygroup00000000000000# 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. """Transport adapter for urllib3.""" from __future__ import absolute_import import logging # Certifi is Mozilla's certificate bundle. Urllib3 needs a certificate bundle # to verify HTTPS requests, and certifi is the recommended and most reliable # way to get a root certificate bundle. See # http://urllib3.readthedocs.io/en/latest/user-guide.html\ # #certificate-verification # For more details. try: import certifi except ImportError: # pragma: NO COVER certifi = None try: import urllib3 except ImportError as caught_exc: # pragma: NO COVER import six six.raise_from( ImportError( 'The urllib3 library is not installed, please install the ' 'urllib3 package to use the urllib3 transport.' ), caught_exc, ) import six import urllib3.exceptions # pylint: disable=ungrouped-imports from google.auth import exceptions from google.auth import transport _LOGGER = logging.getLogger(__name__) class _Response(transport.Response): """urllib3 transport response adapter. Args: response (urllib3.response.HTTPResponse): The raw urllib3 response. """ def __init__(self, response): self._response = response @property def status(self): return self._response.status @property def headers(self): return self._response.headers @property def data(self): return self._response.data class Request(transport.Request): """urllib3 request adapter. This class is used internally for making requests using various transports in a consistent way. If you use :class:`AuthorizedHttp` you do not need to construct or use this class directly. This class can be useful if you want to manually refresh a :class:`~google.auth.credentials.Credentials` instance:: import google.auth.transport.urllib3 import urllib3 http = urllib3.PoolManager() request = google.auth.transport.urllib3.Request(http) credentials.refresh(request) Args: http (urllib3.request.RequestMethods): An instance of any urllib3 class that implements :class:`~urllib3.request.RequestMethods`, usually :class:`urllib3.PoolManager`. .. automethod:: __call__ """ def __init__(self, http): self.http = http def __call__(self, url, method='GET', body=None, headers=None, timeout=None, **kwargs): """Make an HTTP request using urllib3. Args: url (str): The URI to be requested. method (str): The HTTP method to use for the request. Defaults to 'GET'. body (bytes): The payload / body in HTTP request. headers (Mapping[str, str]): Request headers. timeout (Optional[int]): The number of seconds to wait for a response from the server. If not specified or if None, the urllib3 default timeout will be used. kwargs: Additional arguments passed throught to the underlying urllib3 :meth:`urlopen` method. Returns: google.auth.transport.Response: The HTTP response. Raises: google.auth.exceptions.TransportError: If any exception occurred. """ # urllib3 uses a sentinel default value for timeout, so only set it if # specified. if timeout is not None: kwargs['timeout'] = timeout try: _LOGGER.debug('Making request: %s %s', method, url) response = self.http.request( method, url, body=body, headers=headers, **kwargs) return _Response(response) except urllib3.exceptions.HTTPError as caught_exc: new_exc = exceptions.TransportError(caught_exc) six.raise_from(new_exc, caught_exc) def _make_default_http(): if certifi is not None: return urllib3.PoolManager( cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) else: return urllib3.PoolManager() class AuthorizedHttp(urllib3.request.RequestMethods): """A urllib3 HTTP class with credentials. This class is used to perform requests to API endpoints that require authorization:: from google.auth.transport.urllib3 import AuthorizedHttp authed_http = AuthorizedHttp(credentials) response = authed_http.request( 'GET', 'https://www.googleapis.com/storage/v1/b') This class implements :class:`urllib3.request.RequestMethods` and can be used just like any other :class:`urllib3.PoolManager`. The underlying :meth:`urlopen` implementation handles adding the credentials' headers to the request and refreshing credentials as needed. Args: credentials (google.auth.credentials.Credentials): The credentials to add to the request. http (urllib3.PoolManager): The underlying HTTP object to use to make requests. If not specified, a :class:`urllib3.PoolManager` instance will be constructed with sane defaults. refresh_status_codes (Sequence[int]): Which HTTP status codes indicate that credentials should be refreshed and the request should be retried. max_refresh_attempts (int): The maximum number of times to attempt to refresh the credentials and retry the request. """ def __init__(self, credentials, http=None, refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES, max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS): if http is None: http = _make_default_http() self.credentials = credentials self.http = http self._refresh_status_codes = refresh_status_codes self._max_refresh_attempts = max_refresh_attempts # Request instance used by internal methods (for example, # credentials.refresh). self._request = Request(self.http) super(AuthorizedHttp, self).__init__() def urlopen(self, method, url, body=None, headers=None, **kwargs): """Implementation of urllib3's urlopen.""" # pylint: disable=arguments-differ # We use kwargs to collect additional args that we don't need to # introspect here. However, we do explicitly collect the two # positional arguments. # Use a kwarg for this instead of an attribute to maintain # thread-safety. _credential_refresh_attempt = kwargs.pop( '_credential_refresh_attempt', 0) if headers is None: headers = self.headers # Make a copy of the headers. They will be modified by the credentials # and we want to pass the original headers if we recurse. request_headers = headers.copy() self.credentials.before_request( self._request, method, url, request_headers) response = self.http.urlopen( method, url, body=body, headers=request_headers, **kwargs) # If the response indicated that the credentials needed to be # refreshed, then refresh the credentials and re-attempt the # request. # A stored token may expire between the time it is retrieved and # the time the request is made, so we may need to try twice. # The reason urllib3's retries aren't used is because they # don't allow you to modify the request headers. :/ if (response.status in self._refresh_status_codes and _credential_refresh_attempt < self._max_refresh_attempts): _LOGGER.info( 'Refreshing credentials due to a %s response. Attempt %s/%s.', response.status, _credential_refresh_attempt + 1, self._max_refresh_attempts) self.credentials.refresh(self._request) # Recurse. Pass in the original headers, not our modified set. return self.urlopen( method, url, body=body, headers=headers, _credential_refresh_attempt=_credential_refresh_attempt + 1, **kwargs) return response # Proxy methods for compliance with the urllib3.PoolManager interface def __enter__(self): """Proxy to ``self.http``.""" return self.http.__enter__() def __exit__(self, exc_type, exc_val, exc_tb): """Proxy to ``self.http``.""" return self.http.__exit__(exc_type, exc_val, exc_tb) @property def headers(self): """Proxy to ``self.http``.""" return self.http.headers @headers.setter def headers(self, value): """Proxy to ``self.http``.""" self.http.headers = value google-auth-1.5.1/google/auth/transport/_http_client.py0000644110576702575230000000724613277071027026405 0ustar theaflowersprimarygroup00000000000000# 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. """Transport adapter for http.client, for internal use only.""" import logging import socket import six from six.moves import http_client from six.moves import urllib from google.auth import exceptions from google.auth import transport _LOGGER = logging.getLogger(__name__) class Response(transport.Response): """http.client transport response adapter. Args: response (http.client.HTTPResponse): The raw http client response. """ def __init__(self, response): self._status = response.status self._headers = { key.lower(): value for key, value in response.getheaders()} self._data = response.read() @property def status(self): return self._status @property def headers(self): return self._headers @property def data(self): return self._data class Request(transport.Request): """http.client transport request adapter.""" def __call__(self, url, method='GET', body=None, headers=None, timeout=None, **kwargs): """Make an HTTP request using http.client. Args: url (str): The URI to be requested. method (str): The HTTP method to use for the request. Defaults to 'GET'. body (bytes): The payload / body in HTTP request. headers (Mapping): Request headers. timeout (Optional(int)): The number of seconds to wait for a response from the server. If not specified or if None, the socket global default timeout will be used. kwargs: Additional arguments passed throught to the underlying :meth:`~http.client.HTTPConnection.request` method. Returns: Response: The HTTP response. Raises: google.auth.exceptions.TransportError: If any exception occurred. """ # socket._GLOBAL_DEFAULT_TIMEOUT is the default in http.client. if timeout is None: timeout = socket._GLOBAL_DEFAULT_TIMEOUT # http.client doesn't allow None as the headers argument. if headers is None: headers = {} # http.client needs the host and path parts specified separately. parts = urllib.parse.urlsplit(url) path = urllib.parse.urlunsplit( ('', '', parts.path, parts.query, parts.fragment)) if parts.scheme != 'http': raise exceptions.TransportError( 'http.client transport only supports the http scheme, {}' 'was specified'.format(parts.scheme)) connection = http_client.HTTPConnection(parts.netloc, timeout=timeout) try: _LOGGER.debug('Making request: %s %s', method, url) connection.request( method, path, body=body, headers=headers, **kwargs) response = connection.getresponse() return Response(response) except (http_client.HTTPException, socket.error) as caught_exc: new_exc = exceptions.TransportError(caught_exc) six.raise_from(new_exc, caught_exc) finally: connection.close() google-auth-1.5.1/google/auth/transport/__init__.py0000644110576702575230000000656213277071027025470 0ustar theaflowersprimarygroup00000000000000# 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. """Transport - HTTP client library support. :mod:`google.auth` is designed to work with various HTTP client libraries such as urllib3 and requests. In order to work across these libraries with different interfaces some abstraction is needed. This module provides two interfaces that are implemented by transport adapters to support HTTP libraries. :class:`Request` defines the interface expected by :mod:`google.auth` to make requests. :class:`Response` defines the interface for the return value of :class:`Request`. """ import abc import six from six.moves import http_client DEFAULT_REFRESH_STATUS_CODES = (http_client.UNAUTHORIZED,) """Sequence[int]: Which HTTP status code indicate that credentials should be refreshed and a request should be retried. """ DEFAULT_MAX_REFRESH_ATTEMPTS = 2 """int: How many times to refresh the credentials and retry a request.""" @six.add_metaclass(abc.ABCMeta) class Response(object): """HTTP Response data.""" @abc.abstractproperty def status(self): """int: The HTTP status code.""" raise NotImplementedError('status must be implemented.') @abc.abstractproperty def headers(self): """Mapping[str, str]: The HTTP response headers.""" raise NotImplementedError('headers must be implemented.') @abc.abstractproperty def data(self): """bytes: The response body.""" raise NotImplementedError('data must be implemented.') @six.add_metaclass(abc.ABCMeta) class Request(object): """Interface for a callable that makes HTTP requests. Specific transport implementations should provide an implementation of this that adapts their specific request / response API. .. automethod:: __call__ """ @abc.abstractmethod def __call__(self, url, method='GET', body=None, headers=None, timeout=None, **kwargs): """Make an HTTP request. Args: url (str): The URI to be requested. method (str): The HTTP method to use for the request. Defaults to 'GET'. body (bytes): The payload / body in HTTP request. headers (Mapping[str, str]): Request headers. timeout (Optional[int]): The number of seconds to wait for a response from the server. If not specified or if None, the transport-specific default timeout will be used. kwargs: Additionally arguments passed on to the transport's request method. Returns: Response: The HTTP response. Raises: google.auth.exceptions.TransportError: If any exception occurred. """ # pylint: disable=redundant-returns-doc, missing-raises-doc # (pylint doesn't play well with abstract docstrings.) raise NotImplementedError('__call__ must be implemented.') google-auth-1.5.1/google/auth/transport/requests.py0000644110576702575230000002065213277071027025600 0ustar theaflowersprimarygroup00000000000000# 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. """Transport adapter for Requests.""" from __future__ import absolute_import import functools import logging try: import requests except ImportError as caught_exc: # pragma: NO COVER import six six.raise_from( ImportError( 'The requests library is not installed, please install the ' 'requests package to use the requests transport.' ), caught_exc, ) import requests.adapters # pylint: disable=ungrouped-imports import requests.exceptions # pylint: disable=ungrouped-imports import six # pylint: disable=ungrouped-imports from google.auth import exceptions from google.auth import transport _LOGGER = logging.getLogger(__name__) class _Response(transport.Response): """Requests transport response adapter. Args: response (requests.Response): The raw Requests response. """ def __init__(self, response): self._response = response @property def status(self): return self._response.status_code @property def headers(self): return self._response.headers @property def data(self): return self._response.content class Request(transport.Request): """Requests request adapter. This class is used internally for making requests using various transports in a consistent way. If you use :class:`AuthorizedSession` you do not need to construct or use this class directly. This class can be useful if you want to manually refresh a :class:`~google.auth.credentials.Credentials` instance:: import google.auth.transport.requests import requests request = google.auth.transport.requests.Request() credentials.refresh(request) Args: session (requests.Session): An instance :class:`requests.Session` used to make HTTP requests. If not specified, a session will be created. .. automethod:: __call__ """ def __init__(self, session=None): if not session: session = requests.Session() self.session = session def __call__(self, url, method='GET', body=None, headers=None, timeout=None, **kwargs): """Make an HTTP request using requests. Args: url (str): The URI to be requested. method (str): The HTTP method to use for the request. Defaults to 'GET'. body (bytes): The payload / body in HTTP request. headers (Mapping[str, str]): Request headers. timeout (Optional[int]): The number of seconds to wait for a response from the server. If not specified or if None, the requests default timeout will be used. kwargs: Additional arguments passed through to the underlying requests :meth:`~requests.Session.request` method. Returns: google.auth.transport.Response: The HTTP response. Raises: google.auth.exceptions.TransportError: If any exception occurred. """ try: _LOGGER.debug('Making request: %s %s', method, url) response = self.session.request( method, url, data=body, headers=headers, timeout=timeout, **kwargs) return _Response(response) except requests.exceptions.RequestException as caught_exc: new_exc = exceptions.TransportError(caught_exc) six.raise_from(new_exc, caught_exc) class AuthorizedSession(requests.Session): """A Requests Session class with credentials. This class is used to perform requests to API endpoints that require authorization:: from google.auth.transport.requests import AuthorizedSession authed_session = AuthorizedSession(credentials) response = authed_session.request( 'GET', 'https://www.googleapis.com/storage/v1/b') The underlying :meth:`request` implementation handles adding the credentials' headers to the request and refreshing credentials as needed. Args: credentials (google.auth.credentials.Credentials): The credentials to add to the request. refresh_status_codes (Sequence[int]): Which HTTP status codes indicate that credentials should be refreshed and the request should be retried. max_refresh_attempts (int): The maximum number of times to attempt to refresh the credentials and retry the request. refresh_timeout (Optional[int]): The timeout value in seconds for credential refresh HTTP requests. kwargs: Additional arguments passed to the :class:`requests.Session` constructor. """ def __init__(self, credentials, refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES, max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS, refresh_timeout=None, **kwargs): super(AuthorizedSession, self).__init__(**kwargs) self.credentials = credentials self._refresh_status_codes = refresh_status_codes self._max_refresh_attempts = max_refresh_attempts self._refresh_timeout = refresh_timeout auth_request_session = requests.Session() # Using an adapter to make HTTP requests robust to network errors. # This adapter retrys HTTP requests when network errors occur # and the requests seems safely retryable. retry_adapter = requests.adapters.HTTPAdapter(max_retries=3) auth_request_session.mount("https://", retry_adapter) # Request instance used by internal methods (for example, # credentials.refresh). # Do not pass `self` as the session here, as it can lead to infinite # recursion. self._auth_request = Request(auth_request_session) def request(self, method, url, data=None, headers=None, **kwargs): """Implementation of Requests' request.""" # pylint: disable=arguments-differ # Requests has a ton of arguments to request, but only two # (method, url) are required. We pass through all of the other # arguments to super, so no need to exhaustively list them here. # Use a kwarg for this instead of an attribute to maintain # thread-safety. _credential_refresh_attempt = kwargs.pop( '_credential_refresh_attempt', 0) # Make a copy of the headers. They will be modified by the credentials # and we want to pass the original headers if we recurse. request_headers = headers.copy() if headers is not None else {} self.credentials.before_request( self._auth_request, method, url, request_headers) response = super(AuthorizedSession, self).request( method, url, data=data, headers=request_headers, **kwargs) # If the response indicated that the credentials needed to be # refreshed, then refresh the credentials and re-attempt the # request. # A stored token may expire between the time it is retrieved and # the time the request is made, so we may need to try twice. if (response.status_code in self._refresh_status_codes and _credential_refresh_attempt < self._max_refresh_attempts): _LOGGER.info( 'Refreshing credentials due to a %s response. Attempt %s/%s.', response.status_code, _credential_refresh_attempt + 1, self._max_refresh_attempts) auth_request_with_timeout = functools.partial( self._auth_request, timeout=self._refresh_timeout) self.credentials.refresh(auth_request_with_timeout) # Recurse. Pass in the original headers, not our modified set. return self.request( method, url, data=data, headers=headers, _credential_refresh_attempt=_credential_refresh_attempt + 1, **kwargs) return response google-auth-1.5.1/google/auth/iam.py0000644110576702575230000000705113277071027022435 0ustar theaflowersprimarygroup00000000000000# Copyright 2017 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. """Tools for using the Google `Cloud Identity and Access Management (IAM) API`_'s auth-related functionality. .. _Cloud Identity and Access Management (IAM) API: https://cloud.google.com/iam/docs/ """ import base64 import json from six.moves import http_client from google.auth import _helpers from google.auth import crypt from google.auth import exceptions _IAM_API_ROOT_URI = 'https://iam.googleapis.com/v1' _SIGN_BLOB_URI = ( _IAM_API_ROOT_URI + '/projects/-/serviceAccounts/{}:signBlob?alt=json') class Signer(crypt.Signer): """Signs messages using the IAM `signBlob API`_. This is useful when you need to sign bytes but do not have access to the credential's private key file. .. _signBlob API: https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts /signBlob """ def __init__(self, request, credentials, service_account_email): """ Args: request (google.auth.transport.Request): The object used to make HTTP requests. credentials (google.auth.credentials.Credentials): The credentials that will be used to authenticate the request to the IAM API. The credentials must have of one the following scopes: - https://www.googleapis.com/auth/iam - https://www.googleapis.com/auth/cloud-platform service_account_email (str): The service account email identifying which service account to use to sign bytes. Often, this can be the same as the service account email in the given credentials. """ self._request = request self._credentials = credentials self._service_account_email = service_account_email def _make_signing_request(self, message): """Makes a request to the API signBlob API.""" message = _helpers.to_bytes(message) method = 'POST' url = _SIGN_BLOB_URI.format(self._service_account_email) headers = {} body = json.dumps({ 'bytesToSign': base64.b64encode(message).decode('utf-8'), }) self._credentials.before_request(self._request, method, url, headers) response = self._request( url=url, method=method, body=body, headers=headers) if response.status != http_client.OK: raise exceptions.TransportError( 'Error calling the IAM signBytes API: {}'.format( response.data)) return json.loads(response.data.decode('utf-8')) @property def key_id(self): """Optional[str]: The key ID used to identify this private key. .. warning:: This is always ``None``. The key ID used by IAM can not be reliably determined ahead of time. """ return None @_helpers.copy_docstring(crypt.Signer) def sign(self, message): response = self._make_signing_request(message) return base64.b64decode(response['signature']) google-auth-1.5.1/google/auth/credentials.py0000644110576702575230000002651113301614745024164 0ustar theaflowersprimarygroup00000000000000# 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 ` 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 because 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) class AnonymousCredentials(Credentials): """Credentials that do not provide any authentication information. These are useful in the case of services that support anonymous access or local service emulators that do not use credentials. """ @property def expired(self): """Returns `False`, anonymous credentials never expire.""" return False @property def valid(self): """Returns `True`, anonymous credentials are always valid.""" return True def refresh(self, request): """Raises :class:`ValueError``, anonymous credentials cannot be refreshed.""" raise ValueError("Anonymous credentials cannot be refreshed.") def apply(self, headers, token=None): """Anonymous credentials do nothing to the request. The optional ``token`` argument is not supported. Raises: ValueError: If a token was specified. """ if token is not None: raise ValueError("Anonymous credentials don't support tokens.") def before_request(self, request, method, url, headers): """Anonymous credentials do nothing to the request.""" @six.add_metaclass(abc.ABCMeta) class ReadOnlyScoped(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`_. 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.with_scopes(scopes=['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(ReadOnlyScoped, 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 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`. Args: scopes (Sequence[str]): The list of scopes to check. Returns: bool: True if the credentials have the given scopes. """ return set(scopes).issubset(set(self._scopes or [])) class Scoped(ReadOnlyScoped): """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. Args: scopes (Sequence[str]): The list of scopes to attach to the current credentials. 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 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.') google-auth-1.5.1/google/auth/crypt/0000755110576702575230000000000013330167570022452 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google/auth/crypt/_python_rsa.py0000644110576702575230000001355613277071027025364 0ustar theaflowersprimarygroup00000000000000# 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. """Pure-Python RSA cryptography implementation. Uses the ``rsa``, ``pyasn1`` and ``pyasn1_modules`` packages to parse PEM files storing PKCS#1 or PKCS#8 keys as well as certificates. There is no support for p12 files. """ from __future__ import absolute_import from pyasn1.codec.der import decoder from pyasn1_modules import pem from pyasn1_modules.rfc2459 import Certificate from pyasn1_modules.rfc5208 import PrivateKeyInfo import rsa import six from google.auth import _helpers from google.auth.crypt import base _POW2 = (128, 64, 32, 16, 8, 4, 2, 1) _CERTIFICATE_MARKER = b'-----BEGIN CERTIFICATE-----' _PKCS1_MARKER = ('-----BEGIN RSA PRIVATE KEY-----', '-----END RSA PRIVATE KEY-----') _PKCS8_MARKER = ('-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----') _PKCS8_SPEC = PrivateKeyInfo() def _bit_list_to_bytes(bit_list): """Converts an iterable of 1s and 0s to bytes. Combines the list 8 at a time, treating each group of 8 bits as a single byte. Args: bit_list (Sequence): Sequence of 1s and 0s. Returns: bytes: The decoded bytes. """ num_bits = len(bit_list) byte_vals = bytearray() for start in six.moves.xrange(0, num_bits, 8): curr_bits = bit_list[start:start + 8] char_val = sum( val * digit for val, digit in six.moves.zip(_POW2, curr_bits)) byte_vals.append(char_val) return bytes(byte_vals) class RSAVerifier(base.Verifier): """Verifies RSA cryptographic signatures using public keys. Args: public_key (rsa.key.PublicKey): The public key used to verify signatures. """ def __init__(self, public_key): self._pubkey = public_key @_helpers.copy_docstring(base.Verifier) def verify(self, message, signature): message = _helpers.to_bytes(message) try: return rsa.pkcs1.verify(message, signature, self._pubkey) except (ValueError, rsa.pkcs1.VerificationError): return False @classmethod def from_string(cls, public_key): """Construct an Verifier instance from a public key or public certificate string. Args: public_key (Union[str, bytes]): The public key in PEM format or the x509 public key certificate. Returns: Verifier: The constructed verifier. Raises: ValueError: If the public_key can't be parsed. """ public_key = _helpers.to_bytes(public_key) is_x509_cert = _CERTIFICATE_MARKER in public_key # If this is a certificate, extract the public key info. if is_x509_cert: der = rsa.pem.load_pem(public_key, 'CERTIFICATE') asn1_cert, remaining = decoder.decode(der, asn1Spec=Certificate()) if remaining != b'': raise ValueError('Unused bytes', remaining) cert_info = asn1_cert['tbsCertificate']['subjectPublicKeyInfo'] key_bytes = _bit_list_to_bytes(cert_info['subjectPublicKey']) pubkey = rsa.PublicKey.load_pkcs1(key_bytes, 'DER') else: pubkey = rsa.PublicKey.load_pkcs1(public_key, 'PEM') return cls(pubkey) class RSASigner(base.Signer, base.FromServiceAccountMixin): """Signs messages with an RSA private key. Args: private_key (rsa.key.PrivateKey): The private key to sign with. key_id (str): Optional key ID used to identify this private key. This can be useful to associate the private key with its associated public key or certificate. """ def __init__(self, private_key, key_id=None): self._key = private_key self._key_id = key_id @property @_helpers.copy_docstring(base.Signer) def key_id(self): return self._key_id @_helpers.copy_docstring(base.Signer) def sign(self, message): message = _helpers.to_bytes(message) return rsa.pkcs1.sign(message, self._key, 'SHA-256') @classmethod def from_string(cls, key, key_id=None): """Construct an Signer instance from a private key in PEM format. Args: key (str): Private key in PEM format. key_id (str): An optional key id used to identify the private key. Returns: google.auth.crypt.Signer: The constructed signer. Raises: ValueError: If the key cannot be parsed as PKCS#1 or PKCS#8 in PEM format. """ key = _helpers.from_bytes(key) # PEM expects str in Python 3 marker_id, key_bytes = pem.readPemBlocksFromFile( six.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER) # Key is in pkcs1 format. if marker_id == 0: private_key = rsa.key.PrivateKey.load_pkcs1( key_bytes, format='DER') # Key is in pkcs8. elif marker_id == 1: key_info, remaining = decoder.decode( key_bytes, asn1Spec=_PKCS8_SPEC) if remaining != b'': raise ValueError('Unused bytes', remaining) private_key_info = key_info.getComponentByName('privateKey') private_key = rsa.key.PrivateKey.load_pkcs1( private_key_info.asOctets(), format='DER') else: raise ValueError('No key could be detected.') return cls(private_key, key_id=key_id) google-auth-1.5.1/google/auth/crypt/rsa.py0000644110576702575230000000206613277071027023616 0ustar theaflowersprimarygroup00000000000000# Copyright 2017 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. """RSA cryptography signer and verifier.""" try: # Prefer cryptograph-based RSA implementation. from google.auth.crypt import _cryptography_rsa RSASigner = _cryptography_rsa.RSASigner RSAVerifier = _cryptography_rsa.RSAVerifier except ImportError: # pragma: NO COVER # Fallback to pure-python RSA implementation if cryptography is # unavailable. from google.auth.crypt import _python_rsa RSASigner = _python_rsa.RSASigner RSAVerifier = _python_rsa.RSAVerifier google-auth-1.5.1/google/auth/crypt/__init__.py0000644110576702575230000000461513277071027024572 0ustar theaflowersprimarygroup00000000000000# 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. """Cryptography helpers for verifying and signing messages. The simplest way to verify signatures is using :func:`verify_signature`:: cert = open('certs.pem').read() valid = crypt.verify_signature(message, signature, cert) If you're going to verify many messages with the same certificate, you can use :class:`RSAVerifier`:: cert = open('certs.pem').read() verifier = crypt.RSAVerifier.from_string(cert) valid = verifier.verify(message, signature) To sign messages use :class:`RSASigner` with a private key:: private_key = open('private_key.pem').read() signer = crypt.RSASigner.from_string(private_key) signature = signer.sign(message) """ import six from google.auth.crypt import base from google.auth.crypt import rsa __all__ = [ 'RSASigner', 'RSAVerifier', 'Signer', 'Verifier', ] # Aliases to maintain the v1.0.0 interface, as the crypt module was split # into submodules. Signer = base.Signer Verifier = base.Verifier RSASigner = rsa.RSASigner RSAVerifier = rsa.RSAVerifier def verify_signature(message, signature, certs): """Verify an RSA cryptographic signature. Checks that the provided ``signature`` was generated from ``bytes`` using the private key associated with the ``cert``. Args: message (Union[str, bytes]): The plaintext message. signature (Union[str, bytes]): The cryptographic signature to check. certs (Union[Sequence, str, bytes]): The certificate or certificates to use to check the signature. Returns: bool: True if the signature is valid, otherwise False. """ if isinstance(certs, (six.text_type, six.binary_type)): certs = [certs] for cert in certs: verifier = rsa.RSAVerifier.from_string(cert) if verifier.verify(message, signature): return True return False google-auth-1.5.1/google/auth/crypt/_cryptography_rsa.py0000644110576702575230000001161013277071027026563 0ustar theaflowersprimarygroup00000000000000# Copyright 2017 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. """RSA verifier and signer that use the ``cryptography`` library. This is a much faster implementation than the default (in ``google.auth.crypt._python_rsa``), which depends on the pure-Python ``rsa`` library. """ import cryptography.exceptions from cryptography.hazmat import backends from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding import cryptography.x509 import pkg_resources from google.auth import _helpers from google.auth.crypt import base _IMPORT_ERROR_MSG = ( 'cryptography>=1.4.0 is required to use cryptography-based RSA ' 'implementation.') try: # pragma: NO COVER release = pkg_resources.get_distribution('cryptography').parsed_version if release < pkg_resources.parse_version('1.4.0'): raise ImportError(_IMPORT_ERROR_MSG) except pkg_resources.DistributionNotFound: # pragma: NO COVER raise ImportError(_IMPORT_ERROR_MSG) _CERTIFICATE_MARKER = b'-----BEGIN CERTIFICATE-----' _BACKEND = backends.default_backend() _PADDING = padding.PKCS1v15() _SHA256 = hashes.SHA256() class RSAVerifier(base.Verifier): """Verifies RSA cryptographic signatures using public keys. Args: public_key ( cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey): The public key used to verify signatures. """ def __init__(self, public_key): self._pubkey = public_key @_helpers.copy_docstring(base.Verifier) def verify(self, message, signature): message = _helpers.to_bytes(message) try: self._pubkey.verify(signature, message, _PADDING, _SHA256) return True except (ValueError, cryptography.exceptions.InvalidSignature): return False @classmethod def from_string(cls, public_key): """Construct an Verifier instance from a public key or public certificate string. Args: public_key (Union[str, bytes]): The public key in PEM format or the x509 public key certificate. Returns: Verifier: The constructed verifier. Raises: ValueError: If the public key can't be parsed. """ public_key_data = _helpers.to_bytes(public_key) if _CERTIFICATE_MARKER in public_key_data: cert = cryptography.x509.load_pem_x509_certificate( public_key_data, _BACKEND) pubkey = cert.public_key() else: pubkey = serialization.load_pem_public_key( public_key_data, _BACKEND) return cls(pubkey) class RSASigner(base.Signer, base.FromServiceAccountMixin): """Signs messages with an RSA private key. Args: private_key ( cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey): The private key to sign with. key_id (str): Optional key ID used to identify this private key. This can be useful to associate the private key with its associated public key or certificate. """ def __init__(self, private_key, key_id=None): self._key = private_key self._key_id = key_id @property @_helpers.copy_docstring(base.Signer) def key_id(self): return self._key_id @_helpers.copy_docstring(base.Signer) def sign(self, message): message = _helpers.to_bytes(message) return self._key.sign( message, _PADDING, _SHA256) @classmethod def from_string(cls, key, key_id=None): """Construct a RSASigner from a private key in PEM format. Args: key (Union[bytes, str]): Private key in PEM format. key_id (str): An optional key id used to identify the private key. Returns: google.auth.crypt._cryptography_rsa.RSASigner: The constructed signer. Raises: ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode). UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded into a UTF-8 ``str``. ValueError: If ``cryptography`` "Could not deserialize key data." """ key = _helpers.to_bytes(key) private_key = serialization.load_pem_private_key( key, password=None, backend=_BACKEND) return cls(private_key, key_id=key_id) google-auth-1.5.1/google/auth/crypt/base.py0000644110576702575230000001016213277071027023737 0ustar theaflowersprimarygroup00000000000000# 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. """Base classes for cryptographic signers and verifiers.""" import abc import io import json import six _JSON_FILE_PRIVATE_KEY = 'private_key' _JSON_FILE_PRIVATE_KEY_ID = 'private_key_id' @six.add_metaclass(abc.ABCMeta) class Verifier(object): """Abstract base class for crytographic signature verifiers.""" @abc.abstractmethod def verify(self, message, signature): """Verifies a message against a cryptographic signature. Args: message (Union[str, bytes]): The message to verify. signature (Union[str, bytes]): The cryptography signature to check. Returns: bool: True if message was signed by the private key associated with the public key that this object was constructed with. """ # pylint: disable=missing-raises-doc,redundant-returns-doc # (pylint doesn't recognize that this is abstract) raise NotImplementedError('Verify must be implemented') @six.add_metaclass(abc.ABCMeta) class Signer(object): """Abstract base class for cryptographic signers.""" @abc.abstractproperty def key_id(self): """Optional[str]: The key ID used to identify this private key.""" raise NotImplementedError('Key id must be implemented') @abc.abstractmethod def sign(self, message): """Signs a message. Args: message (Union[str, bytes]): The message to be signed. Returns: bytes: The signature of the message. """ # pylint: disable=missing-raises-doc,redundant-returns-doc # (pylint doesn't recognize that this is abstract) raise NotImplementedError('Sign must be implemented') @six.add_metaclass(abc.ABCMeta) class FromServiceAccountMixin(object): """Mix-in to enable factory constructors for a Signer.""" @abc.abstractmethod def from_string(cls, key, key_id=None): """Construct an Signer instance from a private key string. Args: key (str): Private key as a string. key_id (str): An optional key id used to identify the private key. Returns: google.auth.crypt.Signer: The constructed signer. Raises: ValueError: If the key cannot be parsed. """ raise NotImplementedError('from_string must be implemented') @classmethod def from_service_account_info(cls, info): """Creates a Signer instance instance from a dictionary containing service account info in Google format. Args: info (Mapping[str, str]): The service account info in Google format. Returns: google.auth.crypt.Signer: The constructed signer. Raises: ValueError: If the info is not in the expected format. """ if _JSON_FILE_PRIVATE_KEY not in info: raise ValueError( 'The private_key field was not found in the service account ' 'info.') return cls.from_string( info[_JSON_FILE_PRIVATE_KEY], info.get(_JSON_FILE_PRIVATE_KEY_ID)) @classmethod def from_service_account_file(cls, filename): """Creates a Signer instance from a service account .json file in Google format. Args: filename (str): The path to the service account .json file. Returns: google.auth.crypt.Signer: The constructed signer. """ with io.open(filename, 'r', encoding='utf-8') as json_file: data = json.load(json_file) return cls.from_service_account_info(data) google-auth-1.5.1/google/auth/crypt/_helpers.py0000644110576702575230000000000013277071027024614 0ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google/auth/app_engine.py0000644110576702575230000001153313277071027023774 0ustar theaflowersprimarygroup00000000000000# 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. """Google App Engine standard environment support. This module provides authentication and signing for applications running on App Engine in the standard environment using the `App Identity API`_. .. _App Identity API: https://cloud.google.com/appengine/docs/python/appidentity/ """ import datetime from google.auth import _helpers from google.auth import credentials from google.auth import crypt try: from google.appengine.api import app_identity except ImportError: app_identity = None class Signer(crypt.Signer): """Signs messages using the App Engine App Identity service. This can be used in place of :class:`google.auth.crypt.Signer` when running in the App Engine standard environment. """ @property def key_id(self): """Optional[str]: The key ID used to identify this private key. .. warning:: This is always ``None``. The key ID used by App Engine can not be reliably determined ahead of time. """ return None @_helpers.copy_docstring(crypt.Signer) def sign(self, message): message = _helpers.to_bytes(message) _, signature = app_identity.sign_blob(message) return signature def get_project_id(): """Gets the project ID for the current App Engine application. Returns: str: The project ID Raises: EnvironmentError: If the App Engine APIs are unavailable. """ # pylint: disable=missing-raises-doc # Pylint rightfully thinks EnvironmentError is OSError, but doesn't # realize it's a valid alias. if app_identity is None: raise EnvironmentError( 'The App Engine APIs are not available.') return app_identity.get_application_id() class Credentials(credentials.Scoped, credentials.Signing, credentials.Credentials): """App Engine standard environment credentials. These credentials use the App Engine App Identity API to obtain access tokens. """ def __init__(self, scopes=None, service_account_id=None): """ Args: scopes (Sequence[str]): Scopes to request from the App Identity API. service_account_id (str): The service account ID passed into :func:`google.appengine.api.app_identity.get_access_token`. If not specified, the default application service account ID will be used. Raises: EnvironmentError: If the App Engine APIs are unavailable. """ # pylint: disable=missing-raises-doc # Pylint rightfully thinks EnvironmentError is OSError, but doesn't # realize it's a valid alias. if app_identity is None: raise EnvironmentError( 'The App Engine APIs are not available.') super(Credentials, self).__init__() self._scopes = scopes self._service_account_id = service_account_id self._signer = Signer() @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): # pylint: disable=unused-argument token, ttl = app_identity.get_access_token( self._scopes, self._service_account_id) expiry = datetime.datetime.utcfromtimestamp(ttl) self.token, self.expiry = token, expiry @property def service_account_email(self): """The service account email.""" if self._service_account_id is None: self._service_account_id = app_identity.get_service_account_name() return self._service_account_id @property def requires_scopes(self): """Checks if the credentials requires scopes. Returns: bool: True if there are no scopes set otherwise False. """ return not self._scopes @_helpers.copy_docstring(credentials.Scoped) def with_scopes(self, scopes): return self.__class__( scopes=scopes, service_account_id=self._service_account_id) @_helpers.copy_docstring(credentials.Signing) def sign_bytes(self, message): return self._signer.sign(message) @property @_helpers.copy_docstring(credentials.Signing) def signer_email(self): return self.service_account_email @property @_helpers.copy_docstring(credentials.Signing) def signer(self): return self._signer google-auth-1.5.1/google/auth/__init__.py0000644110576702575230000000150413277071027023423 0ustar theaflowersprimarygroup00000000000000# 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. """Google Auth Library for Python.""" import logging from google.auth._default import default __all__ = [ 'default', ] # Set default logging handler to avoid "No handler found" warnings. logging.getLogger(__name__).addHandler(logging.NullHandler()) google-auth-1.5.1/google/auth/_service_account_info.py0000644110576702575230000000445613277071027026223 0ustar theaflowersprimarygroup00000000000000# 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. """Helper functions for loading data from a Google service account file.""" import io import json import six from google.auth import crypt def from_dict(data, require=None): """Validates a dictionary containing Google service account data. Creates and returns a :class:`google.auth.crypt.Signer` instance from the private key specified in the data. Args: data (Mapping[str, str]): The service account data require (Sequence[str]): List of keys required to be present in the info. Returns: google.auth.crypt.Signer: A signer created from the private key in the service account file. Raises: ValueError: if the data was in the wrong format, or if one of the required keys is missing. """ keys_needed = set(require if require is not None else []) missing = keys_needed.difference(six.iterkeys(data)) if missing: raise ValueError( 'Service account info was not in the expected format, missing ' 'fields {}.'.format(', '.join(missing))) # Create a signer. signer = crypt.RSASigner.from_service_account_info(data) return signer def from_filename(filename, require=None): """Reads a Google service account JSON file and returns its parsed info. Args: filename (str): The path to the service account .json file. require (Sequence[str]): List of keys required to be present in the info. Returns: Tuple[ Mapping[str, str], google.auth.crypt.Signer ]: The verified info and a signer instance. """ with io.open(filename, 'r', encoding='utf-8') as json_file: data = json.load(json_file) return data, from_dict(data, require=require) google-auth-1.5.1/google/auth/_oauth2client.py0000644110576702575230000001327713277071027024436 0ustar theaflowersprimarygroup00000000000000# 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. """Helpers for transitioning from oauth2client to google-auth. .. warning:: This module is private as it is intended to assist first-party downstream clients with the transition from oauth2client to google-auth. """ from __future__ import absolute_import import six from google.auth import _helpers import google.auth.app_engine import google.oauth2.credentials import google.oauth2.service_account try: import oauth2client.client import oauth2client.contrib.gce import oauth2client.service_account except ImportError as caught_exc: six.raise_from( ImportError('oauth2client is not installed.'), caught_exc) try: import oauth2client.contrib.appengine _HAS_APPENGINE = True except ImportError: _HAS_APPENGINE = False _CONVERT_ERROR_TMPL = ( 'Unable to convert {} to a google-auth credentials class.') def _convert_oauth2_credentials(credentials): """Converts to :class:`google.oauth2.credentials.Credentials`. Args: credentials (Union[oauth2client.client.OAuth2Credentials, oauth2client.client.GoogleCredentials]): The credentials to convert. Returns: google.oauth2.credentials.Credentials: The converted credentials. """ new_credentials = google.oauth2.credentials.Credentials( token=credentials.access_token, refresh_token=credentials.refresh_token, token_uri=credentials.token_uri, client_id=credentials.client_id, client_secret=credentials.client_secret, scopes=credentials.scopes) new_credentials._expires = credentials.token_expiry return new_credentials def _convert_service_account_credentials(credentials): """Converts to :class:`google.oauth2.service_account.Credentials`. Args: credentials (Union[ oauth2client.service_account.ServiceAccountCredentials, oauth2client.service_account._JWTAccessCredentials]): The credentials to convert. Returns: google.oauth2.service_account.Credentials: The converted credentials. """ info = credentials.serialization_data.copy() info['token_uri'] = credentials.token_uri return google.oauth2.service_account.Credentials.from_service_account_info( info) def _convert_gce_app_assertion_credentials(credentials): """Converts to :class:`google.auth.compute_engine.Credentials`. Args: credentials (oauth2client.contrib.gce.AppAssertionCredentials): The credentials to convert. Returns: google.oauth2.service_account.Credentials: The converted credentials. """ return google.auth.compute_engine.Credentials( service_account_email=credentials.service_account_email) def _convert_appengine_app_assertion_credentials(credentials): """Converts to :class:`google.auth.app_engine.Credentials`. Args: credentials (oauth2client.contrib.app_engine.AppAssertionCredentials): The credentials to convert. Returns: google.oauth2.service_account.Credentials: The converted credentials. """ # pylint: disable=invalid-name return google.auth.app_engine.Credentials( scopes=_helpers.string_to_scopes(credentials.scope), service_account_id=credentials.service_account_id) _CLASS_CONVERSION_MAP = { oauth2client.client.OAuth2Credentials: _convert_oauth2_credentials, oauth2client.client.GoogleCredentials: _convert_oauth2_credentials, oauth2client.service_account.ServiceAccountCredentials: _convert_service_account_credentials, oauth2client.service_account._JWTAccessCredentials: _convert_service_account_credentials, oauth2client.contrib.gce.AppAssertionCredentials: _convert_gce_app_assertion_credentials, } if _HAS_APPENGINE: _CLASS_CONVERSION_MAP[ oauth2client.contrib.appengine.AppAssertionCredentials] = ( _convert_appengine_app_assertion_credentials) def convert(credentials): """Convert oauth2client credentials to google-auth credentials. This class converts: - :class:`oauth2client.client.OAuth2Credentials` to :class:`google.oauth2.credentials.Credentials`. - :class:`oauth2client.client.GoogleCredentials` to :class:`google.oauth2.credentials.Credentials`. - :class:`oauth2client.service_account.ServiceAccountCredentials` to :class:`google.oauth2.service_account.Credentials`. - :class:`oauth2client.service_account._JWTAccessCredentials` to :class:`google.oauth2.service_account.Credentials`. - :class:`oauth2client.contrib.gce.AppAssertionCredentials` to :class:`google.auth.compute_engine.Credentials`. - :class:`oauth2client.contrib.appengine.AppAssertionCredentials` to :class:`google.auth.app_engine.Credentials`. Returns: google.auth.credentials.Credentials: The converted credentials. Raises: ValueError: If the credentials could not be converted. """ credentials_class = type(credentials) try: return _CLASS_CONVERSION_MAP[credentials_class](credentials) except KeyError as caught_exc: new_exc = ValueError(_CONVERT_ERROR_TMPL.format(credentials_class)) six.raise_from(new_exc, caught_exc) google-auth-1.5.1/google/auth/_default.py0000644110576702575230000002662413304067320023451 0ustar theaflowersprimarygroup00000000000000# Copyright 2015 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. """Application default credentials. Implements application default credentials and project ID detection. """ import io import json import logging import os import warnings import six from google.auth import environment_vars from google.auth import exceptions import google.auth.transport._http_client _LOGGER = logging.getLogger(__name__) # Valid types accepted for file-based credentials. _AUTHORIZED_USER_TYPE = 'authorized_user' _SERVICE_ACCOUNT_TYPE = 'service_account' _VALID_TYPES = (_AUTHORIZED_USER_TYPE, _SERVICE_ACCOUNT_TYPE) # Help message when no credentials can be found. _HELP_MESSAGE = """\ Could not automatically determine credentials. Please set {env} or \ explicitly create credentials and re-run the application. For more \ information, please see \ https://developers.google.com/accounts/docs/application-default-credentials. """.format(env=environment_vars.CREDENTIALS).strip() # Warning when using Cloud SDK user credentials _CLOUD_SDK_CREDENTIALS_WARNING = """\ Your application has authenticated using end user credentials from Google \ Cloud SDK. We recommend that most server applications use service accounts \ instead. If your application continues to use end user credentials from Cloud \ SDK, you might receive a "quota exceeded" or "API not enabled" error. For \ more information about service accounts, see \ https://cloud.google.com/docs/authentication/.""" def _warn_about_problematic_credentials(credentials): """Determines if the credentials are problematic. Credentials from the Cloud SDK that are associated with Cloud SDK's project are problematic because they may not have APIs enabled and have limited quota. If this is the case, warn about it. """ from google.auth import _cloud_sdk if credentials.client_id == _cloud_sdk.CLOUD_SDK_CLIENT_ID: warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING) def _load_credentials_from_file(filename): """Loads credentials from a file. The credentials file must be a service account key or stored authorized user credentials. Args: filename (str): The full path to the credentials file. Returns: Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded credentials and the project ID. Authorized user credentials do not have the project ID information. Raises: google.auth.exceptions.DefaultCredentialsError: if the file is in the wrong format or is missing. """ if not os.path.exists(filename): raise exceptions.DefaultCredentialsError( 'File {} was not found.'.format(filename)) with io.open(filename, 'r') as file_obj: try: info = json.load(file_obj) except ValueError as caught_exc: new_exc = exceptions.DefaultCredentialsError( 'File {} is not a valid json file.'.format(filename), caught_exc) six.raise_from(new_exc, caught_exc) # The type key should indicate that the file is either a service account # credentials file or an authorized user credentials file. credential_type = info.get('type') if credential_type == _AUTHORIZED_USER_TYPE: from google.auth import _cloud_sdk try: credentials = _cloud_sdk.load_authorized_user_credentials(info) except ValueError as caught_exc: msg = 'Failed to load authorized user credentials from {}'.format( filename) new_exc = exceptions.DefaultCredentialsError(msg, caught_exc) six.raise_from(new_exc, caught_exc) # Authorized user credentials do not contain the project ID. _warn_about_problematic_credentials(credentials) return credentials, None elif credential_type == _SERVICE_ACCOUNT_TYPE: from google.oauth2 import service_account try: credentials = ( service_account.Credentials.from_service_account_info(info)) except ValueError as caught_exc: msg = 'Failed to load service account credentials from {}'.format( filename) new_exc = exceptions.DefaultCredentialsError(msg, caught_exc) six.raise_from(new_exc, caught_exc) return credentials, info.get('project_id') else: raise exceptions.DefaultCredentialsError( 'The file {file} does not have a valid type. ' 'Type is {type}, expected one of {valid_types}.'.format( file=filename, type=credential_type, valid_types=_VALID_TYPES)) def _get_gcloud_sdk_credentials(): """Gets the credentials and project ID from the Cloud SDK.""" from google.auth import _cloud_sdk # Check if application default credentials exist. credentials_filename = ( _cloud_sdk.get_application_default_credentials_path()) if not os.path.isfile(credentials_filename): return None, None credentials, project_id = _load_credentials_from_file( credentials_filename) if not project_id: project_id = _cloud_sdk.get_project_id() return credentials, project_id def _get_explicit_environ_credentials(): """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment variable.""" explicit_file = os.environ.get(environment_vars.CREDENTIALS) if explicit_file is not None: credentials, project_id = _load_credentials_from_file( os.environ[environment_vars.CREDENTIALS]) return credentials, project_id else: return None, None def _get_gae_credentials(): """Gets Google App Engine App Identity credentials and project ID.""" from google.auth import app_engine try: credentials = app_engine.Credentials() project_id = app_engine.get_project_id() return credentials, project_id except EnvironmentError: return None, None def _get_gce_credentials(request=None): """Gets credentials and project ID from the GCE Metadata Service.""" # Ping requires a transport, but we want application default credentials # to require no arguments. So, we'll use the _http_client transport which # uses http.client. This is only acceptable because the metadata server # doesn't do SSL and never requires proxies. from google.auth import compute_engine from google.auth.compute_engine import _metadata if request is None: request = google.auth.transport._http_client.Request() if _metadata.ping(request=request): # Get the project ID. try: project_id = _metadata.get_project_id(request=request) except exceptions.TransportError: project_id = None return compute_engine.Credentials(), project_id else: return None, None def default(scopes=None, request=None): """Gets the default credentials for the current environment. `Application Default Credentials`_ provides an easy way to obtain credentials to call Google APIs for server-to-server or local applications. This function acquires credentials from the environment in the following order: 1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set to the path of a valid service account JSON private key file, then it is loaded and returned. The project ID returned is the project ID defined in the service account file if available (some older files do not contain project ID information). 2. If the `Google Cloud SDK`_ is installed and has application default credentials set they are loaded and returned. To enable application default credentials with the Cloud SDK run:: gcloud auth application-default login If the Cloud SDK has an active project, the project ID is returned. The active project can be set using:: gcloud config set project 3. If the application is running in the `App Engine standard environment`_ then the credentials and project ID from the `App Identity Service`_ are used. 4. If the application is running in `Compute Engine`_ or the `App Engine flexible environment`_ then the credentials and project ID are obtained from the `Metadata Service`_. 5. If no credentials are found, :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised. .. _Application Default Credentials: https://developers.google.com\ /identity/protocols/application-default-credentials .. _Google Cloud SDK: https://cloud.google.com/sdk .. _App Engine standard environment: https://cloud.google.com/appengine .. _App Identity Service: https://cloud.google.com/appengine/docs/python\ /appidentity/ .. _Compute Engine: https://cloud.google.com/compute .. _App Engine flexible environment: https://cloud.google.com\ /appengine/flexible .. _Metadata Service: https://cloud.google.com/compute/docs\ /storing-retrieving-metadata Example:: import google.auth credentials, project_id = google.auth.default() Args: scopes (Sequence[str]): The list of scopes for the credentials. If specified, the credentials will automatically be scoped if necessary. request (google.auth.transport.Request): An object used to make HTTP requests. This is used to detect whether the application is running on Compute Engine. If not specified, then it will use the standard library http client to make requests. Returns: Tuple[~google.auth.credentials.Credentials, Optional[str]]: the current environment's credentials and project ID. Project ID may be None, which indicates that the Project ID could not be ascertained from the environment. Raises: ~google.auth.exceptions.DefaultCredentialsError: If no credentials were found, or if the credentials found were invalid. """ from google.auth.credentials import with_scopes_if_required explicit_project_id = os.environ.get( environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)) checkers = ( _get_explicit_environ_credentials, _get_gcloud_sdk_credentials, _get_gae_credentials, lambda: _get_gce_credentials(request)) for checker in checkers: credentials, project_id = checker() if credentials is not None: credentials = with_scopes_if_required(credentials, scopes) effective_project_id = explicit_project_id or project_id if not effective_project_id: _LOGGER.warning( 'No project ID could be determined. Consider running ' '`gcloud config set project` or setting the %s ' 'environment variable', environment_vars.PROJECT) return credentials, effective_project_id raise exceptions.DefaultCredentialsError(_HELP_MESSAGE) google-auth-1.5.1/google/auth/exceptions.py0000644110576702575230000000205513277071027024047 0ustar theaflowersprimarygroup00000000000000# 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. """Exceptions used in the google.auth package.""" class GoogleAuthError(Exception): """Base class for all google.auth errors.""" class TransportError(GoogleAuthError): """Used to indicate an error occurred during an HTTP request.""" class RefreshError(GoogleAuthError): """Used to indicate that an refreshing the credentials' access token failed.""" class DefaultCredentialsError(GoogleAuthError): """Used to indicate that acquiring default credentials failed.""" google-auth-1.5.1/google/auth/environment_vars.py0000644110576702575230000000344413277071027025270 0ustar theaflowersprimarygroup00000000000000# 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. """Environment variables used by :mod:`google.auth`.""" PROJECT = 'GOOGLE_CLOUD_PROJECT' """Environment variable defining default project. This used by :func:`google.auth.default` to explicitly set a project ID. This environment variable is also used by the Google Cloud Python Library. """ LEGACY_PROJECT = 'GCLOUD_PROJECT' """Previously used environment variable defining the default project. This environment variable is used instead of the current one in some situations (such as Google App Engine). """ CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS' """Environment variable defining the location of Google application default credentials.""" # The environment variable name which can replace ~/.config if set. CLOUD_SDK_CONFIG_DIR = 'CLOUDSDK_CONFIG' """Environment variable defines the location of Google Cloud SDK's config files.""" # These two variables allow for customization of the addresses used when # contacting the GCE metadata service. GCE_METADATA_ROOT = 'GCE_METADATA_ROOT' """Environment variable providing an alternate hostname or host:port to be used for GCE metadata requests.""" GCE_METADATA_IP = 'GCE_METADATA_IP' """Environment variable providing an alternate ip:port to be used for ip-only GCE metadata requests.""" google-auth-1.5.1/google/auth/_cloud_sdk.py0000644110576702575230000000757313304067320023776 0ustar theaflowersprimarygroup00000000000000# Copyright 2015 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. """Helpers for reading the Google Cloud SDK's configuration.""" import json import os import subprocess from google.auth import environment_vars import google.oauth2.credentials # The ~/.config subdirectory containing gcloud credentials. _CONFIG_DIRECTORY = 'gcloud' # Windows systems store config at %APPDATA%\gcloud _WINDOWS_CONFIG_ROOT_ENV_VAR = 'APPDATA' # The name of the file in the Cloud SDK config that contains default # credentials. _CREDENTIALS_FILENAME = 'application_default_credentials.json' # The name of the Cloud SDK shell script _CLOUD_SDK_POSIX_COMMAND = 'gcloud' _CLOUD_SDK_WINDOWS_COMMAND = 'gcloud.cmd' # The command to get the Cloud SDK configuration _CLOUD_SDK_CONFIG_COMMAND = ('config', 'config-helper', '--format', 'json') # Cloud SDK's application-default client ID CLOUD_SDK_CLIENT_ID = ( '764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com') def get_config_path(): """Returns the absolute path the the Cloud SDK's configuration directory. Returns: str: The Cloud SDK config path. """ # If the path is explicitly set, return that. try: return os.environ[environment_vars.CLOUD_SDK_CONFIG_DIR] except KeyError: pass # Non-windows systems store this at ~/.config/gcloud if os.name != 'nt': return os.path.join( os.path.expanduser('~'), '.config', _CONFIG_DIRECTORY) # Windows systems store config at %APPDATA%\gcloud else: try: return os.path.join( os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], _CONFIG_DIRECTORY) except KeyError: # This should never happen unless someone is really # messing with things, but we'll cover the case anyway. drive = os.environ.get('SystemDrive', 'C:') return os.path.join( drive, '\\', _CONFIG_DIRECTORY) def get_application_default_credentials_path(): """Gets the path to the application default credentials file. The path may or may not exist. Returns: str: The full path to application default credentials. """ config_path = get_config_path() return os.path.join(config_path, _CREDENTIALS_FILENAME) def load_authorized_user_credentials(info): """Loads an authorized user credential. Args: info (Mapping[str, str]): The loaded file's data. Returns: google.oauth2.credentials.Credentials: The constructed credentials. Raises: ValueError: if the info is in the wrong format or missing data. """ return google.oauth2.credentials.Credentials.from_authorized_user_info( info) def get_project_id(): """Gets the project ID from the Cloud SDK. Returns: Optional[str]: The project ID. """ if os.name == 'nt': command = _CLOUD_SDK_WINDOWS_COMMAND else: command = _CLOUD_SDK_POSIX_COMMAND try: output = subprocess.check_output( (command,) + _CLOUD_SDK_CONFIG_COMMAND, stderr=subprocess.STDOUT) except (subprocess.CalledProcessError, OSError, IOError): return None try: configuration = json.loads(output.decode('utf-8')) except ValueError: return None try: return configuration['configuration']['properties']['core']['project'] except KeyError: return None google-auth-1.5.1/google/auth/_helpers.py0000644110576702575230000001412313277071027023466 0ustar theaflowersprimarygroup00000000000000# Copyright 2015 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. """Helper functions for commonly used utilities.""" import base64 import calendar import datetime import six from six.moves import urllib CLOCK_SKEW_SECS = 300 # 5 minutes in seconds CLOCK_SKEW = datetime.timedelta(seconds=CLOCK_SKEW_SECS) def copy_docstring(source_class): """Decorator that copies a method's docstring from another class. Args: source_class (type): The class that has the documented method. Returns: Callable: A decorator that will copy the docstring of the same named method in the source class to the decorated method. """ def decorator(method): """Decorator implementation. Args: method (Callable): The method to copy the docstring to. Returns: Callable: the same method passed in with an updated docstring. Raises: ValueError: if the method already has a docstring. """ if method.__doc__: raise ValueError('Method already has a docstring.') source_method = getattr(source_class, method.__name__) method.__doc__ = source_method.__doc__ return method return decorator def utcnow(): """Returns the current UTC datetime. Returns: datetime: The current time in UTC. """ return datetime.datetime.utcnow() def datetime_to_secs(value): """Convert a datetime object to the number of seconds since the UNIX epoch. Args: value (datetime): The datetime to convert. Returns: int: The number of seconds since the UNIX epoch. """ return calendar.timegm(value.utctimetuple()) def to_bytes(value, encoding='utf-8'): """Converts a string value to bytes, if necessary. Unfortunately, ``six.b`` is insufficient for this task since in Python 2 because it does not modify ``unicode`` objects. Args: value (Union[str, bytes]): The value to be converted. encoding (str): The encoding to use to convert unicode to bytes. Defaults to "utf-8". Returns: bytes: The original value converted to bytes (if unicode) or as passed in if it started out as bytes. Raises: ValueError: If the value could not be converted to bytes. """ result = (value.encode(encoding) if isinstance(value, six.text_type) else value) if isinstance(result, six.binary_type): return result else: raise ValueError('{0!r} could not be converted to bytes'.format(value)) def from_bytes(value): """Converts bytes to a string value, if necessary. Args: value (Union[str, bytes]): The value to be converted. Returns: str: The original value converted to unicode (if bytes) or as passed in if it started out as unicode. Raises: ValueError: If the value could not be converted to unicode. """ result = (value.decode('utf-8') if isinstance(value, six.binary_type) else value) if isinstance(result, six.text_type): return result else: raise ValueError( '{0!r} could not be converted to unicode'.format(value)) def update_query(url, params, remove=None): """Updates a URL's query parameters. Replaces any current values if they are already present in the URL. Args: url (str): The URL to update. params (Mapping[str, str]): A mapping of query parameter keys to values. remove (Sequence[str]): Parameters to remove from the query string. Returns: str: The URL with updated query parameters. Examples: >>> url = 'http://example.com?a=1' >>> update_query(url, {'a': '2'}) http://example.com?a=2 >>> update_query(url, {'b': '3'}) http://example.com?a=1&b=3 >> update_query(url, {'b': '3'}, remove=['a']) http://example.com?b=3 """ if remove is None: remove = [] # Split the URL into parts. parts = urllib.parse.urlparse(url) # Parse the query string. query_params = urllib.parse.parse_qs(parts.query) # Update the query parameters with the new parameters. query_params.update(params) # Remove any values specified in remove. query_params = { key: value for key, value in six.iteritems(query_params) if key not in remove} # Re-encoded the query string. new_query = urllib.parse.urlencode(query_params, doseq=True) # Unsplit the url. new_parts = parts._replace(query=new_query) return urllib.parse.urlunparse(new_parts) def scopes_to_string(scopes): """Converts scope value to a string suitable for sending to OAuth 2.0 authorization servers. Args: scopes (Sequence[str]): The sequence of scopes to convert. Returns: str: The scopes formatted as a single string. """ return ' '.join(scopes) def string_to_scopes(scopes): """Converts stringifed scopes value to a list. Args: scopes (Union[Sequence, str]): The string of space-separated scopes to convert. Returns: Sequence(str): The separated scopes. """ if not scopes: return [] return scopes.split(' ') def padded_urlsafe_b64decode(value): """Decodes base64 strings lacking padding characters. Google infrastructure tends to omit the base64 padding characters. Args: value (Union[str, bytes]): The encoded value. Returns: bytes: The decoded value """ b64string = to_bytes(value) padded = b64string + b'=' * (-len(b64string) % 4) return base64.urlsafe_b64decode(padded) google-auth-1.5.1/google/auth/jwt.py0000644110576702575230000006412513301614745022476 0ustar theaflowersprimarygroup00000000000000# 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. """JSON Web Tokens Provides support for creating (encoding) and verifying (decoding) JWTs, especially JWTs generated and consumed by Google infrastructure. See `rfc7519`_ for more details on JWTs. To encode a JWT use :func:`encode`:: from google.auth import crypt from google.auth import jwt signer = crypt.Signer(private_key) payload = {'some': 'payload'} encoded = jwt.encode(signer, payload) To decode a JWT and verify claims use :func:`decode`:: claims = jwt.decode(encoded, certs=public_certs) You can also skip verification:: claims = jwt.decode(encoded, verify=False) .. _rfc7519: https://tools.ietf.org/html/rfc7519 """ import base64 import collections import copy import datetime import json import cachetools import six from six.moves import urllib from google.auth import _helpers from google.auth import _service_account_info from google.auth import crypt from google.auth import exceptions import google.auth.credentials _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds _DEFAULT_MAX_CACHE_SIZE = 10 def encode(signer, payload, header=None, key_id=None): """Make a signed JWT. Args: signer (google.auth.crypt.Signer): The signer used to sign the JWT. payload (Mapping[str, str]): The JWT payload. header (Mapping[str, str]): Additional JWT header payload. key_id (str): The key id to add to the JWT header. If the signer has a key id it will be used as the default. If this is specified it will override the signer's key id. Returns: bytes: The encoded JWT. """ if header is None: header = {} if key_id is None: key_id = signer.key_id header.update({'typ': 'JWT', 'alg': 'RS256'}) if key_id is not None: header['kid'] = key_id segments = [ base64.urlsafe_b64encode(json.dumps(header).encode('utf-8')), base64.urlsafe_b64encode(json.dumps(payload).encode('utf-8')), ] signing_input = b'.'.join(segments) signature = signer.sign(signing_input) segments.append(base64.urlsafe_b64encode(signature)) return b'.'.join(segments) def _decode_jwt_segment(encoded_section): """Decodes a single JWT segment.""" section_bytes = _helpers.padded_urlsafe_b64decode(encoded_section) try: return json.loads(section_bytes.decode('utf-8')) except ValueError as caught_exc: new_exc = ValueError('Can\'t parse segment: {0}'.format(section_bytes)) six.raise_from(new_exc, caught_exc) def _unverified_decode(token): """Decodes a token and does no verification. Args: token (Union[str, bytes]): The encoded JWT. Returns: Tuple[str, str, str, str]: header, payload, signed_section, and signature. Raises: ValueError: if there are an incorrect amount of segments in the token. """ token = _helpers.to_bytes(token) if token.count(b'.') != 2: raise ValueError( 'Wrong number of segments in token: {0}'.format(token)) encoded_header, encoded_payload, signature = token.split(b'.') signed_section = encoded_header + b'.' + encoded_payload signature = _helpers.padded_urlsafe_b64decode(signature) # Parse segments header = _decode_jwt_segment(encoded_header) payload = _decode_jwt_segment(encoded_payload) return header, payload, signed_section, signature def decode_header(token): """Return the decoded header of a token. No verification is done. This is useful to extract the key id from the header in order to acquire the appropriate certificate to verify the token. Args: token (Union[str, bytes]): the encoded JWT. Returns: Mapping: The decoded JWT header. """ header, _, _, _ = _unverified_decode(token) return header def _verify_iat_and_exp(payload): """Verifies the ``iat`` (Issued At) and ``exp`` (Expires) claims in a token payload. Args: payload (Mapping[str, str]): The JWT payload. Raises: ValueError: if any checks failed. """ now = _helpers.datetime_to_secs(_helpers.utcnow()) # Make sure the iat and exp claims are present. for key in ('iat', 'exp'): if key not in payload: raise ValueError( 'Token does not contain required claim {}'.format(key)) # Make sure the token wasn't issued in the future. iat = payload['iat'] # Err on the side of accepting a token that is slightly early to account # for clock skew. earliest = iat - _helpers.CLOCK_SKEW_SECS if now < earliest: raise ValueError('Token used too early, {} < {}'.format(now, iat)) # Make sure the token wasn't issued in the past. exp = payload['exp'] # Err on the side of accepting a token that is slightly out of date # to account for clow skew. latest = exp + _helpers.CLOCK_SKEW_SECS if latest < now: raise ValueError('Token expired, {} < {}'.format(latest, now)) def decode(token, certs=None, verify=True, audience=None): """Decode and verify a JWT. Args: token (str): The encoded JWT. certs (Union[str, bytes, Mapping[str, Union[str, bytes]]]): The certificate used to validate the JWT signatyre. If bytes or string, it must the the public key certificate in PEM format. If a mapping, it must be a mapping of key IDs to public key certificates in PEM format. The mapping must contain the same key ID that's specified in the token's header. verify (bool): Whether to perform signature and claim validation. Verification is done by default. audience (str): The audience claim, 'aud', that this JWT should contain. If None then the JWT's 'aud' parameter is not verified. Returns: Mapping[str, str]: The deserialized JSON payload in the JWT. Raises: ValueError: if any verification checks failed. """ header, payload, signed_section, signature = _unverified_decode(token) if not verify: return payload # If certs is specified as a dictionary of key IDs to certificates, then # use the certificate identified by the key ID in the token header. if isinstance(certs, collections.Mapping): key_id = header.get('kid') if key_id: if key_id not in certs: raise ValueError( 'Certificate for key id {} not found.'.format(key_id)) certs_to_check = [certs[key_id]] # If there's no key id in the header, check against all of the certs. else: certs_to_check = certs.values() else: certs_to_check = certs # Verify that the signature matches the message. if not crypt.verify_signature(signed_section, signature, certs_to_check): raise ValueError('Could not verify token signature.') # Verify the issued at and created times in the payload. _verify_iat_and_exp(payload) # Check audience. if audience is not None: claim_audience = payload.get('aud') if audience != claim_audience: raise ValueError( 'Token has wrong audience {}, expected {}'.format( claim_audience, audience)) return payload class Credentials(google.auth.credentials.Signing, google.auth.credentials.Credentials): """Credentials that use a JWT as the bearer token. These credentials require an "audience" claim. This claim identifies the intended recipient of the bearer token. The constructor arguments determine the claims for the JWT that is sent with requests. Usually, you'll construct these credentials with one of the helper constructors as shown in the next section. To create JWT credentials using a Google service account private key JSON file:: audience = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher' credentials = jwt.Credentials.from_service_account_file( 'service-account.json', audience=audience) If you already have the service account file loaded and parsed:: service_account_info = json.load(open('service_account.json')) credentials = jwt.Credentials.from_service_account_info( service_account_info, audience=audience) Both helper methods pass on arguments to the constructor, so you can specify the JWT claims:: credentials = jwt.Credentials.from_service_account_file( 'service-account.json', audience=audience, additional_claims={'meta': 'data'}) You can also construct the credentials directly if you have a :class:`~google.auth.crypt.Signer` instance:: credentials = jwt.Credentials( signer, issuer='your-issuer', subject='your-subject', audience=audience) The claims are considered immutable. If you want to modify the claims, you can easily create another instance using :meth:`with_claims`:: new_audience = ( 'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber') new_credentials = credentials.with_claims(audience=new_audience) """ def __init__(self, signer, issuer, subject, audience, additional_claims=None, token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. issuer (str): The `iss` claim. subject (str): The `sub` claim. audience (str): the `aud` claim. The intended audience for the credentials. additional_claims (Mapping[str, str]): Any additional claims for the JWT payload. token_lifetime (int): The amount of time in seconds for which the token is valid. Defaults to 1 hour. """ super(Credentials, self).__init__() self._signer = signer self._issuer = issuer self._subject = subject self._audience = audience self._token_lifetime = token_lifetime if additional_claims is None: additional_claims = {} self._additional_claims = additional_claims @classmethod def _from_signer_and_info(cls, signer, info, **kwargs): """Creates a Credentials instance from a signer and service account info. Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. info (Mapping[str, str]): The service account info. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.Credentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ kwargs.setdefault('subject', info['client_email']) kwargs.setdefault('issuer', info['client_email']) return cls(signer, **kwargs) @classmethod def from_service_account_info(cls, info, **kwargs): """Creates an Credentials instance from a dictionary. Args: info (Mapping[str, str]): The service account info in Google format. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.Credentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ signer = _service_account_info.from_dict( info, require=['client_email']) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod def from_service_account_file(cls, filename, **kwargs): """Creates a Credentials instance from a service account .json file in Google format. Args: filename (str): The path to the service account .json file. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.Credentials: The constructed credentials. """ info, signer = _service_account_info.from_filename( filename, require=['client_email']) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod def from_signing_credentials(cls, credentials, audience, **kwargs): """Creates a new :class:`google.auth.jwt.Credentials` instance from an existing :class:`google.auth.credentials.Signing` instance. The new instance will use the same signer as the existing instance and will use the existing instance's signer email as the issuer and subject by default. Example:: svc_creds = service_account.Credentials.from_service_account_file( 'service_account.json') audience = ( 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher') jwt_creds = jwt.Credentials.from_signing_credentials( svc_creds, audience=audience) Args: credentials (google.auth.credentials.Signing): The credentials to use to construct the new credentials. audience (str): the `aud` claim. The intended audience for the credentials. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.Credentials: A new Credentials instance. """ kwargs.setdefault('issuer', credentials.signer_email) kwargs.setdefault('subject', credentials.signer_email) return cls( credentials.signer, audience=audience, **kwargs) def with_claims(self, issuer=None, subject=None, audience=None, additional_claims=None): """Returns a copy of these credentials with modified claims. Args: issuer (str): The `iss` claim. If unspecified the current issuer claim will be used. subject (str): The `sub` claim. If unspecified the current subject claim will be used. audience (str): the `aud` claim. If unspecified the current audience claim will be used. additional_claims (Mapping[str, str]): Any additional claims for the JWT payload. This will be merged with the current additional claims. Returns: google.auth.jwt.Credentials: A new credentials instance. """ new_additional_claims = copy.deepcopy(self._additional_claims) new_additional_claims.update(additional_claims or {}) return self.__class__( self._signer, issuer=issuer if issuer is not None else self._issuer, subject=subject if subject is not None else self._subject, audience=audience if audience is not None else self._audience, additional_claims=new_additional_claims) def _make_jwt(self): """Make a signed JWT. Returns: Tuple[bytes, datetime]: The encoded JWT and the expiration. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=self._token_lifetime) expiry = now + lifetime payload = { 'iss': self._issuer, 'sub': self._subject, 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), 'aud': self._audience, } payload.update(self._additional_claims) jwt = encode(self._signer, payload) return jwt, expiry def refresh(self, request): """Refreshes the access token. Args: request (Any): Unused. """ # pylint: disable=unused-argument # (pylint doesn't correctly recognize overridden methods.) self.token, self.expiry = self._make_jwt() @_helpers.copy_docstring(google.auth.credentials.Signing) def sign_bytes(self, message): return self._signer.sign(message) @property @_helpers.copy_docstring(google.auth.credentials.Signing) def signer_email(self): return self._issuer @property @_helpers.copy_docstring(google.auth.credentials.Signing) def signer(self): return self._signer class OnDemandCredentials( google.auth.credentials.Signing, google.auth.credentials.Credentials): """On-demand JWT credentials. Like :class:`Credentials`, this class uses a JWT as the bearer token for authentication. However, this class does not require the audience at construction time. Instead, it will generate a new token on-demand for each request using the request URI as the audience. It caches tokens so that multiple requests to the same URI do not incur the overhead of generating a new token every time. This behavior is especially useful for `gRPC`_ clients. A gRPC service may have multiple audience and gRPC clients may not know all of the audiences required for accessing a particular service. With these credentials, no knowledge of the audiences is required ahead of time. .. _grpc: http://www.grpc.io/ """ def __init__(self, signer, issuer, subject, additional_claims=None, token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS, max_cache_size=_DEFAULT_MAX_CACHE_SIZE): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. issuer (str): The `iss` claim. subject (str): The `sub` claim. additional_claims (Mapping[str, str]): Any additional claims for the JWT payload. token_lifetime (int): The amount of time in seconds for which the token is valid. Defaults to 1 hour. max_cache_size (int): The maximum number of JWT tokens to keep in cache. Tokens are cached using :class:`cachetools.LRUCache`. """ super(OnDemandCredentials, self).__init__() self._signer = signer self._issuer = issuer self._subject = subject self._token_lifetime = token_lifetime if additional_claims is None: additional_claims = {} self._additional_claims = additional_claims self._cache = cachetools.LRUCache(maxsize=max_cache_size) @classmethod def _from_signer_and_info(cls, signer, info, **kwargs): """Creates an OnDemandCredentials instance from a signer and service account info. Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. info (Mapping[str, str]): The service account info. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.OnDemandCredentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ kwargs.setdefault('subject', info['client_email']) kwargs.setdefault('issuer', info['client_email']) return cls(signer, **kwargs) @classmethod def from_service_account_info(cls, info, **kwargs): """Creates an OnDemandCredentials instance from a dictionary. Args: info (Mapping[str, str]): The service account info in Google format. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.OnDemandCredentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ signer = _service_account_info.from_dict( info, require=['client_email']) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod def from_service_account_file(cls, filename, **kwargs): """Creates an OnDemandCredentials instance from a service account .json file in Google format. Args: filename (str): The path to the service account .json file. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.OnDemandCredentials: The constructed credentials. """ info, signer = _service_account_info.from_filename( filename, require=['client_email']) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod def from_signing_credentials(cls, credentials, **kwargs): """Creates a new :class:`google.auth.jwt.OnDemandCredentials` instance from an existing :class:`google.auth.credentials.Signing` instance. The new instance will use the same signer as the existing instance and will use the existing instance's signer email as the issuer and subject by default. Example:: svc_creds = service_account.Credentials.from_service_account_file( 'service_account.json') jwt_creds = jwt.OnDemandCredentials.from_signing_credentials( svc_creds) Args: credentials (google.auth.credentials.Signing): The credentials to use to construct the new credentials. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.Credentials: A new Credentials instance. """ kwargs.setdefault('issuer', credentials.signer_email) kwargs.setdefault('subject', credentials.signer_email) return cls(credentials.signer, **kwargs) def with_claims(self, issuer=None, subject=None, additional_claims=None): """Returns a copy of these credentials with modified claims. Args: issuer (str): The `iss` claim. If unspecified the current issuer claim will be used. subject (str): The `sub` claim. If unspecified the current subject claim will be used. additional_claims (Mapping[str, str]): Any additional claims for the JWT payload. This will be merged with the current additional claims. Returns: google.auth.jwt.OnDemandCredentials: A new credentials instance. """ new_additional_claims = copy.deepcopy(self._additional_claims) new_additional_claims.update(additional_claims or {}) return self.__class__( self._signer, issuer=issuer if issuer is not None else self._issuer, subject=subject if subject is not None else self._subject, additional_claims=new_additional_claims, max_cache_size=self._cache.maxsize) @property def valid(self): """Checks the validity of the credentials. These credentials are always valid because it generates tokens on demand. """ return True def _make_jwt_for_audience(self, audience): """Make a new JWT for the given audience. Args: audience (str): The intended audience. Returns: Tuple[bytes, datetime]: The encoded JWT and the expiration. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=self._token_lifetime) expiry = now + lifetime payload = { 'iss': self._issuer, 'sub': self._subject, 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), 'aud': audience, } payload.update(self._additional_claims) jwt = encode(self._signer, payload) return jwt, expiry def _get_jwt_for_audience(self, audience): """Get a JWT For a given audience. If there is already an existing, non-expired token in the cache for the audience, that token is used. Otherwise, a new token will be created. Args: audience (str): The intended audience. Returns: bytes: The encoded JWT. """ token, expiry = self._cache.get(audience, (None, None)) if token is None or expiry < _helpers.utcnow(): token, expiry = self._make_jwt_for_audience(audience) self._cache[audience] = token, expiry return token def refresh(self, request): """Raises an exception, these credentials can not be directly refreshed. Args: request (Any): Unused. Raises: google.auth.RefreshError """ # pylint: disable=unused-argument # (pylint doesn't correctly recognize overridden methods.) raise exceptions.RefreshError( 'OnDemandCredentials can not be directly refreshed.') def before_request(self, request, method, url, headers): """Performs credential-specific before request logic. Args: request (Any): Unused. JWT credentials do not need to make an HTTP request to refresh. method (str): The request's HTTP method. url (str): The request's URI. This is used as the audience claim when generating the JWT. headers (Mapping): The request's headers. """ # pylint: disable=unused-argument # (pylint doesn't correctly recognize overridden methods.) parts = urllib.parse.urlsplit(url) # Strip query string and fragment audience = urllib.parse.urlunsplit( (parts.scheme, parts.netloc, parts.path, None, None)) token = self._get_jwt_for_audience(audience) self.apply(headers, token=token) @_helpers.copy_docstring(google.auth.credentials.Signing) def sign_bytes(self, message): return self._signer.sign(message) @property @_helpers.copy_docstring(google.auth.credentials.Signing) def signer_email(self): return self._issuer @property @_helpers.copy_docstring(google.auth.credentials.Signing) def signer(self): return self._signer google-auth-1.5.1/google/auth/compute_engine/0000755110576702575230000000000013330167570024312 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google/auth/compute_engine/_metadata.py0000644110576702575230000001604713277071027026614 0ustar theaflowersprimarygroup00000000000000# 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. """Provides helper methods for talking to the Compute Engine metadata server. See https://cloud.google.com/compute/docs/metadata for more details. """ import datetime import json import logging import os import six from six.moves import http_client from six.moves.urllib import parse as urlparse from google.auth import _helpers from google.auth import environment_vars from google.auth import exceptions _LOGGER = logging.getLogger(__name__) _METADATA_ROOT = 'http://{}/computeMetadata/v1/'.format( os.getenv(environment_vars.GCE_METADATA_ROOT, 'metadata.google.internal')) # This is used to ping the metadata server, it avoids the cost of a DNS # lookup. _METADATA_IP_ROOT = 'http://{}'.format( os.getenv(environment_vars.GCE_METADATA_IP, '169.254.169.254')) _METADATA_FLAVOR_HEADER = 'metadata-flavor' _METADATA_FLAVOR_VALUE = 'Google' _METADATA_HEADERS = {_METADATA_FLAVOR_HEADER: _METADATA_FLAVOR_VALUE} # Timeout in seconds to wait for the GCE metadata server when detecting the # GCE environment. try: _METADATA_DEFAULT_TIMEOUT = int(os.getenv('GCE_METADATA_TIMEOUT', 3)) except ValueError: # pragma: NO COVER _METADATA_DEFAULT_TIMEOUT = 3 def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT): """Checks to see if the metadata server is available. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. timeout (int): How long to wait for the metadata server to respond. Returns: bool: True if the metadata server is reachable, False otherwise. """ # NOTE: The explicit ``timeout`` is a workaround. The underlying # issue is that resolving an unknown host on some networks will take # 20-30 seconds; making this timeout short fixes the issue, but # could lead to false negatives in the event that we are on GCE, but # the metadata resolution was particularly slow. The latter case is # "unlikely". try: response = request( url=_METADATA_IP_ROOT, method='GET', headers=_METADATA_HEADERS, timeout=timeout) metadata_flavor = response.headers.get(_METADATA_FLAVOR_HEADER) return (response.status == http_client.OK and metadata_flavor == _METADATA_FLAVOR_VALUE) except exceptions.TransportError: _LOGGER.info('Compute Engine Metadata server unavailable.') return False def get(request, path, root=_METADATA_ROOT, recursive=False): """Fetch a resource from the metadata server. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. path (str): The resource to retrieve. For example, ``'instance/service-accounts/default'``. root (str): The full path to the metadata server root. recursive (bool): Whether to do a recursive query of metadata. See https://cloud.google.com/compute/docs/metadata#aggcontents for more details. Returns: Union[Mapping, str]: If the metadata server returns JSON, a mapping of the decoded JSON is return. Otherwise, the response content is returned as a string. Raises: google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ base_url = urlparse.urljoin(root, path) query_params = {} if recursive: query_params['recursive'] = 'true' url = _helpers.update_query(base_url, query_params) response = request(url=url, method='GET', headers=_METADATA_HEADERS) if response.status == http_client.OK: content = _helpers.from_bytes(response.data) if response.headers['content-type'] == 'application/json': try: return json.loads(content) except ValueError as caught_exc: new_exc = exceptions.TransportError( 'Received invalid JSON from the Google Compute Engine' 'metadata service: {:.20}'.format(content)) six.raise_from(new_exc, caught_exc) else: return content else: raise exceptions.TransportError( 'Failed to retrieve {} from the Google Compute Engine' 'metadata service. Status: {} Response:\n{}'.format( url, response.status, response.data), response) def get_project_id(request): """Get the Google Cloud Project ID from the metadata server. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. Returns: str: The project ID Raises: google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ return get(request, 'project/project-id') def get_service_account_info(request, service_account='default'): """Get information about a service account from the metadata server. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. service_account (str): The string 'default' or a service account email address. The determines which service account for which to acquire information. Returns: Mapping: The service account's information, for example:: { 'email': '...', 'scopes': ['scope', ...], 'aliases': ['default', '...'] } Raises: google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ return get( request, 'instance/service-accounts/{0}/'.format(service_account), recursive=True) def get_service_account_token(request, service_account='default'): """Get the OAuth 2.0 access token for a service account. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. service_account (str): The string 'default' or a service account email address. The determines which service account for which to acquire an access token. Returns: Union[str, datetime]: The access token and its expiration. Raises: google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ token_json = get( request, 'instance/service-accounts/{0}/token'.format(service_account)) token_expiry = _helpers.utcnow() + datetime.timedelta( seconds=token_json['expires_in']) return token_json['access_token'], token_expiry google-auth-1.5.1/google/auth/compute_engine/credentials.py0000644110576702575230000002100513304036026027147 0ustar theaflowersprimarygroup00000000000000# 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. """Google Compute Engine credentials. This module provides authentication for application running on Google Compute Engine using the Compute Engine metadata server. """ import datetime import six from google.auth import _helpers from google.auth import credentials from google.auth import exceptions from google.auth import iam from google.auth import jwt from google.auth.compute_engine import _metadata from google.oauth2 import _client class Credentials(credentials.ReadOnlyScoped, credentials.Credentials): """Compute Engine Credentials. These credentials use the Google Compute Engine metadata server to obtain OAuth 2.0 access tokens associated with the instance's service account. For more information about Compute Engine authentication, including how to configure scopes, see the `Compute Engine authentication documentation`_. .. note:: Compute Engine instances can be created with scopes and therefore these credentials are considered to be 'scoped'. However, you can not use :meth:`~google.auth.credentials.ScopedCredentials.with_scopes` because it is not possible to change the scopes that the instance has. Also note that :meth:`~google.auth.credentials.ScopedCredentials.has_scopes` will not work until the credentials have been refreshed. .. _Compute Engine authentication documentation: https://cloud.google.com/compute/docs/authentication#using """ def __init__(self, service_account_email='default'): """ Args: service_account_email (str): The service account email to use, or 'default'. A Compute Engine instance may have multiple service accounts. """ super(Credentials, self).__init__() self._service_account_email = service_account_email def _retrieve_info(self, request): """Retrieve information about the service account. Updates the scopes and retrieves the full service account email. Args: request (google.auth.transport.Request): The object used to make HTTP requests. """ info = _metadata.get_service_account_info( request, service_account=self._service_account_email) self._service_account_email = info['email'] self._scopes = info['scopes'] def refresh(self, request): """Refresh the access token and scopes. Args: request (google.auth.transport.Request): The object used to make HTTP requests. Raises: google.auth.exceptions.RefreshError: If the Compute Engine metadata service can't be reached if if the instance has not credentials. """ try: self._retrieve_info(request) self.token, self.expiry = _metadata.get_service_account_token( request, service_account=self._service_account_email) except exceptions.TransportError as caught_exc: new_exc = exceptions.RefreshError(caught_exc) six.raise_from(new_exc, caught_exc) @property def service_account_email(self): """The service account email. .. note: This is not guaranteed to be set until :meth`refresh` has been called. """ return self._service_account_email @property def requires_scopes(self): """False: Compute Engine credentials can not be scoped.""" return False _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds _DEFAULT_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token' class IDTokenCredentials(credentials.Credentials, credentials.Signing): """Open ID Connect ID Token-based service account credentials. These credentials relies on the default service account of a GCE instance. In order for this to work, the GCE instance must have been started with a service account that has access to the IAM Cloud API. """ def __init__(self, request, target_audience, token_uri=_DEFAULT_TOKEN_URI, additional_claims=None, service_account_email=None): """ Args: request (google.auth.transport.Request): The object used to make HTTP requests. target_audience (str): The intended audience for these credentials, used when requesting the ID Token. The ID Token's ``aud`` claim will be set to this string. token_uri (str): The OAuth 2.0 Token URI. additional_claims (Mapping[str, str]): Any additional claims for the JWT assertion used in the authorization grant. service_account_email (str): Optional explicit service account to use to sign JWT tokens. By default, this is the default GCE service account. """ super(IDTokenCredentials, self).__init__() if service_account_email is None: sa_info = _metadata.get_service_account_info(request) service_account_email = sa_info['email'] self._service_account_email = service_account_email self._signer = iam.Signer( request=request, credentials=Credentials(), service_account_email=service_account_email) self._token_uri = token_uri self._target_audience = target_audience if additional_claims is not None: self._additional_claims = additional_claims else: self._additional_claims = {} def with_target_audience(self, target_audience): """Create a copy of these credentials with the specified target audience. Args: target_audience (str): The intended audience for these credentials, used when requesting the ID Token. Returns: google.auth.service_account.IDTokenCredentials: A new credentials instance. """ return self.__class__( self._signer, service_account_email=self._service_account_email, token_uri=self._token_uri, target_audience=target_audience, additional_claims=self._additional_claims.copy()) def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. This assertion is used during the OAuth 2.0 grant to acquire an ID token. Returns: bytes: The authorization grant assertion. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS) expiry = now + lifetime payload = { 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. 'iss': self.service_account_email, # The audience must be the auth token endpoint's URI 'aud': self._token_uri, # The target audience specifies which service the ID token is # intended for. 'target_audience': self._target_audience } payload.update(self._additional_claims) token = jwt.encode(self._signer, payload) return token @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): assertion = self._make_authorization_grant_assertion() access_token, expiry, _ = _client.id_token_jwt_grant( request, self._token_uri, assertion) self.token = access_token self.expiry = expiry @property @_helpers.copy_docstring(credentials.Signing) def signer(self): return self._signer @_helpers.copy_docstring(credentials.Signing) def sign_bytes(self, message): return self._signer.sign(message) @property def service_account_email(self): """The service account email.""" return self._service_account_email @property def signer_email(self): return self._service_account_email google-auth-1.5.1/google/auth/compute_engine/__init__.py0000644110576702575230000000146013304036026026414 0ustar theaflowersprimarygroup00000000000000# 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. """Google Compute Engine authentication.""" from google.auth.compute_engine.credentials import Credentials from google.auth.compute_engine.credentials import IDTokenCredentials __all__ = [ 'Credentials', 'IDTokenCredentials', ] google-auth-1.5.1/google/oauth2/0000755110576702575230000000000013330167570021552 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google/oauth2/id_token.py0000644110576702575230000001272613277071027023731 0ustar theaflowersprimarygroup00000000000000# 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. """Google ID Token helpers. Provides support for verifying `OpenID Connect ID Tokens`_, especially ones generated by Google infrastructure. To parse and verify an ID Token issued by Google's OAuth 2.0 authorization server use :func:`verify_oauth2_token`. To verify an ID Token issued by Firebase, use :func:`verify_firebase_token`. A general purpose ID Token verifier is available as :func:`verify_token`. Example:: from google.oauth2 import id_token from google.auth.transport import requests request = requests.Request() id_info = id_token.verify_oauth2_token( token, request, 'my-client-id.example.com') if id_info['iss'] != 'https://accounts.google.com': raise ValueError('Wrong issuer.') userid = id_info['sub'] By default, this will re-fetch certificates for each verification. Because Google's public keys are only changed infrequently (on the order of once per day), you may wish to take advantage of caching to reduce latency and the potential for network errors. This can be accomplished using an external library like `CacheControl`_ to create a cache-aware :class:`google.auth.transport.Request`:: import cachecontrol import google.auth.transport.requests import requests session = requests.session() cached_session = cachecontrol.CacheControl(session) request = google.auth.transport.requests.Request(session=cached_session) .. _OpenID Connect ID Token: http://openid.net/specs/openid-connect-core-1_0.html#IDToken .. _CacheControl: https://cachecontrol.readthedocs.io """ import json from six.moves import http_client from google.auth import exceptions from google.auth import jwt # The URL that provides public certificates for verifying ID tokens issued # by Google's OAuth 2.0 authorization server. _GOOGLE_OAUTH2_CERTS_URL = 'https://www.googleapis.com/oauth2/v1/certs' # The URL that provides public certificates for verifying ID tokens issued # by Firebase and the Google APIs infrastructure _GOOGLE_APIS_CERTS_URL = ( 'https://www.googleapis.com/robot/v1/metadata/x509' '/securetoken@system.gserviceaccount.com') def _fetch_certs(request, certs_url): """Fetches certificates. Google-style cerificate endpoints return JSON in the format of ``{'key id': 'x509 certificate'}``. Args: request (google.auth.transport.Request): The object used to make HTTP requests. certs_url (str): The certificate endpoint URL. Returns: Mapping[str, str]: A mapping of public key ID to x.509 certificate data. """ response = request(certs_url, method='GET') if response.status != http_client.OK: raise exceptions.TransportError( 'Could not fetch certificates at {}'.format(certs_url)) return json.loads(response.data.decode('utf-8')) def verify_token(id_token, request, audience=None, certs_url=_GOOGLE_OAUTH2_CERTS_URL): """Verifies an ID token and returns the decoded token. Args: id_token (Union[str, bytes]): The encoded token. request (google.auth.transport.Request): The object used to make HTTP requests. audience (str): The audience that this token is intended for. If None then the audience is not verified. certs_url (str): The URL that specifies the certificates to use to verify the token. This URL should return JSON in the format of ``{'key id': 'x509 certificate'}``. Returns: Mapping[str, Any]: The decoded token. """ certs = _fetch_certs(request, certs_url) return jwt.decode(id_token, certs=certs, audience=audience) def verify_oauth2_token(id_token, request, audience=None): """Verifies an ID Token issued by Google's OAuth 2.0 authorization server. Args: id_token (Union[str, bytes]): The encoded token. request (google.auth.transport.Request): The object used to make HTTP requests. audience (str): The audience that this token is intended for. This is typically your application's OAuth 2.0 client ID. If None then the audience is not verified. Returns: Mapping[str, Any]: The decoded token. """ return verify_token( id_token, request, audience=audience, certs_url=_GOOGLE_OAUTH2_CERTS_URL) def verify_firebase_token(id_token, request, audience=None): """Verifies an ID Token issued by Firebase Authentication. Args: id_token (Union[str, bytes]): The encoded token. request (google.auth.transport.Request): The object used to make HTTP requests. audience (str): The audience that this token is intended for. This is typically your Firebase application ID. If None then the audience is not verified. Returns: Mapping[str, Any]: The decoded token. """ return verify_token( id_token, request, audience=audience, certs_url=_GOOGLE_APIS_CERTS_URL) google-auth-1.5.1/google/oauth2/credentials.py0000644110576702575230000001650013330162371024415 0ustar theaflowersprimarygroup00000000000000# 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. """OAuth 2.0 Credentials. This module provides credentials based on OAuth 2.0 access and refresh tokens. These credentials usually access resources on behalf of a user (resource owner). Specifically, this is intended to use access tokens acquired using the `Authorization Code grant`_ and can refresh those tokens using a optional `refresh token`_. Obtaining the initial access and refresh token is outside of the scope of this module. Consult `rfc6749 section 4.1`_ for complete details on the Authorization Code grant flow. .. _Authorization Code grant: https://tools.ietf.org/html/rfc6749#section-1.3.1 .. _refresh token: https://tools.ietf.org/html/rfc6749#section-6 .. _rfc6749 section 4.1: https://tools.ietf.org/html/rfc6749#section-4.1 """ import io import json import six from google.auth import _helpers from google.auth import credentials from google.auth import exceptions from google.oauth2 import _client # The Google OAuth 2.0 token endpoint. Used for authorized user credentials. _GOOGLE_OAUTH2_TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token' class Credentials(credentials.ReadOnlyScoped, credentials.Credentials): """Credentials using OAuth 2.0 access and refresh tokens.""" def __init__(self, token, refresh_token=None, id_token=None, token_uri=None, client_id=None, client_secret=None, scopes=None): """ Args: token (Optional(str)): The OAuth 2.0 access token. Can be None if refresh information is provided. refresh_token (str): The OAuth 2.0 refresh token. If specified, credentials can be refreshed. id_token (str): The Open ID Connect ID Token. token_uri (str): The OAuth 2.0 authorization server's token endpoint URI. Must be specified for refresh, can be left as None if the token can not be refreshed. client_id (str): The OAuth 2.0 client ID. Must be specified for refresh, can be left as None if the token can not be refreshed. client_secret(str): The OAuth 2.0 client secret. Must be specified for refresh, can be left as None if the token can not be refreshed. scopes (Sequence[str]): The scopes that were originally used to obtain authorization. This is a purely informative parameter that can be used by :meth:`has_scopes`. OAuth 2.0 credentials can not request additional scopes after authorization. """ super(Credentials, self).__init__() self.token = token self._refresh_token = refresh_token self._id_token = id_token self._scopes = scopes self._token_uri = token_uri self._client_id = client_id self._client_secret = client_secret @property def refresh_token(self): """Optional[str]: The OAuth 2.0 refresh token.""" return self._refresh_token @property def token_uri(self): """Optional[str]: The OAuth 2.0 authorization server's token endpoint URI.""" return self._token_uri @property def id_token(self): """Optional[str]: The Open ID Connect ID Token. Depending on the authorization server and the scopes requested, this may be populated when credentials are obtained and updated when :meth:`refresh` is called. This token is a JWT. It can be verified and decoded using :func:`google.oauth2.id_token.verify_oauth2_token`. """ return self._id_token @property def client_id(self): """Optional[str]: The OAuth 2.0 client ID.""" return self._client_id @property def client_secret(self): """Optional[str]: The OAuth 2.0 client secret.""" return self._client_secret @property def requires_scopes(self): """False: OAuth 2.0 credentials have their scopes set when the initial token is requested and can not be changed.""" return False @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): if (self._refresh_token is None or self._token_uri is None or self._client_id is None or self._client_secret is None): raise exceptions.RefreshError( 'The credentials do not contain the necessary fields need to ' 'refresh the access token. You must specify refresh_token, ' 'token_uri, client_id, and client_secret.') access_token, refresh_token, expiry, grant_response = ( _client.refresh_grant( request, self._token_uri, self._refresh_token, self._client_id, self._client_secret)) self.token = access_token self.expiry = expiry self._refresh_token = refresh_token self._id_token = grant_response.get('id_token') @classmethod def from_authorized_user_info(cls, info, scopes=None): """Creates a Credentials instance from parsed authorized user info. Args: info (Mapping[str, str]): The authorized user info in Google format. scopes (Sequence[str]): Optional list of scopes to include in the credentials. Returns: google.oauth2.credentials.Credentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ keys_needed = set(('refresh_token', 'client_id', 'client_secret')) missing = keys_needed.difference(six.iterkeys(info)) if missing: raise ValueError( 'Authorized user info was not in the expected format, missing ' 'fields {}.'.format(', '.join(missing))) return Credentials( None, # No access token, must be refreshed. refresh_token=info['refresh_token'], token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT, scopes=scopes, client_id=info['client_id'], client_secret=info['client_secret']) @classmethod def from_authorized_user_file(cls, filename, scopes=None): """Creates a Credentials instance from an authorized user json file. Args: filename (str): The path to the authorized user json file. scopes (Sequence[str]): Optional list of scopes to include in the credentials. Returns: google.oauth2.credentials.Credentials: The constructed credentials. Raises: ValueError: If the file is not in the expected format. """ with io.open(filename, 'r', encoding='utf-8') as json_file: data = json.load(json_file) return cls.from_authorized_user_info(data, scopes) google-auth-1.5.1/google/oauth2/service_account.py0000644110576702575230000005004513277071027025305 0ustar theaflowersprimarygroup00000000000000# 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. """Service Accounts: JSON Web Token (JWT) Profile for OAuth 2.0 This module implements the JWT Profile for OAuth 2.0 Authorization Grants as defined by `RFC 7523`_ with particular support for how this RFC is implemented in Google's infrastructure. Google refers to these credentials as *Service Accounts*. Service accounts are used for server-to-server communication, such as interactions between a web application server and a Google service. The service account belongs to your application instead of to an individual end user. In contrast to other OAuth 2.0 profiles, no users are involved and your application "acts" as the service account. Typically an application uses a service account when the application uses Google APIs to work with its own data rather than a user's data. For example, an application that uses Google Cloud Datastore for data persistence would use a service account to authenticate its calls to the Google Cloud Datastore API. However, an application that needs to access a user's Drive documents would use the normal OAuth 2.0 profile. Additionally, Google Apps domain administrators can grant service accounts `domain-wide delegation`_ authority to access user data on behalf of users in the domain. This profile uses a JWT to acquire an OAuth 2.0 access token. The JWT is used in place of the usual authorization token returned during the standard OAuth 2.0 Authorization Code grant. The JWT is only used for this purpose, as the acquired access token is used as the bearer token when making requests using these credentials. This profile differs from normal OAuth 2.0 profile because no user consent step is required. The use of the private key allows this profile to assert identity directly. This profile also differs from the :mod:`google.auth.jwt` authentication because the JWT credentials use the JWT directly as the bearer token. This profile instead only uses the JWT to obtain an OAuth 2.0 access token. The obtained OAuth 2.0 access token is used as the bearer token. Domain-wide delegation ---------------------- Domain-wide delegation allows a service account to access user data on behalf of any user in a Google Apps domain without consent from the user. For example, an application that uses the Google Calendar API to add events to the calendars of all users in a Google Apps domain would use a service account to access the Google Calendar API on behalf of users. The Google Apps administrator must explicitly authorize the service account to do this. This authorization step is referred to as "delegating domain-wide authority" to a service account. You can use domain-wise delegation by creating a set of credentials with a specific subject using :meth:`~Credentials.with_subject`. .. _RFC 7523: https://tools.ietf.org/html/rfc7523 """ import copy import datetime from google.auth import _helpers from google.auth import _service_account_info from google.auth import credentials from google.auth import jwt from google.oauth2 import _client _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds class Credentials(credentials.Signing, credentials.Scoped, credentials.Credentials): """Service account credentials Usually, you'll create these credentials with one of the helper constructors. To create credentials using a Google service account private key JSON file:: credentials = service_account.Credentials.from_service_account_file( 'service-account.json') Or if you already have the service account file loaded:: service_account_info = json.load(open('service_account.json')) credentials = service_account.Credentials.from_service_account_info( service_account_info) Both helper methods pass on arguments to the constructor, so you can specify additional scopes and a subject if necessary:: credentials = service_account.Credentials.from_service_account_file( 'service-account.json', scopes=['email'], subject='user@example.com') The credentials are considered immutable. If you want to modify the scopes or the subject used for delegation, use :meth:`with_scopes` or :meth:`with_subject`:: scoped_credentials = credentials.with_scopes(['email']) delegated_credentials = credentials.with_subject(subject) """ def __init__(self, signer, service_account_email, token_uri, scopes=None, subject=None, project_id=None, additional_claims=None): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. service_account_email (str): The service account's email. scopes (Sequence[str]): Scopes to request during the authorization grant. token_uri (str): The OAuth 2.0 Token URI. subject (str): For domain-wide delegation, the email address of the user to for which to request delegated access. project_id (str): Project ID associated with the service account credential. additional_claims (Mapping[str, str]): Any additional claims for the JWT assertion used in the authorization grant. .. note:: Typically one of the helper constructors :meth:`from_service_account_file` or :meth:`from_service_account_info` are used instead of calling the constructor directly. """ super(Credentials, self).__init__() self._scopes = scopes self._signer = signer self._service_account_email = service_account_email self._subject = subject self._project_id = project_id self._token_uri = token_uri if additional_claims is not None: self._additional_claims = additional_claims else: self._additional_claims = {} @classmethod def _from_signer_and_info(cls, signer, info, **kwargs): """Creates a Credentials instance from a signer and service account info. Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. info (Mapping[str, str]): The service account info. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.Credentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ return cls( signer, service_account_email=info['client_email'], token_uri=info['token_uri'], project_id=info.get('project_id'), **kwargs) @classmethod def from_service_account_info(cls, info, **kwargs): """Creates a Credentials instance from parsed service account info. Args: info (Mapping[str, str]): The service account info in Google format. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.service_account.Credentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ signer = _service_account_info.from_dict( info, require=['client_email', 'token_uri']) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod def from_service_account_file(cls, filename, **kwargs): """Creates a Credentials instance from a service account json file. Args: filename (str): The path to the service account json file. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.service_account.Credentials: The constructed credentials. """ info, signer = _service_account_info.from_filename( filename, require=['client_email', 'token_uri']) return cls._from_signer_and_info(signer, info, **kwargs) @property def service_account_email(self): """The service account email.""" return self._service_account_email @property def project_id(self): """Project ID associated with this credential.""" return self._project_id @property def requires_scopes(self): """Checks if the credentials requires scopes. Returns: bool: True if there are no scopes set otherwise False. """ return True if not self._scopes else False @_helpers.copy_docstring(credentials.Scoped) def with_scopes(self, scopes): return self.__class__( self._signer, service_account_email=self._service_account_email, scopes=scopes, token_uri=self._token_uri, subject=self._subject, project_id=self._project_id, additional_claims=self._additional_claims.copy()) def with_subject(self, subject): """Create a copy of these credentials with the specified subject. Args: subject (str): The subject claim. Returns: google.auth.service_account.Credentials: A new credentials instance. """ return self.__class__( self._signer, service_account_email=self._service_account_email, scopes=self._scopes, token_uri=self._token_uri, subject=subject, project_id=self._project_id, additional_claims=self._additional_claims.copy()) def with_claims(self, additional_claims): """Returns a copy of these credentials with modified claims. Args: additional_claims (Mapping[str, str]): Any additional claims for the JWT payload. This will be merged with the current additional claims. Returns: google.auth.service_account.Credentials: A new credentials instance. """ new_additional_claims = copy.deepcopy(self._additional_claims) new_additional_claims.update(additional_claims or {}) return self.__class__( self._signer, service_account_email=self._service_account_email, scopes=self._scopes, token_uri=self._token_uri, subject=self._subject, project_id=self._project_id, additional_claims=new_additional_claims) def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. This assertion is used during the OAuth 2.0 grant to acquire an access token. Returns: bytes: The authorization grant assertion. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS) expiry = now + lifetime payload = { 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. 'iss': self._service_account_email, # The audience must be the auth token endpoint's URI 'aud': self._token_uri, 'scope': _helpers.scopes_to_string(self._scopes or ()) } payload.update(self._additional_claims) # The subject can be a user email for domain-wide delegation. if self._subject: payload.setdefault('sub', self._subject) token = jwt.encode(self._signer, payload) return token @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): assertion = self._make_authorization_grant_assertion() access_token, expiry, _ = _client.jwt_grant( request, self._token_uri, assertion) self.token = access_token self.expiry = expiry @_helpers.copy_docstring(credentials.Signing) def sign_bytes(self, message): return self._signer.sign(message) @property @_helpers.copy_docstring(credentials.Signing) def signer(self): return self._signer @property @_helpers.copy_docstring(credentials.Signing) def signer_email(self): return self._service_account_email class IDTokenCredentials(credentials.Signing, credentials.Credentials): """Open ID Connect ID Token-based service account credentials. These credentials are largely similar to :class:`.Credentials`, but instead of using an OAuth 2.0 Access Token as the bearer token, they use an Open ID Connect ID Token as the bearer token. These credentials are useful when communicating to services that require ID Tokens and can not accept access tokens. Usually, you'll create these credentials with one of the helper constructors. To create credentials using a Google service account private key JSON file:: credentials = ( service_account.IDTokenCredentials.from_service_account_file( 'service-account.json')) Or if you already have the service account file loaded:: service_account_info = json.load(open('service_account.json')) credentials = ( service_account.IDTokenCredentials.from_service_account_info( service_account_info)) Both helper methods pass on arguments to the constructor, so you can specify additional scopes and a subject if necessary:: credentials = ( service_account.IDTokenCredentials.from_service_account_file( 'service-account.json', scopes=['email'], subject='user@example.com')) ` The credentials are considered immutable. If you want to modify the scopes or the subject used for delegation, use :meth:`with_scopes` or :meth:`with_subject`:: scoped_credentials = credentials.with_scopes(['email']) delegated_credentials = credentials.with_subject(subject) """ def __init__(self, signer, service_account_email, token_uri, target_audience, additional_claims=None): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. service_account_email (str): The service account's email. token_uri (str): The OAuth 2.0 Token URI. target_audience (str): The intended audience for these credentials, used when requesting the ID Token. The ID Token's ``aud`` claim will be set to this string. additional_claims (Mapping[str, str]): Any additional claims for the JWT assertion used in the authorization grant. .. note:: Typically one of the helper constructors :meth:`from_service_account_file` or :meth:`from_service_account_info` are used instead of calling the constructor directly. """ super(IDTokenCredentials, self).__init__() self._signer = signer self._service_account_email = service_account_email self._token_uri = token_uri self._target_audience = target_audience if additional_claims is not None: self._additional_claims = additional_claims else: self._additional_claims = {} @classmethod def _from_signer_and_info(cls, signer, info, **kwargs): """Creates a credentials instance from a signer and service account info. Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. info (Mapping[str, str]): The service account info. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.jwt.IDTokenCredentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ kwargs.setdefault('service_account_email', info['client_email']) kwargs.setdefault('token_uri', info['token_uri']) return cls(signer, **kwargs) @classmethod def from_service_account_info(cls, info, **kwargs): """Creates a credentials instance from parsed service account info. Args: info (Mapping[str, str]): The service account info in Google format. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.service_account.IDTokenCredentials: The constructed credentials. Raises: ValueError: If the info is not in the expected format. """ signer = _service_account_info.from_dict( info, require=['client_email', 'token_uri']) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod def from_service_account_file(cls, filename, **kwargs): """Creates a credentials instance from a service account json file. Args: filename (str): The path to the service account json file. kwargs: Additional arguments to pass to the constructor. Returns: google.auth.service_account.IDTokenCredentials: The constructed credentials. """ info, signer = _service_account_info.from_filename( filename, require=['client_email', 'token_uri']) return cls._from_signer_and_info(signer, info, **kwargs) def with_target_audience(self, target_audience): """Create a copy of these credentials with the specified target audience. Args: target_audience (str): The intended audience for these credentials, used when requesting the ID Token. Returns: google.auth.service_account.IDTokenCredentials: A new credentials instance. """ return self.__class__( self._signer, service_account_email=self._service_account_email, token_uri=self._token_uri, target_audience=target_audience, additional_claims=self._additional_claims.copy()) def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. This assertion is used during the OAuth 2.0 grant to acquire an ID token. Returns: bytes: The authorization grant assertion. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS) expiry = now + lifetime payload = { 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. 'iss': self.service_account_email, # The audience must be the auth token endpoint's URI 'aud': self._token_uri, # The target audience specifies which service the ID token is # intended for. 'target_audience': self._target_audience } payload.update(self._additional_claims) token = jwt.encode(self._signer, payload) return token @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): assertion = self._make_authorization_grant_assertion() access_token, expiry, _ = _client.id_token_jwt_grant( request, self._token_uri, assertion) self.token = access_token self.expiry = expiry @property def service_account_email(self): """The service account email.""" return self._service_account_email @_helpers.copy_docstring(credentials.Signing) def sign_bytes(self, message): return self._signer.sign(message) @property @_helpers.copy_docstring(credentials.Signing) def signer(self): return self._signer @property @_helpers.copy_docstring(credentials.Signing) def signer_email(self): return self._service_account_email google-auth-1.5.1/google/oauth2/__init__.py0000644110576702575230000000115413277071027023665 0ustar theaflowersprimarygroup00000000000000# 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. """Google OAuth 2.0 Library for Python.""" google-auth-1.5.1/google/oauth2/_client.py0000644110576702575230000001743613277071027023555 0ustar theaflowersprimarygroup00000000000000# 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. """OAuth 2.0 client. This is a client for interacting with an OAuth 2.0 authorization server's token endpoint. For more information about the token endpoint, see `Section 3.1 of rfc6749`_ .. _Section 3.1 of rfc6749: https://tools.ietf.org/html/rfc6749#section-3.2 """ import datetime import json import six from six.moves import http_client from six.moves import urllib from google.auth import _helpers from google.auth import exceptions from google.auth import jwt _URLENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded' _JWT_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer' _REFRESH_GRANT_TYPE = 'refresh_token' def _handle_error_response(response_body): """"Translates an error response into an exception. Args: response_body (str): The decoded response data. Raises: google.auth.exceptions.RefreshError """ try: error_data = json.loads(response_body) error_details = '{}: {}'.format( error_data['error'], error_data.get('error_description')) # If no details could be extracted, use the response data. except (KeyError, ValueError): error_details = response_body raise exceptions.RefreshError( error_details, response_body) def _parse_expiry(response_data): """Parses the expiry field from a response into a datetime. Args: response_data (Mapping): The JSON-parsed response data. Returns: Optional[datetime]: The expiration or ``None`` if no expiration was specified. """ expires_in = response_data.get('expires_in', None) if expires_in is not None: return _helpers.utcnow() + datetime.timedelta( seconds=expires_in) else: return None def _token_endpoint_request(request, token_uri, body): """Makes a request to the OAuth 2.0 authorization server's token endpoint. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. token_uri (str): The OAuth 2.0 authorizations server's token endpoint URI. body (Mapping[str, str]): The parameters to send in the request body. Returns: Mapping[str, str]: The JSON-decoded response data. Raises: google.auth.exceptions.RefreshError: If the token endpoint returned an error. """ body = urllib.parse.urlencode(body) headers = { 'content-type': _URLENCODED_CONTENT_TYPE, } response = request( method='POST', url=token_uri, headers=headers, body=body) response_body = response.data.decode('utf-8') if response.status != http_client.OK: _handle_error_response(response_body) response_data = json.loads(response_body) return response_data def jwt_grant(request, token_uri, assertion): """Implements the JWT Profile for OAuth 2.0 Authorization Grants. For more details, see `rfc7523 section 4`_. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. token_uri (str): The OAuth 2.0 authorizations server's token endpoint URI. assertion (str): The OAuth 2.0 assertion. Returns: Tuple[str, Optional[datetime], Mapping[str, str]]: The access token, expiration, and additional data returned by the token endpoint. Raises: google.auth.exceptions.RefreshError: If the token endpoint returned an error. .. _rfc7523 section 4: https://tools.ietf.org/html/rfc7523#section-4 """ body = { 'assertion': assertion, 'grant_type': _JWT_GRANT_TYPE, } response_data = _token_endpoint_request(request, token_uri, body) try: access_token = response_data['access_token'] except KeyError as caught_exc: new_exc = exceptions.RefreshError( 'No access token in response.', response_data) six.raise_from(new_exc, caught_exc) expiry = _parse_expiry(response_data) return access_token, expiry, response_data def id_token_jwt_grant(request, token_uri, assertion): """Implements the JWT Profile for OAuth 2.0 Authorization Grants, but requests an OpenID Connect ID Token instead of an access token. This is a variant on the standard JWT Profile that is currently unique to Google. This was added for the benefit of authenticating to services that require ID Tokens instead of access tokens or JWT bearer tokens. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. token_uri (str): The OAuth 2.0 authorization server's token endpoint URI. assertion (str): JWT token signed by a service account. The token's payload must include a ``target_audience`` claim. Returns: Tuple[str, Optional[datetime], Mapping[str, str]]: The (encoded) Open ID Connect ID Token, expiration, and additional data returned by the endpoint. Raises: google.auth.exceptions.RefreshError: If the token endpoint returned an error. """ body = { 'assertion': assertion, 'grant_type': _JWT_GRANT_TYPE, } response_data = _token_endpoint_request(request, token_uri, body) try: id_token = response_data['id_token'] except KeyError as caught_exc: new_exc = exceptions.RefreshError( 'No ID token in response.', response_data) six.raise_from(new_exc, caught_exc) payload = jwt.decode(id_token, verify=False) expiry = datetime.datetime.utcfromtimestamp(payload['exp']) return id_token, expiry, response_data def refresh_grant(request, token_uri, refresh_token, client_id, client_secret): """Implements the OAuth 2.0 refresh token grant. For more details, see `rfc678 section 6`_. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. token_uri (str): The OAuth 2.0 authorizations server's token endpoint URI. refresh_token (str): The refresh token to use to get a new access token. client_id (str): The OAuth 2.0 application's client ID. client_secret (str): The Oauth 2.0 appliaction's client secret. Returns: Tuple[str, Optional[str], Optional[datetime], Mapping[str, str]]: The access token, new refresh token, expiration, and additional data returned by the token endpoint. Raises: google.auth.exceptions.RefreshError: If the token endpoint returned an error. .. _rfc6748 section 6: https://tools.ietf.org/html/rfc6749#section-6 """ body = { 'grant_type': _REFRESH_GRANT_TYPE, 'client_id': client_id, 'client_secret': client_secret, 'refresh_token': refresh_token, } response_data = _token_endpoint_request(request, token_uri, body) try: access_token = response_data['access_token'] except KeyError as caught_exc: new_exc = exceptions.RefreshError( 'No access token in response.', response_data) six.raise_from(new_exc, caught_exc) refresh_token = response_data.get('refresh_token', refresh_token) expiry = _parse_expiry(response_data) return access_token, refresh_token, expiry, response_data google-auth-1.5.1/google/__init__.py0000644110576702575230000000141313277071027022461 0ustar theaflowersprimarygroup00000000000000# 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. """Google namespace package.""" try: import pkg_resources pkg_resources.declare_namespace(__name__) except ImportError: import pkgutil __path__ = pkgutil.extend_path(__path__, __name__) google-auth-1.5.1/tests/0000755110576702575230000000000013330167570020236 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/test__cloud_sdk.py0000644110576702575230000001165313277071027023764 0ustar theaflowersprimarygroup00000000000000# 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. import io import json import os import subprocess import mock import pytest from google.auth import _cloud_sdk from google.auth import environment_vars import google.oauth2.credentials DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, 'authorized_user.json') with io.open(AUTHORIZED_USER_FILE) as fh: AUTHORIZED_USER_FILE_DATA = json.load(fh) SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, 'service_account.json') with io.open(SERVICE_ACCOUNT_FILE) as fh: SERVICE_ACCOUNT_FILE_DATA = json.load(fh) with io.open(os.path.join(DATA_DIR, 'cloud_sdk_config.json'), 'rb') as fh: CLOUD_SDK_CONFIG_FILE_DATA = fh.read() @pytest.mark.parametrize('data, expected_project_id', [ (CLOUD_SDK_CONFIG_FILE_DATA, 'example-project'), (b'I am some bad json', None), (b'{}', None) ]) def test_get_project_id(data, expected_project_id): check_output_patch = mock.patch( 'subprocess.check_output', autospec=True, return_value=data) with check_output_patch as check_output: project_id = _cloud_sdk.get_project_id() assert project_id == expected_project_id assert check_output.called @mock.patch( 'subprocess.check_output', autospec=True, side_effect=subprocess.CalledProcessError(-1, None)) def test_get_project_id_call_error(check_output): project_id = _cloud_sdk.get_project_id() assert project_id is None assert check_output.called @mock.patch('os.name', new='nt') def test_get_project_id_windows(): check_output_patch = mock.patch( 'subprocess.check_output', autospec=True, return_value=CLOUD_SDK_CONFIG_FILE_DATA) with check_output_patch as check_output: project_id = _cloud_sdk.get_project_id() assert project_id == 'example-project' assert check_output.called # Make sure the executable is `gcloud.cmd`. args = check_output.call_args[0] command = args[0] executable = command[0] assert executable == 'gcloud.cmd' @mock.patch( 'google.auth._cloud_sdk.get_config_path', autospec=True) def test_get_application_default_credentials_path(get_config_dir): config_path = 'config_path' get_config_dir.return_value = config_path credentials_path = _cloud_sdk.get_application_default_credentials_path() assert credentials_path == os.path.join( config_path, _cloud_sdk._CREDENTIALS_FILENAME) def test_get_config_path_env_var(monkeypatch): config_path_sentinel = 'config_path' monkeypatch.setenv( environment_vars.CLOUD_SDK_CONFIG_DIR, config_path_sentinel) config_path = _cloud_sdk.get_config_path() assert config_path == config_path_sentinel @mock.patch('os.path.expanduser') def test_get_config_path_unix(expanduser): expanduser.side_effect = lambda path: path config_path = _cloud_sdk.get_config_path() assert os.path.split(config_path) == ( '~/.config', _cloud_sdk._CONFIG_DIRECTORY) @mock.patch('os.name', new='nt') def test_get_config_path_windows(monkeypatch): appdata = 'appdata' monkeypatch.setenv(_cloud_sdk._WINDOWS_CONFIG_ROOT_ENV_VAR, appdata) config_path = _cloud_sdk.get_config_path() assert os.path.split(config_path) == ( appdata, _cloud_sdk._CONFIG_DIRECTORY) @mock.patch('os.name', new='nt') def test_get_config_path_no_appdata(monkeypatch): monkeypatch.delenv(_cloud_sdk._WINDOWS_CONFIG_ROOT_ENV_VAR, raising=False) monkeypatch.setenv('SystemDrive', 'G:') config_path = _cloud_sdk.get_config_path() assert os.path.split(config_path) == ( 'G:/\\', _cloud_sdk._CONFIG_DIRECTORY) def test_load_authorized_user_credentials(): credentials = _cloud_sdk.load_authorized_user_credentials( AUTHORIZED_USER_FILE_DATA) assert isinstance(credentials, google.oauth2.credentials.Credentials) assert credentials.token is None assert (credentials._refresh_token == AUTHORIZED_USER_FILE_DATA['refresh_token']) assert credentials._client_id == AUTHORIZED_USER_FILE_DATA['client_id'] assert (credentials._client_secret == AUTHORIZED_USER_FILE_DATA['client_secret']) assert (credentials._token_uri == google.oauth2.credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT) def test_load_authorized_user_credentials_bad_format(): with pytest.raises(ValueError) as excinfo: _cloud_sdk.load_authorized_user_credentials({}) assert excinfo.match(r'missing fields') google-auth-1.5.1/tests/conftest.py0000644110576702575230000000226713277071027022445 0ustar theaflowersprimarygroup00000000000000# 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. import sys import mock import pytest @pytest.fixture def mock_non_existent_module(monkeypatch): """Mocks a non-existing module in sys.modules. Additionally mocks any non-existing modules specified in the dotted path. """ def _mock_non_existent_module(path): parts = path.split('.') partial = [] for part in parts: partial.append(part) current_module = '.'.join(partial) if current_module not in sys.modules: monkeypatch.setitem( sys.modules, current_module, mock.MagicMock()) return _mock_non_existent_module google-auth-1.5.1/tests/transport/0000755110576702575230000000000013330167570022272 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/transport/test_urllib3.py0000644110576702575230000001110113310773731025251 0ustar theaflowersprimarygroup00000000000000# 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. import mock from six.moves import http_client import urllib3 import google.auth.credentials import google.auth.transport.urllib3 from tests.transport import compliance class TestRequestResponse(compliance.RequestResponseTests): def make_request(self): http = urllib3.PoolManager() return google.auth.transport.urllib3.Request(http) def test_timeout(self): http = mock.create_autospec(urllib3.PoolManager) request = google.auth.transport.urllib3.Request(http) request(url='http://example.com', method='GET', timeout=5) assert http.request.call_args[1]['timeout'] == 5 def test__make_default_http_with_certifi(): http = google.auth.transport.urllib3._make_default_http() assert 'cert_reqs' in http.connection_pool_kw @mock.patch.object(google.auth.transport.urllib3, 'certifi', new=None) def test__make_default_http_without_certifi(): http = google.auth.transport.urllib3._make_default_http() assert 'cert_reqs' not in http.connection_pool_kw class CredentialsStub(google.auth.credentials.Credentials): def __init__(self, token='token'): super(CredentialsStub, self).__init__() self.token = token def apply(self, headers, token=None): headers['authorization'] = self.token def before_request(self, request, method, url, headers): self.apply(headers) def refresh(self, request): self.token += '1' class HttpStub(object): def __init__(self, responses, headers=None): self.responses = responses self.requests = [] self.headers = headers or {} def urlopen(self, method, url, body=None, headers=None, **kwargs): self.requests.append((method, url, body, headers, kwargs)) return self.responses.pop(0) class ResponseStub(object): def __init__(self, status=http_client.OK, data=None): self.status = status self.data = data class TestAuthorizedHttp(object): TEST_URL = 'http://example.com' def test_authed_http_defaults(self): authed_http = google.auth.transport.urllib3.AuthorizedHttp( mock.sentinel.credentials) assert authed_http.credentials == mock.sentinel.credentials assert isinstance(authed_http.http, urllib3.PoolManager) def test_urlopen_no_refresh(self): credentials = mock.Mock(wraps=CredentialsStub()) response = ResponseStub() http = HttpStub([response]) authed_http = google.auth.transport.urllib3.AuthorizedHttp( credentials, http=http) result = authed_http.urlopen('GET', self.TEST_URL) assert result == response assert credentials.before_request.called assert not credentials.refresh.called assert http.requests == [ ('GET', self.TEST_URL, None, {'authorization': 'token'}, {})] def test_urlopen_refresh(self): credentials = mock.Mock(wraps=CredentialsStub()) final_response = ResponseStub(status=http_client.OK) # First request will 401, second request will succeed. http = HttpStub([ ResponseStub(status=http_client.UNAUTHORIZED), final_response]) authed_http = google.auth.transport.urllib3.AuthorizedHttp( credentials, http=http) authed_http = authed_http.urlopen('GET', 'http://example.com') assert authed_http == final_response assert credentials.before_request.call_count == 2 assert credentials.refresh.called assert http.requests == [ ('GET', self.TEST_URL, None, {'authorization': 'token'}, {}), ('GET', self.TEST_URL, None, {'authorization': 'token1'}, {})] def test_proxies(self): http = mock.create_autospec(urllib3.PoolManager) authed_http = google.auth.transport.urllib3.AuthorizedHttp( None, http=http) with authed_http: pass assert http.__enter__.called assert http.__exit__.called authed_http.headers = mock.sentinel.headers assert authed_http.headers == http.headers google-auth-1.5.1/tests/transport/compliance.py0000644110576702575230000000713613277071027024766 0ustar theaflowersprimarygroup00000000000000# 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. import time import flask import pytest from pytest_localserver.http import WSGIServer from six.moves import http_client from google.auth import exceptions # .invalid will never resolve, see https://tools.ietf.org/html/rfc2606 NXDOMAIN = 'test.invalid' class RequestResponseTests(object): @pytest.fixture(scope='module') def server(self): """Provides a test HTTP server. The test server is automatically created before a test and destroyed at the end. The server is serving a test application that can be used to verify requests. """ app = flask.Flask(__name__) app.debug = True # pylint: disable=unused-variable # (pylint thinks the flask routes are unusued.) @app.route('/basic') def index(): header_value = flask.request.headers.get('x-test-header', 'value') headers = {'X-Test-Header': header_value} return 'Basic Content', http_client.OK, headers @app.route('/server_error') def server_error(): return 'Error', http_client.INTERNAL_SERVER_ERROR @app.route('/wait') def wait(): time.sleep(3) return 'Waited' # pylint: enable=unused-variable server = WSGIServer(application=app.wsgi_app) server.start() yield server server.stop() def test_request_basic(self, server): request = self.make_request() response = request(url=server.url + '/basic', method='GET') assert response.status == http_client.OK assert response.headers['x-test-header'] == 'value' assert response.data == b'Basic Content' def test_request_with_timeout_success(self, server): request = self.make_request() response = request(url=server.url + '/basic', method='GET', timeout=2) assert response.status == http_client.OK assert response.headers['x-test-header'] == 'value' assert response.data == b'Basic Content' def test_request_with_timeout_failure(self, server): request = self.make_request() with pytest.raises(exceptions.TransportError): request(url=server.url + '/wait', method='GET', timeout=1) def test_request_headers(self, server): request = self.make_request() response = request( url=server.url + '/basic', method='GET', headers={ 'x-test-header': 'hello world'}) assert response.status == http_client.OK assert response.headers['x-test-header'] == 'hello world' assert response.data == b'Basic Content' def test_request_error(self, server): request = self.make_request() response = request(url=server.url + '/server_error', method='GET') assert response.status == http_client.INTERNAL_SERVER_ERROR assert response.data == b'Error' def test_connection_error(self): request = self.make_request() with pytest.raises(exceptions.TransportError): request(url='http://{}'.format(NXDOMAIN), method='GET') google-auth-1.5.1/tests/transport/test_grpc.py0000644110576702575230000001167013277071027024644 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import mock import pytest from google.auth import _helpers from google.auth import credentials from google.auth import transport try: # pylint: disable=ungrouped-imports import grpc import google.auth.transport.grpc HAS_GRPC = True except ImportError: # pragma: NO COVER HAS_GRPC = False pytestmark = pytest.mark.skipif(not HAS_GRPC, reason='gRPC is unavailable.') class CredentialsStub(credentials.Credentials): def __init__(self, token='token'): super(CredentialsStub, self).__init__() self.token = token self.expiry = None def refresh(self, request): self.token += '1' class TestAuthMetadataPlugin(object): def test_call_no_refresh(self): credentials = CredentialsStub() request = mock.create_autospec(transport.Request) plugin = google.auth.transport.grpc.AuthMetadataPlugin( credentials, request) context = mock.create_autospec(grpc.AuthMetadataContext, instance=True) context.method_name = mock.sentinel.method_name context.service_url = mock.sentinel.service_url callback = mock.create_autospec(grpc.AuthMetadataPluginCallback) plugin(context, callback) callback.assert_called_once_with( [(u'authorization', u'Bearer {}'.format(credentials.token))], None) def test_call_refresh(self): credentials = CredentialsStub() credentials.expiry = datetime.datetime.min + _helpers.CLOCK_SKEW request = mock.create_autospec(transport.Request) plugin = google.auth.transport.grpc.AuthMetadataPlugin( credentials, request) context = mock.create_autospec(grpc.AuthMetadataContext, instance=True) context.method_name = mock.sentinel.method_name context.service_url = mock.sentinel.service_url callback = mock.create_autospec(grpc.AuthMetadataPluginCallback) plugin(context, callback) assert credentials.token == 'token1' callback.assert_called_once_with( [(u'authorization', u'Bearer {}'.format(credentials.token))], None) @mock.patch('grpc.composite_channel_credentials', autospec=True) @mock.patch('grpc.metadata_call_credentials', autospec=True) @mock.patch('grpc.ssl_channel_credentials', autospec=True) @mock.patch('grpc.secure_channel', autospec=True) def test_secure_authorized_channel( secure_channel, ssl_channel_credentials, metadata_call_credentials, composite_channel_credentials): credentials = CredentialsStub() request = mock.create_autospec(transport.Request) target = 'example.com:80' channel = google.auth.transport.grpc.secure_authorized_channel( credentials, request, target, options=mock.sentinel.options) # Check the auth plugin construction. auth_plugin = metadata_call_credentials.call_args[0][0] assert isinstance( auth_plugin, google.auth.transport.grpc.AuthMetadataPlugin) assert auth_plugin._credentials == credentials assert auth_plugin._request == request # Check the ssl channel call. assert ssl_channel_credentials.called # Check the composite credentials call. composite_channel_credentials.assert_called_once_with( ssl_channel_credentials.return_value, metadata_call_credentials.return_value) # Check the channel call. secure_channel.assert_called_once_with( target, composite_channel_credentials.return_value, options=mock.sentinel.options) assert channel == secure_channel.return_value @mock.patch('grpc.composite_channel_credentials', autospec=True) @mock.patch('grpc.metadata_call_credentials', autospec=True) @mock.patch('grpc.ssl_channel_credentials', autospec=True) @mock.patch('grpc.secure_channel', autospec=True) def test_secure_authorized_channel_explicit_ssl( secure_channel, ssl_channel_credentials, metadata_call_credentials, composite_channel_credentials): credentials = mock.Mock() request = mock.Mock() target = 'example.com:80' ssl_credentials = mock.Mock() google.auth.transport.grpc.secure_authorized_channel( credentials, request, target, ssl_credentials=ssl_credentials) # Check the ssl channel call. assert not ssl_channel_credentials.called # Check the composite credentials call. composite_channel_credentials.assert_called_once_with( ssl_credentials, metadata_call_credentials.return_value) google-auth-1.5.1/tests/transport/__init__.py0000644110576702575230000000000013277071027024372 0ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/transport/test_requests.py0000644110576702575230000001055213277071027025562 0ustar theaflowersprimarygroup00000000000000# 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. import mock import requests import requests.adapters from six.moves import http_client import google.auth.credentials import google.auth.transport.requests from tests.transport import compliance class TestRequestResponse(compliance.RequestResponseTests): def make_request(self): return google.auth.transport.requests.Request() def test_timeout(self): http = mock.create_autospec(requests.Session, instance=True) request = google.auth.transport.requests.Request(http) request(url='http://example.com', method='GET', timeout=5) assert http.request.call_args[1]['timeout'] == 5 class CredentialsStub(google.auth.credentials.Credentials): def __init__(self, token='token'): super(CredentialsStub, self).__init__() self.token = token def apply(self, headers, token=None): headers['authorization'] = self.token def before_request(self, request, method, url, headers): self.apply(headers) def refresh(self, request): self.token += '1' class AdapterStub(requests.adapters.BaseAdapter): def __init__(self, responses, headers=None): super(AdapterStub, self).__init__() self.responses = responses self.requests = [] self.headers = headers or {} def send(self, request, **kwargs): # pylint: disable=arguments-differ # request is the only required argument here and the only argument # we care about. self.requests.append(request) return self.responses.pop(0) def close(self): # pragma: NO COVER # pylint wants this to be here because it's abstract in the base # class, but requests never actually calls it. return def make_response(status=http_client.OK, data=None): response = requests.Response() response.status_code = status response._content = data return response class TestAuthorizedHttp(object): TEST_URL = 'http://example.com/' def test_constructor(self): authed_session = google.auth.transport.requests.AuthorizedSession( mock.sentinel.credentials) assert authed_session.credentials == mock.sentinel.credentials def test_request_no_refresh(self): credentials = mock.Mock(wraps=CredentialsStub()) response = make_response() adapter = AdapterStub([response]) authed_session = google.auth.transport.requests.AuthorizedSession( credentials) authed_session.mount(self.TEST_URL, adapter) result = authed_session.request('GET', self.TEST_URL) assert response == result assert credentials.before_request.called assert not credentials.refresh.called assert len(adapter.requests) == 1 assert adapter.requests[0].url == self.TEST_URL assert adapter.requests[0].headers['authorization'] == 'token' def test_request_refresh(self): credentials = mock.Mock(wraps=CredentialsStub()) final_response = make_response(status=http_client.OK) # First request will 401, second request will succeed. adapter = AdapterStub([ make_response(status=http_client.UNAUTHORIZED), final_response]) authed_session = google.auth.transport.requests.AuthorizedSession( credentials) authed_session.mount(self.TEST_URL, adapter) result = authed_session.request('GET', self.TEST_URL) assert result == final_response assert credentials.before_request.call_count == 2 assert credentials.refresh.called assert len(adapter.requests) == 2 assert adapter.requests[0].url == self.TEST_URL assert adapter.requests[0].headers['authorization'] == 'token' assert adapter.requests[1].url == self.TEST_URL assert adapter.requests[1].headers['authorization'] == 'token1' google-auth-1.5.1/tests/transport/test__http_client.py0000644110576702575230000000212713277071027026362 0ustar theaflowersprimarygroup00000000000000# 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. import pytest from google.auth import exceptions import google.auth.transport._http_client from tests.transport import compliance class TestRequestResponse(compliance.RequestResponseTests): def make_request(self): return google.auth.transport._http_client.Request() def test_non_http(self): request = self.make_request() with pytest.raises(exceptions.TransportError) as excinfo: request(url='https://{}'.format(compliance.NXDOMAIN), method='GET') assert excinfo.match('https') google-auth-1.5.1/tests/crypt/0000755110576702575230000000000013330167570021377 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/crypt/__init__.py0000644110576702575230000000000013277071027023477 0ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/crypt/test__cryptography_rsa.py0000644110576702575230000001456013277071027026556 0ustar theaflowersprimarygroup00000000000000# 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. import json import os from cryptography.hazmat.primitives.asymmetric import rsa import pytest from google.auth import _helpers from google.auth.crypt import _cryptography_rsa from google.auth.crypt import base DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') # To generate privatekey.pem, privatekey.pub, and public_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ # > -keyout privatekey.pem # $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: PRIVATE_KEY_BYTES = fh.read() PKCS1_KEY_BYTES = PRIVATE_KEY_BYTES with open(os.path.join(DATA_DIR, 'privatekey.pub'), 'rb') as fh: PUBLIC_KEY_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: PUBLIC_CERT_BYTES = fh.read() # To generate pem_from_pkcs12.pem and privatekey.p12: # $ openssl pkcs12 -export -out privatekey.p12 -inkey privatekey.pem \ # > -in public_cert.pem # $ openssl pkcs12 -in privatekey.p12 -nocerts -nodes \ # > -out pem_from_pkcs12.pem with open(os.path.join(DATA_DIR, 'pem_from_pkcs12.pem'), 'rb') as fh: PKCS8_KEY_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'privatekey.p12'), 'rb') as fh: PKCS12_KEY_BYTES = fh.read() # The service account JSON file can be generated from the Google Cloud Console. SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: SERVICE_ACCOUNT_INFO = json.load(fh) class TestRSAVerifier(object): def test_verify_success(self): to_sign = b'foo' signer = _cryptography_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) assert verifier.verify(to_sign, actual_signature) def test_verify_unicode_success(self): to_sign = u'foo' signer = _cryptography_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) assert verifier.verify(to_sign, actual_signature) def test_verify_failure(self): verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) bad_signature1 = b'' assert not verifier.verify(b'foo', bad_signature1) bad_signature2 = b'a' assert not verifier.verify(b'foo', bad_signature2) def test_from_string_pub_key(self): verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) assert isinstance(verifier, _cryptography_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.RSAPublicKey) def test_from_string_pub_key_unicode(self): public_key = _helpers.from_bytes(PUBLIC_KEY_BYTES) verifier = _cryptography_rsa.RSAVerifier.from_string(public_key) assert isinstance(verifier, _cryptography_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.RSAPublicKey) def test_from_string_pub_cert(self): verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_CERT_BYTES) assert isinstance(verifier, _cryptography_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.RSAPublicKey) def test_from_string_pub_cert_unicode(self): public_cert = _helpers.from_bytes(PUBLIC_CERT_BYTES) verifier = _cryptography_rsa.RSAVerifier.from_string(public_cert) assert isinstance(verifier, _cryptography_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.RSAPublicKey) class TestRSASigner(object): def test_from_string_pkcs1(self): signer = _cryptography_rsa.RSASigner.from_string(PKCS1_KEY_BYTES) assert isinstance(signer, _cryptography_rsa.RSASigner) assert isinstance(signer._key, rsa.RSAPrivateKey) def test_from_string_pkcs1_unicode(self): key_bytes = _helpers.from_bytes(PKCS1_KEY_BYTES) signer = _cryptography_rsa.RSASigner.from_string(key_bytes) assert isinstance(signer, _cryptography_rsa.RSASigner) assert isinstance(signer._key, rsa.RSAPrivateKey) def test_from_string_pkcs8(self): signer = _cryptography_rsa.RSASigner.from_string(PKCS8_KEY_BYTES) assert isinstance(signer, _cryptography_rsa.RSASigner) assert isinstance(signer._key, rsa.RSAPrivateKey) def test_from_string_pkcs8_unicode(self): key_bytes = _helpers.from_bytes(PKCS8_KEY_BYTES) signer = _cryptography_rsa.RSASigner.from_string(key_bytes) assert isinstance(signer, _cryptography_rsa.RSASigner) assert isinstance(signer._key, rsa.RSAPrivateKey) def test_from_string_pkcs12(self): with pytest.raises(ValueError): _cryptography_rsa.RSASigner.from_string(PKCS12_KEY_BYTES) def test_from_string_bogus_key(self): key_bytes = 'bogus-key' with pytest.raises(ValueError): _cryptography_rsa.RSASigner.from_string(key_bytes) def test_from_service_account_info(self): signer = _cryptography_rsa.RSASigner.from_service_account_info( SERVICE_ACCOUNT_INFO) assert signer.key_id == SERVICE_ACCOUNT_INFO[ base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.RSAPrivateKey) def test_from_service_account_info_missing_key(self): with pytest.raises(ValueError) as excinfo: _cryptography_rsa.RSASigner.from_service_account_info({}) assert excinfo.match(base._JSON_FILE_PRIVATE_KEY) def test_from_service_account_file(self): signer = _cryptography_rsa.RSASigner.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE) assert signer.key_id == SERVICE_ACCOUNT_INFO[ base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.RSAPrivateKey) google-auth-1.5.1/tests/crypt/test__python_rsa.py0000644110576702575230000001665113277071027025347 0ustar theaflowersprimarygroup00000000000000# 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. import json import os import mock from pyasn1_modules import pem import pytest import rsa import six from google.auth import _helpers from google.auth.crypt import _python_rsa from google.auth.crypt import base DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') # To generate privatekey.pem, privatekey.pub, and public_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ # > -keyout privatekey.pem # $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: PRIVATE_KEY_BYTES = fh.read() PKCS1_KEY_BYTES = PRIVATE_KEY_BYTES with open(os.path.join(DATA_DIR, 'privatekey.pub'), 'rb') as fh: PUBLIC_KEY_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: PUBLIC_CERT_BYTES = fh.read() # To generate pem_from_pkcs12.pem and privatekey.p12: # $ openssl pkcs12 -export -out privatekey.p12 -inkey privatekey.pem \ # > -in public_cert.pem # $ openssl pkcs12 -in privatekey.p12 -nocerts -nodes \ # > -out pem_from_pkcs12.pem with open(os.path.join(DATA_DIR, 'pem_from_pkcs12.pem'), 'rb') as fh: PKCS8_KEY_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'privatekey.p12'), 'rb') as fh: PKCS12_KEY_BYTES = fh.read() # The service account JSON file can be generated from the Google Cloud Console. SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: SERVICE_ACCOUNT_INFO = json.load(fh) class TestRSAVerifier(object): def test_verify_success(self): to_sign = b'foo' signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) assert verifier.verify(to_sign, actual_signature) def test_verify_unicode_success(self): to_sign = u'foo' signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) assert verifier.verify(to_sign, actual_signature) def test_verify_failure(self): verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) bad_signature1 = b'' assert not verifier.verify(b'foo', bad_signature1) bad_signature2 = b'a' assert not verifier.verify(b'foo', bad_signature2) def test_from_string_pub_key(self): verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) assert isinstance(verifier, _python_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.key.PublicKey) def test_from_string_pub_key_unicode(self): public_key = _helpers.from_bytes(PUBLIC_KEY_BYTES) verifier = _python_rsa.RSAVerifier.from_string(public_key) assert isinstance(verifier, _python_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.key.PublicKey) def test_from_string_pub_cert(self): verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_CERT_BYTES) assert isinstance(verifier, _python_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.key.PublicKey) def test_from_string_pub_cert_unicode(self): public_cert = _helpers.from_bytes(PUBLIC_CERT_BYTES) verifier = _python_rsa.RSAVerifier.from_string(public_cert) assert isinstance(verifier, _python_rsa.RSAVerifier) assert isinstance(verifier._pubkey, rsa.key.PublicKey) def test_from_string_pub_cert_failure(self): cert_bytes = PUBLIC_CERT_BYTES true_der = rsa.pem.load_pem(cert_bytes, 'CERTIFICATE') load_pem_patch = mock.patch( 'rsa.pem.load_pem', return_value=true_der + b'extra', autospec=True) with load_pem_patch as load_pem: with pytest.raises(ValueError): _python_rsa.RSAVerifier.from_string(cert_bytes) load_pem.assert_called_once_with(cert_bytes, 'CERTIFICATE') class TestRSASigner(object): def test_from_string_pkcs1(self): signer = _python_rsa.RSASigner.from_string(PKCS1_KEY_BYTES) assert isinstance(signer, _python_rsa.RSASigner) assert isinstance(signer._key, rsa.key.PrivateKey) def test_from_string_pkcs1_unicode(self): key_bytes = _helpers.from_bytes(PKCS1_KEY_BYTES) signer = _python_rsa.RSASigner.from_string(key_bytes) assert isinstance(signer, _python_rsa.RSASigner) assert isinstance(signer._key, rsa.key.PrivateKey) def test_from_string_pkcs8(self): signer = _python_rsa.RSASigner.from_string(PKCS8_KEY_BYTES) assert isinstance(signer, _python_rsa.RSASigner) assert isinstance(signer._key, rsa.key.PrivateKey) def test_from_string_pkcs8_extra_bytes(self): key_bytes = PKCS8_KEY_BYTES _, pem_bytes = pem.readPemBlocksFromFile( six.StringIO(_helpers.from_bytes(key_bytes)), _python_rsa._PKCS8_MARKER) key_info, remaining = None, 'extra' decode_patch = mock.patch( 'pyasn1.codec.der.decoder.decode', return_value=(key_info, remaining), autospec=True) with decode_patch as decode: with pytest.raises(ValueError): _python_rsa.RSASigner.from_string(key_bytes) # Verify mock was called. decode.assert_called_once_with( pem_bytes, asn1Spec=_python_rsa._PKCS8_SPEC) def test_from_string_pkcs8_unicode(self): key_bytes = _helpers.from_bytes(PKCS8_KEY_BYTES) signer = _python_rsa.RSASigner.from_string(key_bytes) assert isinstance(signer, _python_rsa.RSASigner) assert isinstance(signer._key, rsa.key.PrivateKey) def test_from_string_pkcs12(self): with pytest.raises(ValueError): _python_rsa.RSASigner.from_string(PKCS12_KEY_BYTES) def test_from_string_bogus_key(self): key_bytes = 'bogus-key' with pytest.raises(ValueError): _python_rsa.RSASigner.from_string(key_bytes) def test_from_service_account_info(self): signer = _python_rsa.RSASigner.from_service_account_info( SERVICE_ACCOUNT_INFO) assert signer.key_id == SERVICE_ACCOUNT_INFO[ base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.key.PrivateKey) def test_from_service_account_info_missing_key(self): with pytest.raises(ValueError) as excinfo: _python_rsa.RSASigner.from_service_account_info({}) assert excinfo.match(base._JSON_FILE_PRIVATE_KEY) def test_from_service_account_file(self): signer = _python_rsa.RSASigner.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE) assert signer.key_id == SERVICE_ACCOUNT_INFO[ base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.key.PrivateKey) google-auth-1.5.1/tests/crypt/test_crypt.py0000644110576702575230000000361013277071027024152 0ustar theaflowersprimarygroup00000000000000# 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. import os from google.auth import crypt DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') # To generate privatekey.pem, privatekey.pub, and public_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ # > -keyout privatekey.pem # $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: PRIVATE_KEY_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: PUBLIC_CERT_BYTES = fh.read() # To generate other_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out other_cert.pem with open(os.path.join(DATA_DIR, 'other_cert.pem'), 'rb') as fh: OTHER_CERT_BYTES = fh.read() def test_verify_signature(): to_sign = b'foo' signer = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES) signature = signer.sign(to_sign) assert crypt.verify_signature( to_sign, signature, PUBLIC_CERT_BYTES) # List of certs assert crypt.verify_signature( to_sign, signature, [OTHER_CERT_BYTES, PUBLIC_CERT_BYTES]) def test_verify_signature_failure(): to_sign = b'foo' signer = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES) signature = signer.sign(to_sign) assert not crypt.verify_signature( to_sign, signature, OTHER_CERT_BYTES) google-auth-1.5.1/tests/test__service_account_info.py0000644110576702575230000000354713277071027026207 0ustar theaflowersprimarygroup00000000000000# 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. import json import os import pytest import six from google.auth import _service_account_info from google.auth import crypt DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: SERVICE_ACCOUNT_INFO = json.load(fh) def test_from_dict(): signer = _service_account_info.from_dict(SERVICE_ACCOUNT_INFO) assert isinstance(signer, crypt.RSASigner) assert signer.key_id == SERVICE_ACCOUNT_INFO['private_key_id'] def test_from_dict_bad_private_key(): info = SERVICE_ACCOUNT_INFO.copy() info['private_key'] = 'garbage' with pytest.raises(ValueError) as excinfo: _service_account_info.from_dict(info) assert excinfo.match(r'key') def test_from_dict_bad_format(): with pytest.raises(ValueError) as excinfo: _service_account_info.from_dict({}, require=('meep',)) assert excinfo.match(r'missing fields') def test_from_filename(): info, signer = _service_account_info.from_filename( SERVICE_ACCOUNT_JSON_FILE) for key, value in six.iteritems(SERVICE_ACCOUNT_INFO): assert info[key] == value assert isinstance(signer, crypt.RSASigner) assert signer.key_id == SERVICE_ACCOUNT_INFO['private_key_id'] google-auth-1.5.1/tests/oauth2/0000755110576702575230000000000013330167570021440 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/oauth2/test_id_token.py0000644110576702575230000000726113277071027024654 0ustar theaflowersprimarygroup00000000000000# Copyright 2014 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. import json import mock import pytest from google.auth import exceptions from google.auth import transport from google.oauth2 import id_token def make_request(status, data=None): response = mock.create_autospec(transport.Response, instance=True) response.status = status if data is not None: response.data = json.dumps(data).encode('utf-8') request = mock.create_autospec(transport.Request) request.return_value = response return request def test__fetch_certs_success(): certs = {'1': 'cert'} request = make_request(200, certs) returned_certs = id_token._fetch_certs(request, mock.sentinel.cert_url) request.assert_called_once_with(mock.sentinel.cert_url, method='GET') assert returned_certs == certs def test__fetch_certs_failure(): request = make_request(404) with pytest.raises(exceptions.TransportError): id_token._fetch_certs(request, mock.sentinel.cert_url) request.assert_called_once_with(mock.sentinel.cert_url, method='GET') @mock.patch('google.auth.jwt.decode', autospec=True) @mock.patch('google.oauth2.id_token._fetch_certs', autospec=True) def test_verify_token(_fetch_certs, decode): result = id_token.verify_token(mock.sentinel.token, mock.sentinel.request) assert result == decode.return_value _fetch_certs.assert_called_once_with( mock.sentinel.request, id_token._GOOGLE_OAUTH2_CERTS_URL) decode.assert_called_once_with( mock.sentinel.token, certs=_fetch_certs.return_value, audience=None) @mock.patch('google.auth.jwt.decode', autospec=True) @mock.patch('google.oauth2.id_token._fetch_certs', autospec=True) def test_verify_token_args(_fetch_certs, decode): result = id_token.verify_token( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience, certs_url=mock.sentinel.certs_url) assert result == decode.return_value _fetch_certs.assert_called_once_with( mock.sentinel.request, mock.sentinel.certs_url) decode.assert_called_once_with( mock.sentinel.token, certs=_fetch_certs.return_value, audience=mock.sentinel.audience) @mock.patch('google.oauth2.id_token.verify_token', autospec=True) def test_verify_oauth2_token(verify_token): result = id_token.verify_oauth2_token( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience) assert result == verify_token.return_value verify_token.assert_called_once_with( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience, certs_url=id_token._GOOGLE_OAUTH2_CERTS_URL) @mock.patch('google.oauth2.id_token.verify_token', autospec=True) def test_verify_firebase_token(verify_token): result = id_token.verify_firebase_token( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience) assert result == verify_token.return_value verify_token.assert_called_once_with( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience, certs_url=id_token._GOOGLE_APIS_CERTS_URL) google-auth-1.5.1/tests/oauth2/__init__.py0000644110576702575230000000000013277071027023540 0ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/oauth2/test_credentials.py0000644110576702575230000001251113301620717025341 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import json import os import mock import pytest from google.auth import _helpers from google.auth import exceptions from google.auth import transport from google.oauth2 import credentials DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') AUTH_USER_JSON_FILE = os.path.join(DATA_DIR, 'authorized_user.json') with open(AUTH_USER_JSON_FILE, 'r') as fh: AUTH_USER_INFO = json.load(fh) class TestCredentials(object): TOKEN_URI = 'https://example.com/oauth2/token' REFRESH_TOKEN = 'refresh_token' CLIENT_ID = 'client_id' CLIENT_SECRET = 'client_secret' @classmethod def make_credentials(cls): return credentials.Credentials( token=None, refresh_token=cls.REFRESH_TOKEN, token_uri=cls.TOKEN_URI, client_id=cls.CLIENT_ID, client_secret=cls.CLIENT_SECRET) def test_default_state(self): credentials = self.make_credentials() assert not credentials.valid # Expiration hasn't been set yet assert not credentials.expired # Scopes aren't required for these credentials assert not credentials.requires_scopes # Test properties assert credentials.refresh_token == self.REFRESH_TOKEN assert credentials.token_uri == self.TOKEN_URI assert credentials.client_id == self.CLIENT_ID assert credentials.client_secret == self.CLIENT_SECRET @mock.patch('google.oauth2._client.refresh_grant', autospec=True) @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.min + _helpers.CLOCK_SKEW) def test_refresh_success(self, unused_utcnow, refresh_grant): token = 'token' expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) grant_response = {'id_token': mock.sentinel.id_token} refresh_grant.return_value = ( # Access token token, # New refresh token None, # Expiry, expiry, # Extra data grant_response) request = mock.create_autospec(transport.Request) credentials = self.make_credentials() # Refresh credentials credentials.refresh(request) # Check jwt grant call. refresh_grant.assert_called_with( request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET) # Check that the credentials have the token and expiry assert credentials.token == token assert credentials.expiry == expiry assert credentials.id_token == mock.sentinel.id_token # Check that the credentials are valid (have a token and are not # expired) assert credentials.valid def test_refresh_no_refresh_token(self): request = mock.create_autospec(transport.Request) credentials_ = credentials.Credentials( token=None, refresh_token=None) with pytest.raises(exceptions.RefreshError, match='necessary fields'): credentials_.refresh(request) request.assert_not_called() def test_from_authorized_user_info(self): info = AUTH_USER_INFO.copy() creds = credentials.Credentials.from_authorized_user_info(info) assert creds.client_secret == info['client_secret'] assert creds.client_id == info['client_id'] assert creds.refresh_token == info['refresh_token'] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None scopes = ['email', 'profile'] creds = credentials.Credentials.from_authorized_user_info( info, scopes) assert creds.client_secret == info['client_secret'] assert creds.client_id == info['client_id'] assert creds.refresh_token == info['refresh_token'] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes def test_from_authorized_user_file(self): info = AUTH_USER_INFO.copy() creds = credentials.Credentials.from_authorized_user_file( AUTH_USER_JSON_FILE) assert creds.client_secret == info['client_secret'] assert creds.client_id == info['client_id'] assert creds.refresh_token == info['refresh_token'] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None scopes = ['email', 'profile'] creds = credentials.Credentials.from_authorized_user_file( AUTH_USER_JSON_FILE, scopes) assert creds.client_secret == info['client_secret'] assert creds.client_id == info['client_id'] assert creds.refresh_token == info['refresh_token'] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes google-auth-1.5.1/tests/oauth2/test__client.py0000644110576702575230000001461113277071027024472 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import json import os import mock import pytest import six from six.moves import http_client from six.moves import urllib from google.auth import _helpers from google.auth import crypt from google.auth import exceptions from google.auth import jwt from google.auth import transport from google.oauth2 import _client DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: PRIVATE_KEY_BYTES = fh.read() SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1') def test__handle_error_response(): response_data = json.dumps({ 'error': 'help', 'error_description': 'I\'m alive'}) with pytest.raises(exceptions.RefreshError) as excinfo: _client._handle_error_response(response_data) assert excinfo.match(r'help: I\'m alive') def test__handle_error_response_non_json(): response_data = 'Help, I\'m alive' with pytest.raises(exceptions.RefreshError) as excinfo: _client._handle_error_response(response_data) assert excinfo.match(r'Help, I\'m alive') @mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) def test__parse_expiry(unused_utcnow): result = _client._parse_expiry({'expires_in': 500}) assert result == datetime.datetime.min + datetime.timedelta(seconds=500) def test__parse_expiry_none(): assert _client._parse_expiry({}) is None def make_request(response_data, status=http_client.OK): response = mock.create_autospec(transport.Response, instance=True) response.status = status response.data = json.dumps(response_data).encode('utf-8') request = mock.create_autospec(transport.Request) request.return_value = response return request def test__token_endpoint_request(): request = make_request({'test': 'response'}) result = _client._token_endpoint_request( request, 'http://example.com', {'test': 'params'}) # Check request call request.assert_called_with( method='POST', url='http://example.com', headers={'content-type': 'application/x-www-form-urlencoded'}, body='test=params') # Check result assert result == {'test': 'response'} def test__token_endpoint_request_error(): request = make_request({}, status=http_client.BAD_REQUEST) with pytest.raises(exceptions.RefreshError): _client._token_endpoint_request(request, 'http://example.com', {}) def verify_request_params(request, params): request_body = request.call_args[1]['body'] request_params = urllib.parse.parse_qs(request_body) for key, value in six.iteritems(params): assert request_params[key][0] == value @mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) def test_jwt_grant(utcnow): request = make_request({ 'access_token': 'token', 'expires_in': 500, 'extra': 'data'}) token, expiry, extra_data = _client.jwt_grant( request, 'http://example.com', 'assertion_value') # Check request call verify_request_params(request, { 'grant_type': _client._JWT_GRANT_TYPE, 'assertion': 'assertion_value' }) # Check result assert token == 'token' assert expiry == utcnow() + datetime.timedelta(seconds=500) assert extra_data['extra'] == 'data' def test_jwt_grant_no_access_token(): request = make_request({ # No access token. 'expires_in': 500, 'extra': 'data'}) with pytest.raises(exceptions.RefreshError): _client.jwt_grant(request, 'http://example.com', 'assertion_value') def test_id_token_jwt_grant(): now = _helpers.utcnow() id_token_expiry = _helpers.datetime_to_secs(now) id_token = jwt.encode(SIGNER, {'exp': id_token_expiry}).decode('utf-8') request = make_request({ 'id_token': id_token, 'extra': 'data'}) token, expiry, extra_data = _client.id_token_jwt_grant( request, 'http://example.com', 'assertion_value') # Check request call verify_request_params(request, { 'grant_type': _client._JWT_GRANT_TYPE, 'assertion': 'assertion_value' }) # Check result assert token == id_token # JWT does not store microseconds now = now.replace(microsecond=0) assert expiry == now assert extra_data['extra'] == 'data' def test_id_token_jwt_grant_no_access_token(): request = make_request({ # No access token. 'expires_in': 500, 'extra': 'data'}) with pytest.raises(exceptions.RefreshError): _client.id_token_jwt_grant( request, 'http://example.com', 'assertion_value') @mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) def test_refresh_grant(unused_utcnow): request = make_request({ 'access_token': 'token', 'refresh_token': 'new_refresh_token', 'expires_in': 500, 'extra': 'data'}) token, refresh_token, expiry, extra_data = _client.refresh_grant( request, 'http://example.com', 'refresh_token', 'client_id', 'client_secret') # Check request call verify_request_params(request, { 'grant_type': _client._REFRESH_GRANT_TYPE, 'refresh_token': 'refresh_token', 'client_id': 'client_id', 'client_secret': 'client_secret' }) # Check result assert token == 'token' assert refresh_token == 'new_refresh_token' assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500) assert extra_data['extra'] == 'data' def test_refresh_grant_no_access_token(): request = make_request({ # No access token. 'refresh_token': 'new_refresh_token', 'expires_in': 500, 'extra': 'data'}) with pytest.raises(exceptions.RefreshError): _client.refresh_grant( request, 'http://example.com', 'refresh_token', 'client_id', 'client_secret') google-auth-1.5.1/tests/oauth2/test_service_account.py0000644110576702575230000003174713277071027026242 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import json import os import mock from google.auth import _helpers from google.auth import crypt from google.auth import jwt from google.auth import transport from google.oauth2 import service_account DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: PRIVATE_KEY_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: PUBLIC_CERT_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'other_cert.pem'), 'rb') as fh: OTHER_CERT_BYTES = fh.read() SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: SERVICE_ACCOUNT_INFO = json.load(fh) SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1') class TestCredentials(object): SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' TOKEN_URI = 'https://example.com/oauth2/token' @classmethod def make_credentials(cls): return service_account.Credentials( SIGNER, cls.SERVICE_ACCOUNT_EMAIL, cls.TOKEN_URI) def test_from_service_account_info(self): credentials = service_account.Credentials.from_service_account_info( SERVICE_ACCOUNT_INFO) assert (credentials._signer.key_id == SERVICE_ACCOUNT_INFO['private_key_id']) assert (credentials.service_account_email == SERVICE_ACCOUNT_INFO['client_email']) assert credentials._token_uri == SERVICE_ACCOUNT_INFO['token_uri'] def test_from_service_account_info_args(self): info = SERVICE_ACCOUNT_INFO.copy() scopes = ['email', 'profile'] subject = 'subject' additional_claims = {'meta': 'data'} credentials = service_account.Credentials.from_service_account_info( info, scopes=scopes, subject=subject, additional_claims=additional_claims) assert credentials.service_account_email == info['client_email'] assert credentials.project_id == info['project_id'] assert credentials._signer.key_id == info['private_key_id'] assert credentials._token_uri == info['token_uri'] assert credentials._scopes == scopes assert credentials._subject == subject assert credentials._additional_claims == additional_claims def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = service_account.Credentials.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE) assert credentials.service_account_email == info['client_email'] assert credentials.project_id == info['project_id'] assert credentials._signer.key_id == info['private_key_id'] assert credentials._token_uri == info['token_uri'] def test_from_service_account_file_args(self): info = SERVICE_ACCOUNT_INFO.copy() scopes = ['email', 'profile'] subject = 'subject' additional_claims = {'meta': 'data'} credentials = service_account.Credentials.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE, subject=subject, scopes=scopes, additional_claims=additional_claims) assert credentials.service_account_email == info['client_email'] assert credentials.project_id == info['project_id'] assert credentials._signer.key_id == info['private_key_id'] assert credentials._token_uri == info['token_uri'] assert credentials._scopes == scopes assert credentials._subject == subject assert credentials._additional_claims == additional_claims def test_default_state(self): credentials = self.make_credentials() assert not credentials.valid # Expiration hasn't been set yet assert not credentials.expired # Scopes haven't been specified yet assert credentials.requires_scopes def test_sign_bytes(self): credentials = self.make_credentials() to_sign = b'123' signature = credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) def test_signer(self): credentials = self.make_credentials() assert isinstance(credentials.signer, crypt.Signer) def test_signer_email(self): credentials = self.make_credentials() assert credentials.signer_email == self.SERVICE_ACCOUNT_EMAIL def test_create_scoped(self): credentials = self.make_credentials() scopes = ['email', 'profile'] credentials = credentials.with_scopes(scopes) assert credentials._scopes == scopes def test_with_claims(self): credentials = self.make_credentials() new_credentials = credentials.with_claims({'meep': 'moop'}) assert new_credentials._additional_claims == {'meep': 'moop'} def test__make_authorization_grant_assertion(self): credentials = self.make_credentials() token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL assert payload['aud'] == self.TOKEN_URI def test__make_authorization_grant_assertion_scoped(self): credentials = self.make_credentials() scopes = ['email', 'profile'] credentials = credentials.with_scopes(scopes) token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) assert payload['scope'] == 'email profile' def test__make_authorization_grant_assertion_subject(self): credentials = self.make_credentials() subject = 'user@example.com' credentials = credentials.with_subject(subject) token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) assert payload['sub'] == subject @mock.patch('google.oauth2._client.jwt_grant', autospec=True) def test_refresh_success(self, jwt_grant): credentials = self.make_credentials() token = 'token' jwt_grant.return_value = ( token, _helpers.utcnow() + datetime.timedelta(seconds=500), {}) request = mock.create_autospec(transport.Request, instance=True) # Refresh credentials credentials.refresh(request) # Check jwt grant call. assert jwt_grant.called called_request, token_uri, assertion = jwt_grant.call_args[0] assert called_request == request assert token_uri == credentials._token_uri assert jwt.decode(assertion, PUBLIC_CERT_BYTES) # No further assertion done on the token, as there are separate tests # for checking the authorization grant assertion. # Check that the credentials have the token. assert credentials.token == token # Check that the credentials are valid (have a token and are not # expired) assert credentials.valid @mock.patch('google.oauth2._client.jwt_grant', autospec=True) def test_before_request_refreshes(self, jwt_grant): credentials = self.make_credentials() token = 'token' jwt_grant.return_value = ( token, _helpers.utcnow() + datetime.timedelta(seconds=500), None) request = mock.create_autospec(transport.Request, instance=True) # Credentials should start as invalid assert not credentials.valid # before_request should cause a refresh credentials.before_request( request, 'GET', 'http://example.com?a=1#3', {}) # The refresh endpoint should've been called. assert jwt_grant.called # Credentials should now be valid. assert credentials.valid class TestIDTokenCredentials(object): SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' TOKEN_URI = 'https://example.com/oauth2/token' TARGET_AUDIENCE = 'https://example.com' @classmethod def make_credentials(cls): return service_account.IDTokenCredentials( SIGNER, cls.SERVICE_ACCOUNT_EMAIL, cls.TOKEN_URI, cls.TARGET_AUDIENCE) def test_from_service_account_info(self): credentials = ( service_account.IDTokenCredentials.from_service_account_info( SERVICE_ACCOUNT_INFO, target_audience=self.TARGET_AUDIENCE)) assert (credentials._signer.key_id == SERVICE_ACCOUNT_INFO['private_key_id']) assert (credentials.service_account_email == SERVICE_ACCOUNT_INFO['client_email']) assert credentials._token_uri == SERVICE_ACCOUNT_INFO['token_uri'] assert credentials._target_audience == self.TARGET_AUDIENCE def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = ( service_account.IDTokenCredentials.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE, target_audience=self.TARGET_AUDIENCE)) assert credentials.service_account_email == info['client_email'] assert credentials._signer.key_id == info['private_key_id'] assert credentials._token_uri == info['token_uri'] assert credentials._target_audience == self.TARGET_AUDIENCE def test_default_state(self): credentials = self.make_credentials() assert not credentials.valid # Expiration hasn't been set yet assert not credentials.expired def test_sign_bytes(self): credentials = self.make_credentials() to_sign = b'123' signature = credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) def test_signer(self): credentials = self.make_credentials() assert isinstance(credentials.signer, crypt.Signer) def test_signer_email(self): credentials = self.make_credentials() assert credentials.signer_email == self.SERVICE_ACCOUNT_EMAIL def test_with_target_audience(self): credentials = self.make_credentials() new_credentials = credentials.with_target_audience( 'https://new.example.com') assert new_credentials._target_audience == 'https://new.example.com' def test__make_authorization_grant_assertion(self): credentials = self.make_credentials() token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL assert payload['aud'] == self.TOKEN_URI assert payload['target_audience'] == self.TARGET_AUDIENCE @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) def test_refresh_success(self, id_token_jwt_grant): credentials = self.make_credentials() token = 'token' id_token_jwt_grant.return_value = ( token, _helpers.utcnow() + datetime.timedelta(seconds=500), {}) request = mock.create_autospec(transport.Request, instance=True) # Refresh credentials credentials.refresh(request) # Check jwt grant call. assert id_token_jwt_grant.called called_request, token_uri, assertion = id_token_jwt_grant.call_args[0] assert called_request == request assert token_uri == credentials._token_uri assert jwt.decode(assertion, PUBLIC_CERT_BYTES) # No further assertion done on the token, as there are separate tests # for checking the authorization grant assertion. # Check that the credentials have the token. assert credentials.token == token # Check that the credentials are valid (have a token and are not # expired) assert credentials.valid @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) def test_before_request_refreshes(self, id_token_jwt_grant): credentials = self.make_credentials() token = 'token' id_token_jwt_grant.return_value = ( token, _helpers.utcnow() + datetime.timedelta(seconds=500), None) request = mock.create_autospec(transport.Request, instance=True) # Credentials should start as invalid assert not credentials.valid # before_request should cause a refresh credentials.before_request( request, 'GET', 'http://example.com?a=1#3', {}) # The refresh endpoint should've been called. assert id_token_jwt_grant.called # Credentials should now be valid. assert credentials.valid google-auth-1.5.1/tests/__init__.py0000644110576702575230000000000013277071027022336 0ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/test_iam.py0000644110576702575230000000602713277071027022423 0ustar theaflowersprimarygroup00000000000000# Copyright 2017 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. import base64 import datetime import json import mock import pytest from six.moves import http_client from google.auth import _helpers from google.auth import exceptions from google.auth import iam from google.auth import transport import google.auth.credentials def make_request(status, data=None): response = mock.create_autospec(transport.Response, instance=True) response.status = status if data is not None: response.data = json.dumps(data).encode('utf-8') request = mock.create_autospec(transport.Request) request.return_value = response return request def make_credentials(): class CredentialsImpl(google.auth.credentials.Credentials): def __init__(self): super(CredentialsImpl, self).__init__() self.token = 'token' # Force refresh self.expiry = datetime.datetime.min + _helpers.CLOCK_SKEW def refresh(self, request): pass return CredentialsImpl() class TestSigner(object): def test_constructor(self): request = mock.sentinel.request credentials = mock.create_autospec( google.auth.credentials.Credentials, instance=True) signer = iam.Signer( request, credentials, mock.sentinel.service_account_email) assert signer._request == mock.sentinel.request assert signer._credentials == credentials assert (signer._service_account_email == mock.sentinel.service_account_email) def test_key_id(self): signer = iam.Signer( mock.sentinel.request, mock.sentinel.credentials, mock.sentinel.service_account_email) assert signer.key_id is None def test_sign_bytes(self): signature = b'DEADBEEF' encoded_signature = base64.b64encode(signature).decode('utf-8') request = make_request( http_client.OK, data={'signature': encoded_signature}) credentials = make_credentials() signer = iam.Signer( request, credentials, mock.sentinel.service_account_email) returned_signature = signer.sign('123') assert returned_signature == signature def test_sign_bytes_failure(self): request = make_request(http_client.UNAUTHORIZED) credentials = make_credentials() signer = iam.Signer( request, credentials, mock.sentinel.service_account_email) with pytest.raises(exceptions.TransportError): signer.sign('123') google-auth-1.5.1/tests/test_credentials.py0000644110576702575230000001210213277071027024141 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import pytest from google.auth import _helpers from google.auth import credentials class CredentialsImpl(credentials.Credentials): def refresh(self, request): self.token = request def test_credentials_constructor(): credentials = CredentialsImpl() assert not credentials.token assert not credentials.expiry assert not credentials.expired assert not credentials.valid def test_expired_and_valid(): credentials = CredentialsImpl() credentials.token = 'token' assert credentials.valid assert not credentials.expired # Set the expiration to one second more than now plus the clock skew # accomodation. These credentials should be valid. credentials.expiry = ( datetime.datetime.utcnow() + _helpers.CLOCK_SKEW + datetime.timedelta(seconds=1)) assert credentials.valid assert not credentials.expired # Set the credentials expiration to now. Because of the clock skew # accomodation, these credentials should report as expired. credentials.expiry = datetime.datetime.utcnow() assert not credentials.valid assert credentials.expired def test_before_request(): credentials = CredentialsImpl() request = 'token' headers = {} # First call should call refresh, setting the token. credentials.before_request(request, 'http://example.com', 'GET', headers) assert credentials.valid assert credentials.token == 'token' assert headers['authorization'] == 'Bearer token' request = 'token2' headers = {} # Second call shouldn't call refresh. credentials.before_request(request, 'http://example.com', 'GET', headers) assert credentials.valid assert credentials.token == 'token' assert headers['authorization'] == 'Bearer token' def test_anonymous_credentials_ctor(): anon = credentials.AnonymousCredentials() assert anon.token is None assert anon.expiry is None assert not anon.expired assert anon.valid def test_anonymous_credentials_refresh(): anon = credentials.AnonymousCredentials() request = object() with pytest.raises(ValueError): anon.refresh(request) def test_anonymous_credentials_apply_default(): anon = credentials.AnonymousCredentials() headers = {} anon.apply(headers) assert headers == {} with pytest.raises(ValueError): anon.apply(headers, token='TOKEN') def test_anonymous_credentials_before_request(): anon = credentials.AnonymousCredentials() request = object() method = 'GET' url = 'https://example.com/api/endpoint' headers = {} anon.before_request(request, method, url, headers) assert headers == {} class ReadOnlyScopedCredentialsImpl( credentials.ReadOnlyScoped, CredentialsImpl): @property def requires_scopes(self): return super(ReadOnlyScopedCredentialsImpl, self).requires_scopes def test_readonly_scoped_credentials_constructor(): credentials = ReadOnlyScopedCredentialsImpl() assert credentials._scopes is None def test_readonly_scoped_credentials_scopes(): credentials = ReadOnlyScopedCredentialsImpl() credentials._scopes = ['one', 'two'] assert credentials.scopes == ['one', 'two'] assert credentials.has_scopes(['one']) assert credentials.has_scopes(['two']) assert credentials.has_scopes(['one', 'two']) assert not credentials.has_scopes(['three']) def test_readonly_scoped_credentials_requires_scopes(): credentials = ReadOnlyScopedCredentialsImpl() assert not credentials.requires_scopes class RequiresScopedCredentialsImpl(credentials.Scoped, CredentialsImpl): def __init__(self, scopes=None): super(RequiresScopedCredentialsImpl, self).__init__() self._scopes = scopes @property def requires_scopes(self): return not self.scopes def with_scopes(self, scopes): return RequiresScopedCredentialsImpl(scopes=scopes) def test_create_scoped_if_required_scoped(): unscoped_credentials = RequiresScopedCredentialsImpl() scoped_credentials = credentials.with_scopes_if_required( unscoped_credentials, ['one', 'two']) assert scoped_credentials is not unscoped_credentials assert not scoped_credentials.requires_scopes assert scoped_credentials.has_scopes(['one', 'two']) def test_create_scoped_if_required_not_scopes(): unscoped_credentials = CredentialsImpl() scoped_credentials = credentials.with_scopes_if_required( unscoped_credentials, ['one', 'two']) assert scoped_credentials is unscoped_credentials google-auth-1.5.1/tests/test_app_engine.py0000644110576702575230000001301113277071027023751 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import mock import pytest from google.auth import app_engine class _AppIdentityModule(object): """The interface of the App Idenity app engine module. See https://cloud.google.com/appengine/docs/standard/python/refdocs /google.appengine.api.app_identity.app_identity """ def get_application_id(self): raise NotImplementedError() def sign_blob(self, bytes_to_sign, deadline=None): raise NotImplementedError() def get_service_account_name(self, deadline=None): raise NotImplementedError() def get_access_token(self, scopes, service_account_id=None): raise NotImplementedError() @pytest.fixture def app_identity(monkeypatch): """Mocks the app_identity module for google.auth.app_engine.""" app_identity_module = mock.create_autospec( _AppIdentityModule, instance=True) monkeypatch.setattr( app_engine, 'app_identity', app_identity_module) yield app_identity_module def test_get_project_id(app_identity): app_identity.get_application_id.return_value = mock.sentinel.project assert app_engine.get_project_id() == mock.sentinel.project def test_get_project_id_missing_apis(): with pytest.raises(EnvironmentError) as excinfo: assert app_engine.get_project_id() assert excinfo.match(r'App Engine APIs are not available') class TestSigner(object): def test_key_id(self, app_identity): app_identity.sign_blob.return_value = ( mock.sentinel.key_id, mock.sentinel.signature) signer = app_engine.Signer() assert signer.key_id is None def test_sign(self, app_identity): app_identity.sign_blob.return_value = ( mock.sentinel.key_id, mock.sentinel.signature) signer = app_engine.Signer() to_sign = b'123' signature = signer.sign(to_sign) assert signature == mock.sentinel.signature app_identity.sign_blob.assert_called_with(to_sign) class TestCredentials(object): def test_missing_apis(self): with pytest.raises(EnvironmentError) as excinfo: app_engine.Credentials() assert excinfo.match(r'App Engine APIs are not available') def test_default_state(self, app_identity): credentials = app_engine.Credentials() # Not token acquired yet assert not credentials.valid # Expiration hasn't been set yet assert not credentials.expired # Scopes are required assert not credentials.scopes assert credentials.requires_scopes def test_with_scopes(self, app_identity): credentials = app_engine.Credentials() assert not credentials.scopes assert credentials.requires_scopes scoped_credentials = credentials.with_scopes(['email']) assert scoped_credentials.has_scopes(['email']) assert not scoped_credentials.requires_scopes def test_service_account_email_implicit(self, app_identity): app_identity.get_service_account_name.return_value = ( mock.sentinel.service_account_email) credentials = app_engine.Credentials() assert (credentials.service_account_email == mock.sentinel.service_account_email) assert app_identity.get_service_account_name.called def test_service_account_email_explicit(self, app_identity): credentials = app_engine.Credentials( service_account_id=mock.sentinel.service_account_email) assert (credentials.service_account_email == mock.sentinel.service_account_email) assert not app_identity.get_service_account_name.called @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.min) def test_refresh(self, utcnow, app_identity): token = 'token' ttl = 643942923 app_identity.get_access_token.return_value = token, ttl credentials = app_engine.Credentials(scopes=['email']) credentials.refresh(None) app_identity.get_access_token.assert_called_with( credentials.scopes, credentials._service_account_id) assert credentials.token == token assert credentials.expiry == datetime.datetime( 1990, 5, 29, 1, 2, 3) assert credentials.valid assert not credentials.expired def test_sign_bytes(self, app_identity): app_identity.sign_blob.return_value = ( mock.sentinel.key_id, mock.sentinel.signature) credentials = app_engine.Credentials() to_sign = b'123' signature = credentials.sign_bytes(to_sign) assert signature == mock.sentinel.signature app_identity.sign_blob.assert_called_with(to_sign) def test_signer(self, app_identity): credentials = app_engine.Credentials() assert isinstance(credentials.signer, app_engine.Signer) def test_signer_email(self, app_identity): credentials = app_engine.Credentials() assert credentials.signer_email == credentials.service_account_email google-auth-1.5.1/tests/test__oauth2client.py0000644110576702575230000001250013277071027024406 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import os import sys import mock import oauth2client.client import oauth2client.contrib.gce import oauth2client.service_account import pytest from six.moves import reload_module from google.auth import _oauth2client DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') def test__convert_oauth2_credentials(): old_credentials = oauth2client.client.OAuth2Credentials( 'access_token', 'client_id', 'client_secret', 'refresh_token', datetime.datetime.min, 'token_uri', 'user_agent', scopes='one two') new_credentials = _oauth2client._convert_oauth2_credentials( old_credentials) assert new_credentials.token == old_credentials.access_token assert new_credentials._refresh_token == old_credentials.refresh_token assert new_credentials._client_id == old_credentials.client_id assert new_credentials._client_secret == old_credentials.client_secret assert new_credentials._token_uri == old_credentials.token_uri assert new_credentials.scopes == old_credentials.scopes def test__convert_service_account_credentials(): old_class = oauth2client.service_account.ServiceAccountCredentials old_credentials = old_class.from_json_keyfile_name( SERVICE_ACCOUNT_JSON_FILE) new_credentials = _oauth2client._convert_service_account_credentials( old_credentials) assert (new_credentials.service_account_email == old_credentials.service_account_email) assert new_credentials._signer.key_id == old_credentials._private_key_id assert new_credentials._token_uri == old_credentials.token_uri def test__convert_service_account_credentials_with_jwt(): old_class = oauth2client.service_account._JWTAccessCredentials old_credentials = old_class.from_json_keyfile_name( SERVICE_ACCOUNT_JSON_FILE) new_credentials = _oauth2client._convert_service_account_credentials( old_credentials) assert (new_credentials.service_account_email == old_credentials.service_account_email) assert new_credentials._signer.key_id == old_credentials._private_key_id assert new_credentials._token_uri == old_credentials.token_uri def test__convert_gce_app_assertion_credentials(): old_credentials = oauth2client.contrib.gce.AppAssertionCredentials( email='some_email') new_credentials = _oauth2client._convert_gce_app_assertion_credentials( old_credentials) assert (new_credentials.service_account_email == old_credentials.service_account_email) @pytest.fixture def mock_oauth2client_gae_imports(mock_non_existent_module): mock_non_existent_module('google.appengine.api.app_identity') mock_non_existent_module('google.appengine.ext.ndb') mock_non_existent_module('google.appengine.ext.webapp.util') mock_non_existent_module('webapp2') @mock.patch('google.auth.app_engine.app_identity') def test__convert_appengine_app_assertion_credentials( app_identity, mock_oauth2client_gae_imports): import oauth2client.contrib.appengine service_account_id = 'service_account_id' old_credentials = oauth2client.contrib.appengine.AppAssertionCredentials( scope='one two', service_account_id=service_account_id) new_credentials = ( _oauth2client._convert_appengine_app_assertion_credentials( old_credentials)) assert new_credentials.scopes == ['one', 'two'] assert (new_credentials._service_account_id == old_credentials.service_account_id) class FakeCredentials(object): pass def test_convert_success(): convert_function = mock.Mock(spec=['__call__']) conversion_map_patch = mock.patch.object( _oauth2client, '_CLASS_CONVERSION_MAP', {FakeCredentials: convert_function}) credentials = FakeCredentials() with conversion_map_patch: result = _oauth2client.convert(credentials) convert_function.assert_called_once_with(credentials) assert result == convert_function.return_value def test_convert_not_found(): with pytest.raises(ValueError) as excinfo: _oauth2client.convert('a string is not a real credentials class') assert excinfo.match('Unable to convert') @pytest.fixture def reset__oauth2client_module(): """Reloads the _oauth2client module after a test.""" reload_module(_oauth2client) def test_import_has_app_engine( mock_oauth2client_gae_imports, reset__oauth2client_module): reload_module(_oauth2client) assert _oauth2client._HAS_APPENGINE def test_import_without_oauth2client(monkeypatch, reset__oauth2client_module): monkeypatch.setitem(sys.modules, 'oauth2client', None) with pytest.raises(ImportError) as excinfo: reload_module(_oauth2client) assert excinfo.match('oauth2client') google-auth-1.5.1/tests/test_jwt.py0000644110576702575230000004157513330162371022461 0ustar theaflowersprimarygroup00000000000000# Copyright 2014 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. import base64 import datetime import json import os import mock import pytest from google.auth import _helpers from google.auth import crypt from google.auth import exceptions from google.auth import jwt DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: PRIVATE_KEY_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: PUBLIC_CERT_BYTES = fh.read() with open(os.path.join(DATA_DIR, 'other_cert.pem'), 'rb') as fh: OTHER_CERT_BYTES = fh.read() SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: SERVICE_ACCOUNT_INFO = json.load(fh) @pytest.fixture def signer(): return crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1') def test_encode_basic(signer): test_payload = {'test': 'value'} encoded = jwt.encode(signer, test_payload) header, payload, _, _ = jwt._unverified_decode(encoded) assert payload == test_payload assert header == {'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id} def test_encode_extra_headers(signer): encoded = jwt.encode(signer, {}, header={'extra': 'value'}) header = jwt.decode_header(encoded) assert header == { 'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id, 'extra': 'value'} @pytest.fixture def token_factory(signer): def factory(claims=None, key_id=None): now = _helpers.datetime_to_secs(_helpers.utcnow()) payload = { 'aud': 'audience@example.com', 'iat': now, 'exp': now + 300, 'user': 'billy bob', 'metadata': {'meta': 'data'} } payload.update(claims or {}) # False is specified to remove the signer's key id for testing # headers without key ids. if key_id is False: signer._key_id = None key_id = None return jwt.encode(signer, payload, key_id=key_id) return factory def test_decode_valid(token_factory): payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES) assert payload['aud'] == 'audience@example.com' assert payload['user'] == 'billy bob' assert payload['metadata']['meta'] == 'data' def test_decode_valid_with_audience(token_factory): payload = jwt.decode( token_factory(), certs=PUBLIC_CERT_BYTES, audience='audience@example.com') assert payload['aud'] == 'audience@example.com' assert payload['user'] == 'billy bob' assert payload['metadata']['meta'] == 'data' def test_decode_valid_unverified(token_factory): payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False) assert payload['aud'] == 'audience@example.com' assert payload['user'] == 'billy bob' assert payload['metadata']['meta'] == 'data' def test_decode_bad_token_wrong_number_of_segments(): with pytest.raises(ValueError) as excinfo: jwt.decode('1.2', PUBLIC_CERT_BYTES) assert excinfo.match(r'Wrong number of segments') def test_decode_bad_token_not_base64(): with pytest.raises((ValueError, TypeError)) as excinfo: jwt.decode('1.2.3', PUBLIC_CERT_BYTES) assert excinfo.match(r'Incorrect padding|more than a multiple of 4') def test_decode_bad_token_not_json(): token = b'.'.join([base64.urlsafe_b64encode(b'123!')] * 3) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) assert excinfo.match(r'Can\'t parse segment') def test_decode_bad_token_no_iat_or_exp(signer): token = jwt.encode(signer, {'test': 'value'}) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) assert excinfo.match(r'Token does not contain required claim') def test_decode_bad_token_too_early(token_factory): token = token_factory(claims={ 'iat': _helpers.datetime_to_secs( _helpers.utcnow() + datetime.timedelta(hours=1)) }) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) assert excinfo.match(r'Token used too early') def test_decode_bad_token_expired(token_factory): token = token_factory(claims={ 'exp': _helpers.datetime_to_secs( _helpers.utcnow() - datetime.timedelta(hours=1)) }) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) assert excinfo.match(r'Token expired') def test_decode_bad_token_wrong_audience(token_factory): token = token_factory() audience = 'audience2@example.com' with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience) assert excinfo.match(r'Token has wrong audience') def test_decode_wrong_cert(token_factory): with pytest.raises(ValueError) as excinfo: jwt.decode(token_factory(), OTHER_CERT_BYTES) assert excinfo.match(r'Could not verify token signature') def test_decode_multicert_bad_cert(token_factory): certs = {'1': OTHER_CERT_BYTES, '2': PUBLIC_CERT_BYTES} with pytest.raises(ValueError) as excinfo: jwt.decode(token_factory(), certs) assert excinfo.match(r'Could not verify token signature') def test_decode_no_cert(token_factory): certs = {'2': PUBLIC_CERT_BYTES} with pytest.raises(ValueError) as excinfo: jwt.decode(token_factory(), certs) assert excinfo.match(r'Certificate for key id 1 not found') def test_decode_no_key_id(token_factory): token = token_factory(key_id=False) certs = {'2': PUBLIC_CERT_BYTES} payload = jwt.decode(token, certs) assert payload['user'] == 'billy bob' def test_roundtrip_explicit_key_id(token_factory): token = token_factory(key_id='3') certs = {'2': OTHER_CERT_BYTES, '3': PUBLIC_CERT_BYTES} payload = jwt.decode(token, certs) assert payload['user'] == 'billy bob' class TestCredentials(object): SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' SUBJECT = 'subject' AUDIENCE = 'audience' ADDITIONAL_CLAIMS = {'meta': 'data'} credentials = None @pytest.fixture(autouse=True) def credentials_fixture(self, signer): self.credentials = jwt.Credentials( signer, self.SERVICE_ACCOUNT_EMAIL, self.SERVICE_ACCOUNT_EMAIL, self.AUDIENCE) def test_from_service_account_info(self): with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: info = json.load(fh) credentials = jwt.Credentials.from_service_account_info( info, audience=self.AUDIENCE) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == info['client_email'] assert credentials._audience == self.AUDIENCE def test_from_service_account_info_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.Credentials.from_service_account_info( info, subject=self.SUBJECT, audience=self.AUDIENCE, additional_claims=self.ADDITIONAL_CLAIMS) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == self.SUBJECT assert credentials._audience == self.AUDIENCE assert credentials._additional_claims == self.ADDITIONAL_CLAIMS def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.Credentials.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE, audience=self.AUDIENCE) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == info['client_email'] assert credentials._audience == self.AUDIENCE def test_from_service_account_file_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.Credentials.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE, subject=self.SUBJECT, audience=self.AUDIENCE, additional_claims=self.ADDITIONAL_CLAIMS) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == self.SUBJECT assert credentials._audience == self.AUDIENCE assert credentials._additional_claims == self.ADDITIONAL_CLAIMS def test_from_signing_credentials(self): jwt_from_signing = self.credentials.from_signing_credentials( self.credentials, audience=mock.sentinel.new_audience) jwt_from_info = jwt.Credentials.from_service_account_info( SERVICE_ACCOUNT_INFO, audience=mock.sentinel.new_audience) assert isinstance(jwt_from_signing, jwt.Credentials) assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id assert jwt_from_signing._issuer == jwt_from_info._issuer assert jwt_from_signing._subject == jwt_from_info._subject assert jwt_from_signing._audience == jwt_from_info._audience def test_default_state(self): assert not self.credentials.valid # Expiration hasn't been set yet assert not self.credentials.expired def test_with_claims(self): new_audience = 'new_audience' new_credentials = self.credentials.with_claims( audience=new_audience) assert new_credentials._signer == self.credentials._signer assert new_credentials._issuer == self.credentials._issuer assert new_credentials._subject == self.credentials._subject assert new_credentials._audience == new_audience assert (new_credentials._additional_claims == self.credentials._additional_claims) def test_sign_bytes(self): to_sign = b'123' signature = self.credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) def test_signer(self): assert isinstance(self.credentials.signer, crypt.RSASigner) def test_signer_email(self): assert (self.credentials.signer_email == SERVICE_ACCOUNT_INFO['client_email']) def _verify_token(self, token): payload = jwt.decode(token, PUBLIC_CERT_BYTES) assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL return payload def test_refresh(self): self.credentials.refresh(None) assert self.credentials.valid assert not self.credentials.expired def test_expired(self): assert not self.credentials.expired self.credentials.refresh(None) assert not self.credentials.expired with mock.patch('google.auth._helpers.utcnow') as now: one_day = datetime.timedelta(days=1) now.return_value = self.credentials.expiry + one_day assert self.credentials.expired def test_before_request(self): headers = {} self.credentials.refresh(None) self.credentials.before_request( None, 'GET', 'http://example.com?a=1#3', headers) header_value = headers['authorization'] _, token = header_value.split(' ') # Since the audience is set, it should use the existing token. assert token.encode('utf-8') == self.credentials.token payload = self._verify_token(token) assert payload['aud'] == self.AUDIENCE def test_before_request_refreshes(self): assert not self.credentials.valid self.credentials.before_request( None, 'GET', 'http://example.com?a=1#3', {}) assert self.credentials.valid class TestOnDemandCredentials(object): SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' SUBJECT = 'subject' ADDITIONAL_CLAIMS = {'meta': 'data'} credentials = None @pytest.fixture(autouse=True) def credentials_fixture(self, signer): self.credentials = jwt.OnDemandCredentials( signer, self.SERVICE_ACCOUNT_EMAIL, self.SERVICE_ACCOUNT_EMAIL, max_cache_size=2) def test_from_service_account_info(self): with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: info = json.load(fh) credentials = jwt.OnDemandCredentials.from_service_account_info(info) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == info['client_email'] def test_from_service_account_info_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.OnDemandCredentials.from_service_account_info( info, subject=self.SUBJECT, additional_claims=self.ADDITIONAL_CLAIMS) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == self.SUBJECT assert credentials._additional_claims == self.ADDITIONAL_CLAIMS def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.OnDemandCredentials.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == info['client_email'] def test_from_service_account_file_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.OnDemandCredentials.from_service_account_file( SERVICE_ACCOUNT_JSON_FILE, subject=self.SUBJECT, additional_claims=self.ADDITIONAL_CLAIMS) assert credentials._signer.key_id == info['private_key_id'] assert credentials._issuer == info['client_email'] assert credentials._subject == self.SUBJECT assert credentials._additional_claims == self.ADDITIONAL_CLAIMS def test_from_signing_credentials(self): jwt_from_signing = self.credentials.from_signing_credentials( self.credentials) jwt_from_info = jwt.OnDemandCredentials.from_service_account_info( SERVICE_ACCOUNT_INFO) assert isinstance(jwt_from_signing, jwt.OnDemandCredentials) assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id assert jwt_from_signing._issuer == jwt_from_info._issuer assert jwt_from_signing._subject == jwt_from_info._subject def test_default_state(self): # Credentials are *always* valid. assert self.credentials.valid # Credentials *never* expire. assert not self.credentials.expired def test_with_claims(self): new_claims = {'meep': 'moop'} new_credentials = self.credentials.with_claims( additional_claims=new_claims) assert new_credentials._signer == self.credentials._signer assert new_credentials._issuer == self.credentials._issuer assert new_credentials._subject == self.credentials._subject assert new_credentials._additional_claims == new_claims def test_sign_bytes(self): to_sign = b'123' signature = self.credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) def test_signer(self): assert isinstance(self.credentials.signer, crypt.RSASigner) def test_signer_email(self): assert (self.credentials.signer_email == SERVICE_ACCOUNT_INFO['client_email']) def _verify_token(self, token): payload = jwt.decode(token, PUBLIC_CERT_BYTES) assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL return payload def test_refresh(self): with pytest.raises(exceptions.RefreshError): self.credentials.refresh(None) def test_before_request(self): headers = {} self.credentials.before_request( None, 'GET', 'http://example.com?a=1#3', headers) _, token = headers['authorization'].split(' ') payload = self._verify_token(token) assert payload['aud'] == 'http://example.com' # Making another request should re-use the same token. self.credentials.before_request( None, 'GET', 'http://example.com?b=2', headers) _, new_token = headers['authorization'].split(' ') assert new_token == token def test_expired_token(self): self.credentials._cache['audience'] = ( mock.sentinel.token, datetime.datetime.min) token = self.credentials._get_jwt_for_audience('audience') assert token != mock.sentinel.token google-auth-1.5.1/tests/test__helpers.py0000644110576702575230000001036413277071027023455 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import pytest from six.moves import urllib from google.auth import _helpers class SourceClass(object): def func(self): # pragma: NO COVER """example docstring""" def test_copy_docstring_success(): def func(): # pragma: NO COVER pass _helpers.copy_docstring(SourceClass)(func) assert func.__doc__ == SourceClass.func.__doc__ def test_copy_docstring_conflict(): def func(): # pragma: NO COVER """existing docstring""" pass with pytest.raises(ValueError): _helpers.copy_docstring(SourceClass)(func) def test_copy_docstring_non_existing(): def func2(): # pragma: NO COVER pass with pytest.raises(AttributeError): _helpers.copy_docstring(SourceClass)(func2) def test_utcnow(): assert isinstance(_helpers.utcnow(), datetime.datetime) def test_datetime_to_secs(): assert _helpers.datetime_to_secs( datetime.datetime(1970, 1, 1)) == 0 assert _helpers.datetime_to_secs( datetime.datetime(1990, 5, 29)) == 643939200 def test_to_bytes_with_bytes(): value = b'bytes-val' assert _helpers.to_bytes(value) == value def test_to_bytes_with_unicode(): value = u'string-val' encoded_value = b'string-val' assert _helpers.to_bytes(value) == encoded_value def test_to_bytes_with_nonstring_type(): with pytest.raises(ValueError): _helpers.to_bytes(object()) def test_from_bytes_with_unicode(): value = u'bytes-val' assert _helpers.from_bytes(value) == value def test_from_bytes_with_bytes(): value = b'string-val' decoded_value = u'string-val' assert _helpers.from_bytes(value) == decoded_value def test_from_bytes_with_nonstring_type(): with pytest.raises(ValueError): _helpers.from_bytes(object()) def _assert_query(url, expected): parts = urllib.parse.urlsplit(url) query = urllib.parse.parse_qs(parts.query) assert query == expected def test_update_query_params_no_params(): uri = 'http://www.google.com' updated = _helpers.update_query(uri, {'a': 'b'}) assert updated == uri + '?a=b' def test_update_query_existing_params(): uri = 'http://www.google.com?x=y' updated = _helpers.update_query(uri, {'a': 'b', 'c': 'd&'}) _assert_query(updated, {'x': ['y'], 'a': ['b'], 'c': ['d&']}) def test_update_query_replace_param(): base_uri = 'http://www.google.com' uri = base_uri + '?x=a' updated = _helpers.update_query(uri, {'x': 'b', 'y': 'c'}) _assert_query(updated, {'x': ['b'], 'y': ['c']}) def test_update_query_remove_param(): base_uri = 'http://www.google.com' uri = base_uri + '?x=a' updated = _helpers.update_query(uri, {'y': 'c'}, remove=['x']) _assert_query(updated, {'y': ['c']}) def test_scopes_to_string(): cases = [ ('', ()), ('', []), ('', ('',)), ('', ['', ]), ('a', ('a',)), ('b', ['b', ]), ('a b', ['a', 'b']), ('a b', ('a', 'b')), ('a b', (s for s in ['a', 'b'])), ] for expected, case in cases: assert _helpers.scopes_to_string(case) == expected def test_string_to_scopes(): cases = [ ('', []), ('a', ['a']), ('a b c d e f', ['a', 'b', 'c', 'd', 'e', 'f']), ] for case, expected in cases: assert _helpers.string_to_scopes(case) == expected def test_padded_urlsafe_b64decode(): cases = [ ('YQ==', b'a'), ('YQ', b'a'), ('YWE=', b'aa'), ('YWE', b'aa'), ('YWFhYQ==', b'aaaa'), ('YWFhYQ', b'aaaa'), ('YWFhYWE=', b'aaaaa'), ('YWFhYWE', b'aaaaa'), ] for case, expected in cases: assert _helpers.padded_urlsafe_b64decode(case) == expected google-auth-1.5.1/tests/test__default.py0000644110576702575230000003100513304067320023422 0ustar theaflowersprimarygroup00000000000000# 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. import json import os import mock import pytest from google.auth import _default from google.auth import app_engine from google.auth import compute_engine from google.auth import environment_vars from google.auth import exceptions from google.oauth2 import service_account import google.oauth2.credentials DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, 'authorized_user.json') with open(AUTHORIZED_USER_FILE) as fh: AUTHORIZED_USER_FILE_DATA = json.load(fh) AUTHORIZED_USER_CLOUD_SDK_FILE = os.path.join( DATA_DIR, 'authorized_user_cloud_sdk.json') SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, 'service_account.json') with open(SERVICE_ACCOUNT_FILE) as fh: SERVICE_ACCOUNT_FILE_DATA = json.load(fh) LOAD_FILE_PATCH = mock.patch( 'google.auth._default._load_credentials_from_file', return_value=( mock.sentinel.credentials, mock.sentinel.project_id), autospec=True) def test__load_credentials_from_missing_file(): with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file('') assert excinfo.match(r'not found') def test__load_credentials_from_file_invalid_json(tmpdir): jsonfile = tmpdir.join('invalid.json') jsonfile.write('{') with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(jsonfile)) assert excinfo.match(r'not a valid json file') def test__load_credentials_from_file_invalid_type(tmpdir): jsonfile = tmpdir.join('invalid.json') jsonfile.write(json.dumps({'type': 'not-a-real-type'})) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(jsonfile)) assert excinfo.match(r'does not have a valid type') def test__load_credentials_from_file_authorized_user(): credentials, project_id = _default._load_credentials_from_file( AUTHORIZED_USER_FILE) assert isinstance(credentials, google.oauth2.credentials.Credentials) assert project_id is None def test__load_credentials_from_file_authorized_user_bad_format(tmpdir): filename = tmpdir.join('authorized_user_bad.json') filename.write(json.dumps({'type': 'authorized_user'})) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(filename)) assert excinfo.match(r'Failed to load authorized user') assert excinfo.match(r'missing fields') def test__load_credentials_from_file_authorized_user_cloud_sdk(): with pytest.warns(UserWarning, matches='Cloud SDK'): credentials, project_id = _default._load_credentials_from_file( AUTHORIZED_USER_CLOUD_SDK_FILE) assert isinstance(credentials, google.oauth2.credentials.Credentials) assert project_id is None def test__load_credentials_from_file_service_account(): credentials, project_id = _default._load_credentials_from_file( SERVICE_ACCOUNT_FILE) assert isinstance(credentials, service_account.Credentials) assert project_id == SERVICE_ACCOUNT_FILE_DATA['project_id'] def test__load_credentials_from_file_service_account_bad_format(tmpdir): filename = tmpdir.join('serivce_account_bad.json') filename.write(json.dumps({'type': 'service_account'})) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(filename)) assert excinfo.match(r'Failed to load service account') assert excinfo.match(r'missing fields') @mock.patch.dict(os.environ, {}, clear=True) def test__get_explicit_environ_credentials_no_env(): assert _default._get_explicit_environ_credentials() == (None, None) @LOAD_FILE_PATCH def test__get_explicit_environ_credentials(load, monkeypatch): monkeypatch.setenv(environment_vars.CREDENTIALS, 'filename') credentials, project_id = _default._get_explicit_environ_credentials() assert credentials is mock.sentinel.credentials assert project_id is mock.sentinel.project_id load.assert_called_with('filename') @LOAD_FILE_PATCH def test__get_explicit_environ_credentials_no_project_id(load, monkeypatch): load.return_value = mock.sentinel.credentials, None monkeypatch.setenv(environment_vars.CREDENTIALS, 'filename') credentials, project_id = _default._get_explicit_environ_credentials() assert credentials is mock.sentinel.credentials assert project_id is None @LOAD_FILE_PATCH @mock.patch( 'google.auth._cloud_sdk.get_application_default_credentials_path', autospec=True) def test__get_gcloud_sdk_credentials(get_adc_path, load): get_adc_path.return_value = SERVICE_ACCOUNT_FILE credentials, project_id = _default._get_gcloud_sdk_credentials() assert credentials is mock.sentinel.credentials assert project_id is mock.sentinel.project_id load.assert_called_with(SERVICE_ACCOUNT_FILE) @mock.patch( 'google.auth._cloud_sdk.get_application_default_credentials_path', autospec=True) def test__get_gcloud_sdk_credentials_non_existent(get_adc_path, tmpdir): non_existent = tmpdir.join('non-existent') get_adc_path.return_value = str(non_existent) credentials, project_id = _default._get_gcloud_sdk_credentials() assert credentials is None assert project_id is None @mock.patch( 'google.auth._cloud_sdk.get_project_id', return_value=mock.sentinel.project_id, autospec=True) @mock.patch('os.path.isfile', return_value=True, autospec=True) @LOAD_FILE_PATCH def test__get_gcloud_sdk_credentials_project_id( load, unused_isfile, get_project_id): # Don't return a project ID from load file, make the function check # the Cloud SDK project. load.return_value = mock.sentinel.credentials, None credentials, project_id = _default._get_gcloud_sdk_credentials() assert credentials == mock.sentinel.credentials assert project_id == mock.sentinel.project_id assert get_project_id.called @mock.patch( 'google.auth._cloud_sdk.get_project_id', return_value=None, autospec=True) @mock.patch('os.path.isfile', return_value=True) @LOAD_FILE_PATCH def test__get_gcloud_sdk_credentials_no_project_id( load, unused_isfile, get_project_id): # Don't return a project ID from load file, make the function check # the Cloud SDK project. load.return_value = mock.sentinel.credentials, None credentials, project_id = _default._get_gcloud_sdk_credentials() assert credentials == mock.sentinel.credentials assert project_id is None assert get_project_id.called class _AppIdentityModule(object): """The interface of the App Idenity app engine module. See https://cloud.google.com/appengine/docs/standard/python/refdocs\ /google.appengine.api.app_identity.app_identity """ def get_application_id(self): raise NotImplementedError() @pytest.fixture def app_identity(monkeypatch): """Mocks the app_identity module for google.auth.app_engine.""" app_identity_module = mock.create_autospec( _AppIdentityModule, instance=True) monkeypatch.setattr( app_engine, 'app_identity', app_identity_module) yield app_identity_module def test__get_gae_credentials(app_identity): app_identity.get_application_id.return_value = mock.sentinel.project credentials, project_id = _default._get_gae_credentials() assert isinstance(credentials, app_engine.Credentials) assert project_id == mock.sentinel.project def test__get_gae_credentials_no_apis(): assert _default._get_gae_credentials() == (None, None) @mock.patch( 'google.auth.compute_engine._metadata.ping', return_value=True, autospec=True) @mock.patch( 'google.auth.compute_engine._metadata.get_project_id', return_value='example-project', autospec=True) def test__get_gce_credentials(unused_get, unused_ping): credentials, project_id = _default._get_gce_credentials() assert isinstance(credentials, compute_engine.Credentials) assert project_id == 'example-project' @mock.patch( 'google.auth.compute_engine._metadata.ping', return_value=False, autospec=True) def test__get_gce_credentials_no_ping(unused_ping): credentials, project_id = _default._get_gce_credentials() assert credentials is None assert project_id is None @mock.patch( 'google.auth.compute_engine._metadata.ping', return_value=True, autospec=True) @mock.patch( 'google.auth.compute_engine._metadata.get_project_id', side_effect=exceptions.TransportError(), autospec=True) def test__get_gce_credentials_no_project_id(unused_get, unused_ping): credentials, project_id = _default._get_gce_credentials() assert isinstance(credentials, compute_engine.Credentials) assert project_id is None @mock.patch( 'google.auth.compute_engine._metadata.ping', return_value=False, autospec=True) def test__get_gce_credentials_explicit_request(ping): _default._get_gce_credentials(mock.sentinel.request) ping.assert_called_with(request=mock.sentinel.request) @mock.patch( 'google.auth._default._get_explicit_environ_credentials', return_value=(mock.sentinel.credentials, mock.sentinel.project_id), autospec=True) def test_default_early_out(unused_get): assert _default.default() == ( mock.sentinel.credentials, mock.sentinel.project_id) @mock.patch( 'google.auth._default._get_explicit_environ_credentials', return_value=(mock.sentinel.credentials, mock.sentinel.project_id), autospec=True) def test_default_explict_project_id(unused_get, monkeypatch): monkeypatch.setenv(environment_vars.PROJECT, 'explicit-env') assert _default.default() == ( mock.sentinel.credentials, 'explicit-env') @mock.patch( 'google.auth._default._get_explicit_environ_credentials', return_value=(mock.sentinel.credentials, mock.sentinel.project_id), autospec=True) def test_default_explict_legacy_project_id(unused_get, monkeypatch): monkeypatch.setenv(environment_vars.LEGACY_PROJECT, 'explicit-env') assert _default.default() == ( mock.sentinel.credentials, 'explicit-env') @mock.patch( 'logging.Logger.warning', autospec=True) @mock.patch( 'google.auth._default._get_explicit_environ_credentials', return_value=(mock.sentinel.credentials, None), autospec=True) @mock.patch( 'google.auth._default._get_gcloud_sdk_credentials', return_value=(mock.sentinel.credentials, None), autospec=True) @mock.patch( 'google.auth._default._get_gae_credentials', return_value=(mock.sentinel.credentials, None), autospec=True) @mock.patch( 'google.auth._default._get_gce_credentials', return_value=(mock.sentinel.credentials, None), autospec=True) def test_default_without_project_id( unused_gce, unused_gae, unused_sdk, unused_explicit, logger_warning): assert _default.default() == ( mock.sentinel.credentials, None) logger_warning.assert_called_with(mock.ANY, mock.ANY, mock.ANY) @mock.patch( 'google.auth._default._get_explicit_environ_credentials', return_value=(None, None), autospec=True) @mock.patch( 'google.auth._default._get_gcloud_sdk_credentials', return_value=(None, None), autospec=True) @mock.patch( 'google.auth._default._get_gae_credentials', return_value=(None, None), autospec=True) @mock.patch( 'google.auth._default._get_gce_credentials', return_value=(None, None), autospec=True) def test_default_fail(unused_gce, unused_gae, unused_sdk, unused_explicit): with pytest.raises(exceptions.DefaultCredentialsError): assert _default.default() @mock.patch( 'google.auth._default._get_explicit_environ_credentials', return_value=(mock.sentinel.credentials, mock.sentinel.project_id), autospec=True) @mock.patch( 'google.auth.credentials.with_scopes_if_required', autospec=True) def test_default_scoped(with_scopes, unused_get): scopes = ['one', 'two'] credentials, project_id = _default.default(scopes=scopes) assert credentials == with_scopes.return_value assert project_id == mock.sentinel.project_id with_scopes.assert_called_once_with( mock.sentinel.credentials, scopes) google-auth-1.5.1/tests/data/0000755110576702575230000000000013330167570021147 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/data/other_cert.pem0000644110576702575230000000377113277071027024021 0ustar theaflowersprimarygroup00000000000000-----BEGIN CERTIFICATE----- MIIFtTCCA52gAwIBAgIJAPBsLZmNGfKtMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMTYwOTIxMDI0NTEyWhcNMTYxMDIxMDI0NTEyWjBF MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC CgKCAgEAsiMC7mTsmUXwZoYlT4aHY1FLw8bxIXC+z3IqA+TY1WqfbeiZRo8MA5Zx lTTxYMKPCZUE1XBc7jvD8GJhWIj6pToPYHn73B01IBkLBxq4kF1yV2Z7DVmkvc6H EcxXXq8zkCx0j6XOfiI4+qkXnuQn8cvrk8xfhtnMMZM7iVm6VSN93iRP/8ey6xuL XTHrDX7ukoRce1hpT8O+15GXNrY0irhhYQz5xKibNCJF3EjV28WMry8y7I8uYUFU RWDiQawwK9ec1zhZ94v92+GZDlPevmcFmSERKYQ0NsKcT0Y3lGuGnaExs8GyOpnC oksu4YJGXQjg7lkv4MxzsNbRqmCkUwxw1Mg6FP0tsCNsw9qTrkvWCRA9zp/aU+sZ IBGh1t4UGCub8joeQFvHxvr/3F7mH/dyvCjA34u0Lo1VPx+jYUIi9i0odltMspDW xOpjqdGARZYmlJP5Au9q5cQjPMcwS/EBIb8cwNl32mUE6WnFlep+38mNR/FghIjO ViAkXuKQmcHe6xppZAoHFsO/t3l4Tjek5vNW7erI1rgrFku/fvkIW/G8V1yIm/+Q F+CE4maQzCJfhftpkhM/sPC/FuLNBmNE8BHVX8y58xG4is/cQxL4Z9TsFIw0C5+3 uTrFW9D0agysahMVzPGtCqhDQqJdIJrBQqlS6bztpzBA8zEI0skCAwEAAaOBpzCB pDAdBgNVHQ4EFgQUz/8FmW6TfqXyNJZr7rhc+Tn5sKQwdQYDVR0jBG4wbIAUz/8F mW6TfqXyNJZr7rhc+Tn5sKShSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDw bC2ZjRnyrTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQCQmrcfhurX riR3Q0Y+nq040/3dJIAJXjyI9CEtxaU0nzCNTng7PwgZ0CKmCelQfInuwWFwBSHS 6kBfC1rgJeFnjnTt8a3RCgRlIgUr9NCdPSEccB7TurobwPJ2h6cJjjR8urcb0CXh CEMvPneyPj0xUFY8vVKXMGWahz/kyfwIiVqcX/OtMZ29fUu1onbWl71g2gVLtUZl sECdZ+AC/6HDCVpYIVETMl1T7N/XyqXZQiDLDNRDeZhnapz8w9fsW1KVujAZLNQR pVnw2qa2UK1dSf2FHX+lQU5mFSYM4vtwaMlX/LgfdLZ9I796hFh619WwTVz+LO2N vHnwBMabld3XSPuZRqlbBulDQ07Vbqdjv8DYSLA2aKI4ZkMMKuFLG/oS28V2ZYmv /KpGEs5UgKY+P9NulYpTDwCU/6SomuQpP795wbG6sm7Hzq82r2RmB61GupNRGeqi pXKsy69T388zBxYu6zQrosXiDl5YzaViH7tm0J7opye8dCWjjpnahki0vq2znti7 6cWla2j8Xz1glvLz+JI/NCOMfxUInb82T7ijo80N0VJ2hzf7p2GxRZXAxAV9knLI nM4F5TLjSd7ZhOOZ7ni/eZFueTMisWfypt2nc41whGjHMX/Zp1kPfhB4H2bLKIX/ lSrwNr3qbGTEJX8JqpDBNVAd96XkMvDNyA== -----END CERTIFICATE----- google-auth-1.5.1/tests/data/cloud_sdk_config.json0000644110576702575230000000066113277071027025342 0ustar theaflowersprimarygroup00000000000000{ "configuration": { "active_configuration": "default", "properties": { "core": { "account": "user@example.com", "disable_usage_reporting": "False", "project": "example-project" } } }, "credential": { "access_token": "don't use me", "token_expiry": "2017-03-23T23:09:49Z" }, "sentinels": { "config_sentinel": "/Users/example/.config/gcloud/config_sentinel" } } google-auth-1.5.1/tests/data/privatekey.p120000644110576702575230000000462413277071027023665 0ustar theaflowersprimarygroup000000000000000‚ 0‚ V *†H†÷  ‚ G‚ C0‚ ?0‚ß *†H†÷  ‚Ð0‚Ì0‚Å *†H†÷ 0 *†H†÷  0'ÌdX }µ1€‚˜FÛ+BNq™ ÄbÒ±F¦W_ ,KñŸÞó4þ‚a9âvëtñG¥ü¦9NõÑþ—/Ð*Ð^ßi̬#„ý›È|ìe¥AÞz~~h2aC´ÏxÑšLîÏɨ‡13l "‡(ExÖ)w½_$‚ÑGu›t2Ìycxg–¯‹ÛÔÏ1&:¡Ë Vãþ3f¨~¥Ñì6Ù¯Ó^Ž®iDw”m&®„wj-~Ï„›õÔ쌒âßéË|Øzö‰)SÇÈîO3 Õw(”‡Ïùg…XðV§QÚµ/ÿµÀrlæ8¢wxᦕ'õÅô Ðn¡¾2’Цê´ùŒfÛìYò§´¢>ÿ:üšßØaöwºxÖL #ô‚ä—$ÛѦv–ê5A8½_xȵ×Cô£ˆÁýÝ®Ävr¯o¬g£>HÜøB;"È3O Ëàx]›fXû¸–)@kMI’(‡u,H[šj½ ´¥¡»ÆÏÕ6¹9Ð9úøÐäI\ÍI39M}h’Ü‹ä}ë,µhNz‚h!½Üò/àšP¿îOQž}÷GÑ©à˜[ä`:i•sÙú°ßB#l“›ñv*ÌåÇ(¡—†1ðxX<,äQé;uâˆF¿þþ2Ž€.5˜Ýž/ÄôAQè+T^ÓƒuµÐqý‚¿ˆ„‰,¬=b7…að Ãâ&€ëgRŒ„”/ó‘F"™àá®Êî]äˆ"-–#ž”Ôs ÏHko+âÛ’B)Z…ÉÙ–fæIܱ–ü·9Ü­÷Œ£"="Çv’"#Ɖ•„×qßÑ  åi­m(ûØÙ@Wi³½Š'èAh#9:ì|d_Ô6׸ž©¿É¨joD“Úžÿ€Ó!'ïHHsOá—ƒ½‘”ËÞ×ɆÂâPéxº Ìe&&楰§@¥A²í§ñÜ&G …«ABI0ºr˜yøÈu>âvôˆ2—†»É Û²¼PT/ÜíÇ “kr77?Aü†ˆ–ŒÕ\ª3­_fu“¼U¡?ÔœìwxC- uHOJÄ>Ê‹n§4,çf>Q€úkµ|+>¥ÒIݱëºÜX D7=ܽK)ÌâXþƦ‘I‹¬÷áL0±¹·}Ž£.´JÒÚ5{ ±v‰ k Óˆ§¾‹Ô×C=Z FÌŠ.B Ö÷Q o^cÞ1¤­ÊàŸ0‚X *†H†÷  ‚I‚E0‚A0‚= *†H†÷   ‚î0‚ê0 *†H†÷  0!óP;7¿]ä‚ÈŽ=ó¿¥µû­MVïÐdAv¸îô§ò"ô"¡î¼ÊÍ3‡?ô¸|»ùéúÓc’j5FYŠ“¸Ÿ’›¦µ7e©OùX”ã÷é©“jpPa<½(®³2Ò­«œ4…wþRçÊz¼ Ý/ر²¿Y[«óæWÛ|ë–ÑÔ½¨Ð˜¸'~NÎxÔ=ûá×åºS–qF¨ùï³6(Þb/?x?r?¹sÜŸ|Ù?„Ä t©ÙÝ„¤›íØTv]5wýüŽ-,6ãtxmAˆö´ÉKÈ“¯‚yýHNêܸ—êOˆ¬Å2þ£ü<ÉðÂÖk¥’Rx]ŽÄÖã\Ê\wr$$0•“ÃTÓ[2aÆïe–*V¼Á4Ö¯¤Ðƒ•iBü7„³ÍœÏÆK~%^Z£æ±,Kw‘ ðÊ*¹Ü–Ëãw£˜•Ì.ÍÓÝ›Þ{y[Ä Mä¥éüOVÝsK®â¸›: Å®8ý»pЀjá£Þ§n™ˆ9h.Þs+O!¦+É–õsìÎÞj/Ðáxa/_D–bgýUQŠ"Péž³NpZ¢ñf<'H޶­‡42 É6ã æÁ®ŒôÈ”hp8ži A¾irƒgÌžŠõ~:N–¦Ÿí«ÖlAXË´x)8dº8ž<5» ÚiÇŽÓWÒûÊ‘ G§h³¥Û*ªÆãm!F,êaPßnBm#OÆ„ -"ÝLä æ&›~g–aFiª&ÕC*Úªêe uó#žñ¹)ª£žÀÈ£=jþu2œ:Ûp>²¿Ô¿Û=k3d¥gNŸ(¨ võÛÓ½>9e‰_æ?ó´ÿg¤Kë{ôq×%öJõ"%‚²­å‚6hŒï*ò»_T¿l¹ Ïé>ßi]`çкlCï*ß+\­wÝ2’Ï ðØ!Çuc‹î¡e'±DÚï•Lˆó¾zYìëSzIiÍWçÌwºÍsðNè ºq"§ü”ÈNÙ.[²ç",I.h·Š]#Eš±áM­<`€ÛM&…FÊ¢©;ÀlP²îùSÄ‹¬ ÙÅ Þƒ¿SItºO0àÿaeÛú«±°ÉŸÝ]ÚÖß›ØéEŸM3„³ýCNVÒ¢K741´b{AfïŠ4…z¦«cEÀP©:O*î€B¶Üb~Ù0‡bv,®;27ôWÜSkiÇ» 8ô/›µ: GjyúÂ%Ö8U¼J>&›" ÆÔÞÁ¯KÒxÁS=QVhYoЂ¬`3NfØ#­ò†á¶å±ôÇlÉá`3u²‡æÕMß8ƒ²´quÛˆÁßW] #äƒõ‹•Ô{ ðtåÇíD‡‡‰Óµ§¤þ¤_•ÏNûmšt;ÎQKÏÃÁ«3wZ¬ÌjrÆ“&æôK%¿‹ ìßé \Û -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDh6PSnttDsv+vi tUZTP1E3hVBah6PUGDWZhYgNiyW8quTWCmPvBmCR2YzuhUrY5+CtKP8UJOQico+p oJHSAPsrzSr6YsGs3c9SQOslBmm9Fkh9/f/GZVTVZ6u5AsUmOcVvZ2q7Sz8Vj/aR aIm0EJqRe9cQ5vvN9sg25rIv4xKwIZJ1VixKWJLmpCmDINqn7xvl+ldlUmSr3aGt w21uSDuEJhQlzO3yf2FwJMkJ9SkCm9oVDXyl77OnKXj5bOQ/rojbyGeIxDJSUDWE GKyRPuqKi6rSbwg6h2G/Z9qBJkqM5NNTbGRIFz/9/LdmmwvtaqCxlLtD7RVEryAp +qTGDk5hAgMBAAECggEBAMYYfNDEYpf4A2SdCLne/9zrrfZ0kphdUkL48MDPj5vN TzTRj6f9s5ixZ/+QKn3hdwbguCx13QbH5mocP0IjUhyqoFFHYAWxyyaZfpjM8tO4 QoEYxby3BpjLe62UXESUzChQSytJZFwIDXKcdIPNO3zvVzufEJcfG5no2b9cIvsG Dy6J1FNILWxCtDIqBM+G1B1is9DhZnUDgn0iKzINiZmh1I1l7k/4tMnozVIKAfwo f1kYjG/d2IzDM02mTeTElz3IKeNriaOIYTZgI26xLJxTkiFnBV4JOWFAZw15X+yR +DrjGSIkTfhzbLa20Vt3AFM+LFK0ZoXT2dRnjbYPjQECgYEA+9XJFGwLcEX6pl1p IwXAjXKJdju9DDn4lmHTW0Pbw25h1EXONwm/NPafwsWmPll9kW9IwsxUQVUyBC9a c3Q7rF1e8ai/qqVFRIZof275MI82ciV2Mw8Hz7FPAUyoju5CvnjAEH4+irt1VE/7 SgdvQ1gDBQFegS69ijdz+cOhFxkCgYEA5aVoseMy/gIlsCvNPyw9+Jz/zBpKItX0 jGzdF7lhERRO2cursujKaoHntRckHcE3P/Z4K565bvVq+VaVG0T/BcBKPmPHrLmY iuVXidltW7Jh9/RCVwb5+BvqlwlC470PEwhqoUatY/fPJ74srztrqJHvp1L29FT5 sdmlJW8YwokCgYAUa3dMgp5C0knKp5RY1KSSU5E11w4zKZgwiWob4lq1dAPWtHpO GCo63yyBHImoUJVP75gUw4Cpc4EEudo5tlkIVuHV8nroGVKOhd9/Rb5K47Hke4kk Brn5a0Ues9qPDF65Fw1ryPDFSwHufjXAAO5SpZZJF51UGDgiNvDedbBgMQKBgHSk t7DjPhtW69234eCckD2fQS5ijBV1p2lMQmCygGM0dXiawvN02puOsCqDPoz+fxm2 DwPY80cw0M0k9UeMnBxHt25JMDrDan/iTbxu++T/jlNrdebOXFlxlI5y3c7fULDS LZcNVzTXwhjlt7yp6d0NgzTyJw2ju9BiREfnTiRBAoGBAOPHrTOnPyjO+bVcCPTB WGLsbBd77mVPGIuL0XGrvbVYPE8yIcNbZcthd8VXL/38Ygy8SIZh2ZqsrU1b5WFa XUMLnGEODSS8x/GmW3i3KeirW5OxBNjfUzEF4XkJP8m41iTdsQEXQf9DdUY7X+CB VL5h7N0VstYhGgycuPpcIUQa -----END PRIVATE KEY----- google-auth-1.5.1/tests/data/authorized_user.json0000644110576702575230000000017213277071027025257 0ustar theaflowersprimarygroup00000000000000{ "client_id": "123", "client_secret": "secret", "refresh_token": "alabalaportocala", "type": "authorized_user" } google-auth-1.5.1/tests/data/privatekey.pem0000644110576702575230000000321713277071027024041 0ustar theaflowersprimarygroup00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA4ej0p7bQ7L/r4rVGUz9RN4VQWoej1Bg1mYWIDYslvKrk1gpj 7wZgkdmM7oVK2OfgrSj/FCTkInKPqaCR0gD7K80q+mLBrN3PUkDrJQZpvRZIff3/ xmVU1WeruQLFJjnFb2dqu0s/FY/2kWiJtBCakXvXEOb7zfbINuayL+MSsCGSdVYs SliS5qQpgyDap+8b5fpXZVJkq92hrcNtbkg7hCYUJczt8n9hcCTJCfUpApvaFQ18 pe+zpyl4+WzkP66I28hniMQyUlA1hBiskT7qiouq0m8IOodhv2fagSZKjOTTU2xk SBc//fy3ZpsL7WqgsZS7Q+0VRK8gKfqkxg5OYQIDAQABAoIBAQDGGHzQxGKX+ANk nQi53v/c6632dJKYXVJC+PDAz4+bzU800Y+n/bOYsWf/kCp94XcG4Lgsdd0Gx+Zq HD9CI1IcqqBRR2AFscsmmX6YzPLTuEKBGMW8twaYy3utlFxElMwoUEsrSWRcCA1y nHSDzTt871c7nxCXHxuZ6Nm/XCL7Bg8uidRTSC1sQrQyKgTPhtQdYrPQ4WZ1A4J9 IisyDYmZodSNZe5P+LTJ6M1SCgH8KH9ZGIxv3diMwzNNpk3kxJc9yCnja4mjiGE2 YCNusSycU5IhZwVeCTlhQGcNeV/skfg64xkiJE34c2y2ttFbdwBTPixStGaF09nU Z422D40BAoGBAPvVyRRsC3BF+qZdaSMFwI1yiXY7vQw5+JZh01tD28NuYdRFzjcJ vzT2n8LFpj5ZfZFvSMLMVEFVMgQvWnN0O6xdXvGov6qlRUSGaH9u+TCPNnIldjMP B8+xTwFMqI7uQr54wBB+Poq7dVRP+0oHb0NYAwUBXoEuvYo3c/nDoRcZAoGBAOWl aLHjMv4CJbArzT8sPfic/8waSiLV9Ixs3Re5YREUTtnLq7LoymqB57UXJB3BNz/2 eCueuW71avlWlRtE/wXASj5jx6y5mIrlV4nZbVuyYff0QlcG+fgb6pcJQuO9DxMI aqFGrWP3zye+LK87a6iR76dS9vRU+bHZpSVvGMKJAoGAFGt3TIKeQtJJyqeUWNSk klORNdcOMymYMIlqG+JatXQD1rR6ThgqOt8sgRyJqFCVT++YFMOAqXOBBLnaObZZ CFbh1fJ66BlSjoXff0W+SuOx5HuJJAa5+WtFHrPajwxeuRcNa8jwxUsB7n41wADu UqWWSRedVBg4Ijbw3nWwYDECgYB0pLew4z4bVuvdt+HgnJA9n0EuYowVdadpTEJg soBjNHV4msLzdNqbjrAqgz6M/n8Ztg8D2PNHMNDNJPVHjJwcR7duSTA6w2p/4k28 bvvk/45Ta3XmzlxZcZSOct3O31Cw0i2XDVc018IY5be8qendDYM08icNo7vQYkRH 504kQQKBgQDjx60zpz8ozvm1XAj0wVhi7GwXe+5lTxiLi9Fxq721WDxPMiHDW2XL YXfFVy/9/GIMvEiGYdmarK1NW+VhWl1DC5xhDg0kvMfxplt4tynoq1uTsQTY31Mx BeF5CT/JuNYk3bEBF0H/Q3VGO1/ggVS+YezdFbLWIRoMnLj6XCFEGg== -----END RSA PRIVATE KEY----- google-auth-1.5.1/tests/data/public_cert.pem0000644110576702575230000000217313277071027024151 0ustar theaflowersprimarygroup00000000000000-----BEGIN CERTIFICATE----- MIIDIzCCAgugAwIBAgIJAMfISuBQ5m+5MA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV BAMTCnVuaXQtdGVzdHMwHhcNMTExMjA2MTYyNjAyWhcNMjExMjAzMTYyNjAyWjAV MRMwEQYDVQQDEwp1bml0LXRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEA4ej0p7bQ7L/r4rVGUz9RN4VQWoej1Bg1mYWIDYslvKrk1gpj7wZgkdmM 7oVK2OfgrSj/FCTkInKPqaCR0gD7K80q+mLBrN3PUkDrJQZpvRZIff3/xmVU1Wer uQLFJjnFb2dqu0s/FY/2kWiJtBCakXvXEOb7zfbINuayL+MSsCGSdVYsSliS5qQp gyDap+8b5fpXZVJkq92hrcNtbkg7hCYUJczt8n9hcCTJCfUpApvaFQ18pe+zpyl4 +WzkP66I28hniMQyUlA1hBiskT7qiouq0m8IOodhv2fagSZKjOTTU2xkSBc//fy3 ZpsL7WqgsZS7Q+0VRK8gKfqkxg5OYQIDAQABo3YwdDAdBgNVHQ4EFgQU2RQ8yO+O gN8oVW2SW7RLrfYd9jEwRQYDVR0jBD4wPIAU2RQ8yO+OgN8oVW2SW7RLrfYd9jGh GaQXMBUxEzARBgNVBAMTCnVuaXQtdGVzdHOCCQDHyErgUOZvuTAMBgNVHRMEBTAD AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBRv+M/6+FiVu7KXNjFI5pSN17OcW5QUtPr odJMlWrJBtynn/TA1oJlYu3yV5clc/71Vr/AxuX5xGP+IXL32YDF9lTUJXG/uUGk +JETpKmQviPbRsvzYhz4pf6ZIOZMc3/GIcNq92ECbseGO+yAgyWUVKMmZM0HqXC9 ovNslqe0M8C1sLm1zAR5z/h/litE7/8O2ietija3Q/qtl2TOXJdCA6sgjJX2WUql ybrC55ct18NKf3qhpcEkGQvFU40rVYApJpi98DiZPYFdx1oBDp/f4uZ3ojpxRVFT cDwcJLfNRCPUhormsY7fDS9xSyThiHsW9mjJYdcaKQkwYZ0F11yB -----END CERTIFICATE----- google-auth-1.5.1/tests/data/client_secrets.json0000644110576702575230000000067213277071027025056 0ustar theaflowersprimarygroup00000000000000{ "web": { "client_id": "example.apps.googleusercontent.com", "project_id": "example", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_secret": "itsasecrettoeveryone", "redirect_uris": [ "urn:ietf:wg:oauth:2.0:oob", "http://localhost" ] } } google-auth-1.5.1/tests/compute_engine/0000755110576702575230000000000013330167570023237 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/compute_engine/__init__.py0000644110576702575230000000000013277071027025337 0ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/tests/compute_engine/test_credentials.py0000644110576702575230000003473413304036026027150 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import mock import pytest from google.auth import _helpers from google.auth import exceptions from google.auth import jwt from google.auth import transport from google.auth.compute_engine import credentials class TestCredentials(object): credentials = None @pytest.fixture(autouse=True) def credentials_fixture(self): self.credentials = credentials.Credentials() def test_default_state(self): assert not self.credentials.valid # Expiration hasn't been set yet assert not self.credentials.expired # Scopes aren't needed assert not self.credentials.requires_scopes # Service account email hasn't been populated assert self.credentials.service_account_email == 'default' @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.min + _helpers.CLOCK_SKEW) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) def test_refresh_success(self, get, utcnow): get.side_effect = [{ # First request is for sevice account info. 'email': 'service-account@example.com', 'scopes': ['one', 'two'] }, { # Second request is for the token. 'access_token': 'token', 'expires_in': 500 }] # Refresh credentials self.credentials.refresh(None) # Check that the credentials have the token and proper expiration assert self.credentials.token == 'token' assert self.credentials.expiry == ( utcnow() + datetime.timedelta(seconds=500)) # Check the credential info assert (self.credentials.service_account_email == 'service-account@example.com') assert self.credentials._scopes == ['one', 'two'] # Check that the credentials are valid (have a token and are not # expired) assert self.credentials.valid @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) def test_refresh_error(self, get): get.side_effect = exceptions.TransportError('http error') with pytest.raises(exceptions.RefreshError) as excinfo: self.credentials.refresh(None) assert excinfo.match(r'http error') @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) def test_before_request_refreshes(self, get): get.side_effect = [{ # First request is for sevice account info. 'email': 'service-account@example.com', 'scopes': 'one two' }, { # Second request is for the token. 'access_token': 'token', 'expires_in': 500 }] # Credentials should start as invalid assert not self.credentials.valid # before_request should cause a refresh request = mock.create_autospec(transport.Request, instance=True) self.credentials.before_request( request, 'GET', 'http://example.com?a=1#3', {}) # The refresh endpoint should've been called. assert get.called # Credentials should now be valid. assert self.credentials.valid class TestIDTokenCredentials(object): credentials = None @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) def test_default_state(self, get): get.side_effect = [{ 'email': 'service-account@example.com', 'scope': ['one', 'two'], }] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://example.com") assert not self.credentials.valid # Expiration hasn't been set yet assert not self.credentials.expired # Service account email hasn't been populated assert (self.credentials.service_account_email == 'service-account@example.com') # Signer is initialized assert self.credentials.signer assert self.credentials.signer_email == 'service-account@example.com' @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.utcfromtimestamp(0)) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) def test_make_authorization_grant_assertion(self, sign, get, utcnow): get.side_effect = [{ 'email': 'service-account@example.com', 'scopes': ['one', 'two'] }] sign.side_effect = [b'signature'] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com") # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: assert token.endswith(b'.c2lnbmF0dXJl') # Check that the credentials have the token and proper expiration assert payload == { 'aud': 'https://www.googleapis.com/oauth2/v4/token', 'exp': 3600, 'iat': 0, 'iss': 'service-account@example.com', 'target_audience': 'https://audience.com'} @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.utcfromtimestamp(0)) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) def test_with_service_account(self, sign, get, utcnow): sign.side_effect = [b'signature'] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com", service_account_email="service-account@other.com") # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: assert token.endswith(b'.c2lnbmF0dXJl') # Check that the credentials have the token and proper expiration assert payload == { 'aud': 'https://www.googleapis.com/oauth2/v4/token', 'exp': 3600, 'iat': 0, 'iss': 'service-account@other.com', 'target_audience': 'https://audience.com'} @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.utcfromtimestamp(0)) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) def test_additional_claims(self, sign, get, utcnow): get.side_effect = [{ 'email': 'service-account@example.com', 'scopes': ['one', 'two'] }] sign.side_effect = [b'signature'] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com", additional_claims={'foo': 'bar'}) # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: assert token.endswith(b'.c2lnbmF0dXJl') # Check that the credentials have the token and proper expiration assert payload == { 'aud': 'https://www.googleapis.com/oauth2/v4/token', 'exp': 3600, 'iat': 0, 'iss': 'service-account@example.com', 'target_audience': 'https://audience.com', 'foo': 'bar'} @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.utcfromtimestamp(0)) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) def test_with_target_audience(self, sign, get, utcnow): get.side_effect = [{ 'email': 'service-account@example.com', 'scopes': ['one', 'two'] }] sign.side_effect = [b'signature'] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com") self.credentials = ( self.credentials.with_target_audience("https://actually.not")) # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: assert token.endswith(b'.c2lnbmF0dXJl') # Check that the credentials have the token and proper expiration assert payload == { 'aud': 'https://www.googleapis.com/oauth2/v4/token', 'exp': 3600, 'iat': 0, 'iss': 'service-account@example.com', 'target_audience': 'https://actually.not'} @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.utcfromtimestamp(0)) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) def test_refresh_success(self, id_token_jwt_grant, sign, get, utcnow): get.side_effect = [{ 'email': 'service-account@example.com', 'scopes': ['one', 'two'] }] sign.side_effect = [b'signature'] id_token_jwt_grant.side_effect = [( 'idtoken', datetime.datetime.utcfromtimestamp(3600), {}, )] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com") # Refresh credentials self.credentials.refresh(None) # Check that the credentials have the token and proper expiration assert self.credentials.token == 'idtoken' assert self.credentials.expiry == ( datetime.datetime.utcfromtimestamp(3600)) # Check the credential info assert (self.credentials.service_account_email == 'service-account@example.com') # Check that the credentials are valid (have a token and are not # expired) assert self.credentials.valid @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.utcfromtimestamp(0)) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) def test_refresh_error(self, sign, get, utcnow): get.side_effect = [{ 'email': 'service-account@example.com', 'scopes': ['one', 'two'], }] sign.side_effect = [b'signature'] request = mock.create_autospec(transport.Request, instance=True) response = mock.Mock() response.data = b'{"error": "http error"}' response.status = 500 request.side_effect = [response] self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com") with pytest.raises(exceptions.RefreshError) as excinfo: self.credentials.refresh(request) assert excinfo.match(r'http error') @mock.patch( 'google.auth._helpers.utcnow', return_value=datetime.datetime.utcfromtimestamp(0)) @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) def test_before_request_refreshes( self, id_token_jwt_grant, sign, get, utcnow): get.side_effect = [{ 'email': 'service-account@example.com', 'scopes': 'one two' }] sign.side_effect = [b'signature'] id_token_jwt_grant.side_effect = [( 'idtoken', datetime.datetime.utcfromtimestamp(3600), {}, )] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com") # Credentials should start as invalid assert not self.credentials.valid # before_request should cause a refresh request = mock.create_autospec(transport.Request, instance=True) self.credentials.before_request( request, 'GET', 'http://example.com?a=1#3', {}) # The refresh endpoint should've been called. assert get.called # Credentials should now be valid. assert self.credentials.valid @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) @mock.patch('google.auth.iam.Signer.sign', autospec=True) def test_sign_bytes(self, sign, get): get.side_effect = [{ 'email': 'service-account@example.com', 'scopes': ['one', 'two'] }] sign.side_effect = [b'signature'] request = mock.create_autospec(transport.Request, instance=True) response = mock.Mock() response.data = b'{"signature": "c2lnbmF0dXJl"}' response.status = 200 request.side_effect = [response] self.credentials = credentials.IDTokenCredentials( request=request, target_audience="https://audience.com") # Generate authorization grant: signature = self.credentials.sign_bytes(b"some bytes") # The JWT token signature is 'signature' encoded in base 64: assert signature == b'signature' google-auth-1.5.1/tests/compute_engine/test__metadata.py0000644110576702575230000001400413277071027026567 0ustar theaflowersprimarygroup00000000000000# 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. import datetime import json import os import mock import pytest from six.moves import http_client from six.moves import reload_module from google.auth import _helpers from google.auth import environment_vars from google.auth import exceptions from google.auth import transport from google.auth.compute_engine import _metadata PATH = 'instance/service-accounts/default' def make_request(data, status=http_client.OK, headers=None): response = mock.create_autospec(transport.Response, instance=True) response.status = status response.data = _helpers.to_bytes(data) response.headers = headers or {} request = mock.create_autospec(transport.Request) request.return_value = response return request def test_ping_success(): request = make_request('', headers=_metadata._METADATA_HEADERS) assert _metadata.ping(request) request.assert_called_once_with( method='GET', url=_metadata._METADATA_IP_ROOT, headers=_metadata._METADATA_HEADERS, timeout=_metadata._METADATA_DEFAULT_TIMEOUT) def test_ping_failure_bad_flavor(): request = make_request( '', headers={_metadata._METADATA_FLAVOR_HEADER: 'meep'}) assert not _metadata.ping(request) def test_ping_failure_connection_failed(): request = make_request('') request.side_effect = exceptions.TransportError() assert not _metadata.ping(request) def test_ping_success_custom_root(): request = make_request('', headers=_metadata._METADATA_HEADERS) fake_ip = '1.2.3.4' os.environ[environment_vars.GCE_METADATA_IP] = fake_ip reload_module(_metadata) try: assert _metadata.ping(request) finally: del os.environ[environment_vars.GCE_METADATA_IP] reload_module(_metadata) request.assert_called_once_with( method='GET', url='http://' + fake_ip, headers=_metadata._METADATA_HEADERS, timeout=_metadata._METADATA_DEFAULT_TIMEOUT) def test_get_success_json(): key, value = 'foo', 'bar' data = json.dumps({key: value}) request = make_request( data, headers={'content-type': 'application/json'}) result = _metadata.get(request, PATH) request.assert_called_once_with( method='GET', url=_metadata._METADATA_ROOT + PATH, headers=_metadata._METADATA_HEADERS) assert result[key] == value def test_get_success_text(): data = 'foobar' request = make_request(data, headers={'content-type': 'text/plain'}) result = _metadata.get(request, PATH) request.assert_called_once_with( method='GET', url=_metadata._METADATA_ROOT + PATH, headers=_metadata._METADATA_HEADERS) assert result == data def test_get_success_custom_root(): request = make_request( '{}', headers={'content-type': 'application/json'}) fake_root = 'another.metadata.service' os.environ[environment_vars.GCE_METADATA_ROOT] = fake_root reload_module(_metadata) try: _metadata.get(request, PATH) finally: del os.environ[environment_vars.GCE_METADATA_ROOT] reload_module(_metadata) request.assert_called_once_with( method='GET', url='http://{}/computeMetadata/v1/{}'.format(fake_root, PATH), headers=_metadata._METADATA_HEADERS) def test_get_failure(): request = make_request( 'Metadata error', status=http_client.NOT_FOUND) with pytest.raises(exceptions.TransportError) as excinfo: _metadata.get(request, PATH) assert excinfo.match(r'Metadata error') request.assert_called_once_with( method='GET', url=_metadata._METADATA_ROOT + PATH, headers=_metadata._METADATA_HEADERS) def test_get_failure_bad_json(): request = make_request( '{', headers={'content-type': 'application/json'}) with pytest.raises(exceptions.TransportError) as excinfo: _metadata.get(request, PATH) assert excinfo.match(r'invalid JSON') request.assert_called_once_with( method='GET', url=_metadata._METADATA_ROOT + PATH, headers=_metadata._METADATA_HEADERS) def test_get_project_id(): project = 'example-project' request = make_request( project, headers={'content-type': 'text/plain'}) project_id = _metadata.get_project_id(request) request.assert_called_once_with( method='GET', url=_metadata._METADATA_ROOT + 'project/project-id', headers=_metadata._METADATA_HEADERS) assert project_id == project @mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) def test_get_service_account_token(utcnow): ttl = 500 request = make_request( json.dumps({'access_token': 'token', 'expires_in': ttl}), headers={'content-type': 'application/json'}) token, expiry = _metadata.get_service_account_token(request) request.assert_called_once_with( method='GET', url=_metadata._METADATA_ROOT + PATH + '/token', headers=_metadata._METADATA_HEADERS) assert token == 'token' assert expiry == utcnow() + datetime.timedelta(seconds=ttl) def test_get_service_account_info(): key, value = 'foo', 'bar' request = make_request( json.dumps({key: value}), headers={'content-type': 'application/json'}) info = _metadata.get_service_account_info(request) request.assert_called_once_with( method='GET', url=_metadata._METADATA_ROOT + PATH + '/?recursive=true', headers=_metadata._METADATA_HEADERS) assert info[key] == value google-auth-1.5.1/MANIFEST.in0000644110576702575230000000014413277071027020632 0ustar theaflowersprimarygroup00000000000000include README.rst LICENSE CHANGELOG.rst recursive-include tests * global-exclude *.pyc __pycache__ google-auth-1.5.1/setup.py0000644110576702575230000000400113330166703020576 0ustar theaflowersprimarygroup00000000000000# Copyright 2014 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. import io from setuptools import find_packages from setuptools import setup DEPENDENCIES = ( 'pyasn1-modules>=0.2.1', 'rsa>=3.1.4', 'six>=1.9.0', 'cachetools>=2.0.0', ) with io.open('README.rst', 'r') as fh: long_description = fh.read() setup( name='google-auth', version='1.5.1', author='Google Cloud Platform', author_email='jonwayne+google-auth@google.com', description='Google Authentication Library', long_description=long_description, url='https://github.com/GoogleCloudPlatform/google-auth-library-python', packages=find_packages(exclude=('tests*', 'system_tests*')), namespace_packages=('google',), install_requires=DEPENDENCIES, license='Apache 2.0', keywords='google auth oauth client', classifiers=( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Operating System :: MacOS :: MacOS X', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', ), ) google-auth-1.5.1/google_auth.egg-info/0000755110576702575230000000000013330167570023063 5ustar theaflowersprimarygroup00000000000000google-auth-1.5.1/google_auth.egg-info/PKG-INFO0000644110576702575230000000641013330167570024161 0ustar theaflowersprimarygroup00000000000000Metadata-Version: 1.1 Name: google-auth Version: 1.5.1 Summary: Google Authentication Library Home-page: https://github.com/GoogleCloudPlatform/google-auth-library-python Author: Google Cloud Platform Author-email: jonwayne+google-auth@google.com License: Apache 2.0 Description: Google Auth Python Library ========================== |build| |docs| |pypi| This library simplifies using Google's various server-to-server authentication mechanisms to access Google APIs. .. |build| image:: https://travis-ci.org/GoogleCloudPlatform/google-auth-library-python.svg?branch=master :target: https://travis-ci.org/GoogleCloudPlatform/google-auth-library-python .. |docs| image:: https://readthedocs.org/projects/google-auth/badge/?version=latest :target: https://google-auth.readthedocs.io/en/latest/ .. |pypi| image:: https://img.shields.io/pypi/v/google-auth.svg :target: https://pypi.python.org/pypi/google-auth Installing ---------- You can install using `pip`_:: $ pip install google-auth .. _pip: https://pip.pypa.io/en/stable/ For more information on setting up your Python development environment, please refer to `Python Development Environment Setup Guide`_ for Google Cloud Platform. .. _`Python Development Environment Setup Guide`: https://cloud.google.com/python/setup Documentation ------------- Google Auth Python Library has usage and reference documentation at `google-auth.readthedocs.io `_. Maintainers ----------- - `@jonparrott `_ (Jon Wayne Parrott) - `@dhermes `_ (Danny Hermes) - `@lukesneeringer `_ (Luke Sneeringer) Contributing ------------ Contributions to this library are always welcome and highly encouraged. See `CONTRIBUTING.rst`_ for more information on how to get started. .. _CONTRIBUTING.rst: https://github.com/GoogleCloudPlatform/google-auth-library-python/blob/master/CONTRIBUTING.rst License ------- Apache 2.0 - See `the LICENSE`_ for more information. .. _the LICENSE: https://github.com/GoogleCloudPlatform/google-auth-library-python/blob/master/LICENSE Keywords: google auth oauth client Platform: UNKNOWN Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP google-auth-1.5.1/google_auth.egg-info/namespace_packages.txt0000644110576702575230000000000713330167570027413 0ustar theaflowersprimarygroup00000000000000google google-auth-1.5.1/google_auth.egg-info/SOURCES.txt0000644110576702575230000000452713330167570024757 0ustar theaflowersprimarygroup00000000000000CHANGELOG.rst LICENSE MANIFEST.in README.rst setup.cfg setup.py google/__init__.py google/auth/__init__.py google/auth/_cloud_sdk.py google/auth/_default.py google/auth/_helpers.py google/auth/_oauth2client.py google/auth/_service_account_info.py google/auth/app_engine.py google/auth/credentials.py google/auth/environment_vars.py google/auth/exceptions.py google/auth/iam.py google/auth/jwt.py google/auth/compute_engine/__init__.py google/auth/compute_engine/_metadata.py google/auth/compute_engine/credentials.py google/auth/crypt/__init__.py google/auth/crypt/_cryptography_rsa.py google/auth/crypt/_helpers.py google/auth/crypt/_python_rsa.py google/auth/crypt/base.py google/auth/crypt/rsa.py google/auth/transport/__init__.py google/auth/transport/_http_client.py google/auth/transport/grpc.py google/auth/transport/requests.py google/auth/transport/urllib3.py google/oauth2/__init__.py google/oauth2/_client.py google/oauth2/credentials.py google/oauth2/id_token.py google/oauth2/service_account.py google_auth.egg-info/PKG-INFO google_auth.egg-info/SOURCES.txt google_auth.egg-info/dependency_links.txt google_auth.egg-info/namespace_packages.txt google_auth.egg-info/requires.txt google_auth.egg-info/top_level.txt tests/__init__.py tests/conftest.py tests/test__cloud_sdk.py tests/test__default.py tests/test__helpers.py tests/test__oauth2client.py tests/test__service_account_info.py tests/test_app_engine.py tests/test_credentials.py tests/test_iam.py tests/test_jwt.py tests/compute_engine/__init__.py tests/compute_engine/test__metadata.py tests/compute_engine/test_credentials.py tests/crypt/__init__.py tests/crypt/test__cryptography_rsa.py tests/crypt/test__python_rsa.py tests/crypt/test_crypt.py tests/data/authorized_user.json tests/data/authorized_user_cloud_sdk.json tests/data/client_secrets.json tests/data/cloud_sdk_config.json tests/data/other_cert.pem tests/data/pem_from_pkcs12.pem tests/data/privatekey.p12 tests/data/privatekey.pem tests/data/privatekey.pub tests/data/public_cert.pem tests/data/service_account.json tests/oauth2/__init__.py tests/oauth2/test__client.py tests/oauth2/test_credentials.py tests/oauth2/test_id_token.py tests/oauth2/test_service_account.py tests/transport/__init__.py tests/transport/compliance.py tests/transport/test__http_client.py tests/transport/test_grpc.py tests/transport/test_requests.py tests/transport/test_urllib3.pygoogle-auth-1.5.1/google_auth.egg-info/requires.txt0000644110576702575230000000007613330167570025466 0ustar theaflowersprimarygroup00000000000000pyasn1-modules>=0.2.1 rsa>=3.1.4 six>=1.9.0 cachetools>=2.0.0 google-auth-1.5.1/google_auth.egg-info/top_level.txt0000644110576702575230000000000713330167570025612 0ustar theaflowersprimarygroup00000000000000google google-auth-1.5.1/google_auth.egg-info/dependency_links.txt0000644110576702575230000000000113330167570027131 0ustar theaflowersprimarygroup00000000000000 google-auth-1.5.1/setup.cfg0000644110576702575230000000010313330167570020707 0ustar theaflowersprimarygroup00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 google-auth-1.5.1/README.rst0000644110576702575230000000352213277071027020566 0ustar theaflowersprimarygroup00000000000000Google Auth Python Library ========================== |build| |docs| |pypi| This library simplifies using Google's various server-to-server authentication mechanisms to access Google APIs. .. |build| image:: https://travis-ci.org/GoogleCloudPlatform/google-auth-library-python.svg?branch=master :target: https://travis-ci.org/GoogleCloudPlatform/google-auth-library-python .. |docs| image:: https://readthedocs.org/projects/google-auth/badge/?version=latest :target: https://google-auth.readthedocs.io/en/latest/ .. |pypi| image:: https://img.shields.io/pypi/v/google-auth.svg :target: https://pypi.python.org/pypi/google-auth Installing ---------- You can install using `pip`_:: $ pip install google-auth .. _pip: https://pip.pypa.io/en/stable/ For more information on setting up your Python development environment, please refer to `Python Development Environment Setup Guide`_ for Google Cloud Platform. .. _`Python Development Environment Setup Guide`: https://cloud.google.com/python/setup Documentation ------------- Google Auth Python Library has usage and reference documentation at `google-auth.readthedocs.io `_. Maintainers ----------- - `@jonparrott `_ (Jon Wayne Parrott) - `@dhermes `_ (Danny Hermes) - `@lukesneeringer `_ (Luke Sneeringer) Contributing ------------ Contributions to this library are always welcome and highly encouraged. See `CONTRIBUTING.rst`_ for more information on how to get started. .. _CONTRIBUTING.rst: https://github.com/GoogleCloudPlatform/google-auth-library-python/blob/master/CONTRIBUTING.rst License ------- Apache 2.0 - See `the LICENSE`_ for more information. .. _the LICENSE: https://github.com/GoogleCloudPlatform/google-auth-library-python/blob/master/LICENSE google-auth-1.5.1/CHANGELOG.rst0000644110576702575230000002025013330166703021111 0ustar theaflowersprimarygroup00000000000000Changelog ========= v1.5.1 ------ - Fix check for error text on Python 3.7. (#278) - Use new Auth URIs. (#281) - Add code-of-conduct document. (#270) - Fix some typos in test_urllib3.py (#268) v1.5.0 ------ - Warn when using user credentials from the Cloud SDK (#266) - Add compute engine-based IDTokenCredentials (#236) - Corrected some typos (#265) v1.4.2 ------ - Raise a helpful exception when trying to refresh credentials without a refresh token. (#262) - Fix links to README and CONTRIBUTING in docs/index.rst. (#260) - Fix a typo in credentials.py. (#256) - Use pytest instead of py.test per upstream recommendation, #dropthedot. (#255) - Fix typo on exemple of jwt usage (#245) v1.4.1 ------ - Added a check for the cryptography version before attempting to use it. (#243) v1.4.0 ------ - Added `cryptography`-based RSA signer and verifier. (#185) - Added `google.oauth2.service_account.IDTokenCredentials`. (#234) - Improved documentation around ID Tokens (#224) v1.3.0 ------ - Added ``google.oauth2.credentials.Credentials.from_authorized_user_file`` (#226) - Dropped direct pyasn1 dependency in favor of letting ``pyasn1-modules`` specify the right version. (#230) - ``default()`` now checks for the project ID environment var before warning about missing project ID. (#227) - Fixed the docstrings for ``has_scopes()`` and ``with_scopes()``. (#228) - Fixed example in docstring for ``ReadOnlyScoped``. (#219) - Made ``transport.requests`` use timeouts and retries to improve reliability. (#220) v1.2.1 ------ - Excluded compiled Python files in source distributions. (#215) - Updated docs for creating RSASigner from string. (#213) - Use ``six.raise_from`` wherever possible. (#212) - Fixed a typo in a comment ``seconds`` not ``sections``. (#210) v1.2.0 ------ - Added ``google.auth.credentials.AnonymousCredentials``. (#206) - Updated the documentation to link to the Google Cloud Platform Python setup guide (#204) v1.1.1 ------ - ``google.oauth.credentials.Credentials`` now correctly inherits from ``ReadOnlyScoped`` instead of ``Scoped``. (#200) v1.1.0 ------ - Added ``service_account.Credentials.project_id``. (#187) - Move read-only methods of ``credentials.Scoped`` into new interface ``credentials.ReadOnlyScoped``. (#195, #196) - Make ``compute_engine.Credentials`` derive from ``ReadOnlyScoped`` instead of ``Scoped``. (#195) - Fix App Engine's expiration calculation (#197) - Split ``crypt`` module into a package to allow alternative implementations. (#189) - Add error message to handle case of empty string or missing file for GOOGLE_APPLICATION_CREDENTIALS (#188) v1.0.2 ------ - Fixed a bug where the Cloud SDK executable could not be found on Windows, leading to project ID detection failing. (#179) - Fixed a bug where the timeout argument wasn't being passed through the httplib transport correctly. (#175) - Added documentation for using the library on Google App Engine standard. (#172) - Testing style updates. (#168) - Added documentation around the oauth2client deprecation. (#165) - Fixed a few lint issues caught by newer versions of pylint. (#166) v1.0.1 ------ - Fixed a bug in the clock skew accommodation logic where expired credentials could be used for up to 5 minutes. (#158) v1.0.0 ------ Milestone release for v1.0.0. No significant changes since v0.10.0 v0.10.0 ------- - Added ``jwt.OnDemandCredentials``. (#142) - Added new public property ``id_token`` to ``oauth2.credentials.Credentials``. (#150) - Added the ability to set the address used to communicate with the Compute Engine metadata server via the ``GCE_METADATA_ROOT`` and ``GCE_METADATA_IP`` environment variables. (#148) - Changed the way cloud project IDs are ascertained from the Google Cloud SDK. (#147) - Modified expiration logic to add a 5 minute clock skew accommodation. (#145) v0.9.0 ------ - Added ``service_account.Credentials.with_claims``. (#140) - Moved ``google.auth.oauthlib`` and ``google.auth.flow`` to a new separate package ``google_auth_oauthlib``. (#137, #139, #135, #126) - Added ``InstalledAppFlow`` to ``google_auth_oauthlib``. (#128) - Fixed some packaging and documentation issues. (#131) - Added a helpful error message when importing optional dependencies. (#125) - Made all properties required to reconstruct ``google.oauth2.credentials.Credentials`` public. (#124) - Added official Python 3.6 support. (#102) - Added ``jwt.Credentials.from_signing_credentials`` and removed ``service_account.Credentials.to_jwt_credentials``. (#120) v0.8.0 ------ - Removed one-time token behavior from ``jwt.Credentials``, audience claim is now required and fixed. (#117) - ``crypt.Signer`` and ``crypt.Verifier`` are now abstract base classes. The concrete implementations have been renamed to ``crypt.RSASigner`` and ``crypt.RSAVerifier``. ``app_engine.Signer`` and ``iam.Signer`` now inherit from ``crypt.Signer``. (#115) - ``transport.grpc`` now correctly calls ``Credentials.before_request``. (#116) v0.7.0 ------ - Added ``google.auth.iam.Signer``. (#108) - Fixed issue where ``google.auth.app_engine.Signer`` erroneously returns a tuple from ``sign()``. (#109) - Added public property ``google.auth.credentials.Signing.signer``. (#110) v0.6.0 ------ - Added experimental integration with ``requests-oauthlib`` in ``google.oauth2.oauthlib`` and ``google.oauth2.flow``. (#100, #105, #106) - Fixed typo in ``google_auth_httplib2``'s README. (#105) v0.5.0 ------ - Added ``app_engine.Signer``. (#97) - Added ``crypt.Signer.from_service_account_file``. (#95) - Fixed error handling in the oauth2 client. (#96) - Fixed the App Engine system tests. v0.4.0 ------ - ``transports.grpc.secure_authorized_channel`` now passes ``kwargs`` to ``grpc.secure_channel``. (#90) - Added new property ``credentials.Singing.signer_email`` which can be used to identify the signer of a message. (#89) - (google_auth_httplib2) Added a proxy to ``httplib2.Http.connections``. v0.3.2 ------ - Fixed an issue where an ``ImportError`` would occur if ``google.oauth2`` was imported before ``google.auth``. (#88) v0.3.1 ------ - Fixed a bug where non-padded base64 encoded strings were not accepted. (#87) - Fixed a bug where ID token verification did not correctly call the HTTP request function. (#87) v0.3.0 ------ - Added Google ID token verification helpers. (#82) - Swapped the ``target`` and ``request`` argument order for ``grpc.secure_authorized_channel``. (#81) - Added a user's guide. (#79) - Made ``service_account_email`` a public property on several credential classes. (#76) - Added a ``scope`` argument to ``google.auth.default``. (#75) - Added support for the ``GCLOUD_PROJECT`` environment variable. (#73) v0.2.0 ------ - Added gRPC support. (#67) - Added Requests support. (#66) - Added ``google.auth.credentials.with_scopes_if_required`` helper. (#65) - Added private helper for oauth2client migration. (#70) v0.1.0 ------ First release with core functionality available. This version is ready for initial usage and testing. - Added ``google.auth.credentials``, public interfaces for Credential types. (#8) - Added ``google.oauth2.credentials``, credentials that use OAuth 2.0 access and refresh tokens (#24) - Added ``google.oauth2.service_account``, credentials that use Service Account private keys to obtain OAuth 2.0 access tokens. (#25) - Added ``google.auth.compute_engine``, credentials that use the Compute Engine metadata service to obtain OAuth 2.0 access tokens. (#22) - Added ``google.auth.jwt.Credentials``, credentials that use a JWT as a bearer token. - Added ``google.auth.app_engine``, credentials that use the Google App Engine App Identity service to obtain OAuth 2.0 access tokens. (#46) - Added ``google.auth.default()``, an implementation of Google Application Default Credentials that supports automatic Project ID detection. (#32) - Added system tests for all credential types. (#51, #54, #56, #58, #59, #60, #61, #62) - Added ``google.auth.transports.urllib3.AuthorizedHttp``, an HTTP client that includes authentication provided by credentials. (#19) - Documentation style and formatting updates. v0.0.1 ------ Initial release with foundational functionality for cryptography and JWTs. - ``google.auth.crypt`` for creating and verifying cryptographic signatures. - ``google.auth.jwt`` for creating (encoding) and verifying (decoding) JSON Web tokens.