diff options
| author | AlexSm <[email protected]> | 2023-12-27 23:31:58 +0100 | 
|---|---|---|
| committer | GitHub <[email protected]> | 2023-12-27 23:31:58 +0100 | 
| commit | d67bfb4b4b7549081543e87a31bc6cb5c46ac973 (patch) | |
| tree | 8674f2f1570877cb653e7ddcff37ba00288de15a /contrib/python/google-auth | |
| parent | 1f6bef05ed441c3aa2d565ac792b26cded704ac7 (diff) | |
Import libs 4 (#758)
Diffstat (limited to 'contrib/python/google-auth')
49 files changed, 758 insertions, 218 deletions
diff --git a/contrib/python/google-auth/py3/.dist-info/METADATA b/contrib/python/google-auth/py3/.dist-info/METADATA index 23841a2ee72..f86d77d41bb 100644 --- a/contrib/python/google-auth/py3/.dist-info/METADATA +++ b/contrib/python/google-auth/py3/.dist-info/METADATA @@ -1,6 +1,6 @@  Metadata-Version: 2.1  Name: google-auth -Version: 2.23.0 +Version: 2.25.2  Summary: Google Authentication Library  Home-page: https://github.com/googleapis/google-auth-library-python  Author: Google Cloud Platform @@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.8  Classifier: Programming Language :: Python :: 3.9  Classifier: Programming Language :: Python :: 3.10  Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12  Classifier: Development Status :: 5 - Production/Stable  Classifier: Intended Audience :: Developers  Classifier: License :: OSI Approved :: Apache Software License @@ -23,23 +24,22 @@ Classifier: Operating System :: OS Independent  Classifier: Topic :: Internet :: WWW/HTTP  Requires-Python: >=3.7  License-File: LICENSE -Requires-Dist: cachetools (<6.0,>=2.0.0) -Requires-Dist: pyasn1-modules (>=0.2.1) -Requires-Dist: rsa (<5,>=3.1.4) -Requires-Dist: urllib3 (<2.0) +Requires-Dist: cachetools <6.0,>=2.0.0 +Requires-Dist: pyasn1-modules >=0.2.1 +Requires-Dist: rsa <5,>=3.1.4  Provides-Extra: aiohttp -Requires-Dist: aiohttp (<4.0.0.dev0,>=3.6.2) ; extra == 'aiohttp' -Requires-Dist: requests (<3.0.0.dev0,>=2.20.0) ; extra == 'aiohttp' +Requires-Dist: aiohttp <4.0.0.dev0,>=3.6.2 ; extra == 'aiohttp' +Requires-Dist: requests <3.0.0.dev0,>=2.20.0 ; extra == 'aiohttp'  Provides-Extra: enterprise_cert -Requires-Dist: cryptography (==36.0.2) ; extra == 'enterprise_cert' -Requires-Dist: pyopenssl (==22.0.0) ; extra == 'enterprise_cert' +Requires-Dist: cryptography ==36.0.2 ; extra == 'enterprise_cert' +Requires-Dist: pyopenssl ==22.0.0 ; extra == 'enterprise_cert'  Provides-Extra: pyopenssl -Requires-Dist: pyopenssl (>=20.0.0) ; extra == 'pyopenssl' -Requires-Dist: cryptography (>=38.0.3) ; extra == 'pyopenssl' +Requires-Dist: pyopenssl >=20.0.0 ; extra == 'pyopenssl' +Requires-Dist: cryptography >=38.0.3 ; extra == 'pyopenssl'  Provides-Extra: reauth -Requires-Dist: pyu2f (>=0.1.5) ; extra == 'reauth' +Requires-Dist: pyu2f >=0.1.5 ; extra == 'reauth'  Provides-Extra: requests -Requires-Dist: requests (<3.0.0.dev0,>=2.20.0) ; extra == 'requests' +Requires-Dist: requests <3.0.0.dev0,>=2.20.0 ; extra == 'requests'  Google Auth Python Library  ========================== @@ -63,7 +63,7 @@ You can install using `pip`_::  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 +.. _`Python Development Environment Setup Guide`: https://cloud.google.com/python/docs/setup  Extras  ------ @@ -80,6 +80,16 @@ Supported Python Versions  ^^^^^^^^^^^^^^^^^^^^^^^^^  Python >= 3.7 +**NOTE**: +Python 3.7 was marked as `unsupported`_ by the python community in June 2023. +We recommend that all developers upgrade to Python 3.8 and newer as soon as +they can. Support for Python 3.7 will be removed from this library after +January 1 2024. Previous releases that support Python 3.7 will continue to be available +for download, but releases after January 1 2024 will only target Python 3.8 and +newer. + +.. _unsupported: https://devguide.python.org/versions/#unsupported-versions +  Unsupported Python Versions  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  - Python == 2.7:  The last version of this library with support for Python 2.7 diff --git a/contrib/python/google-auth/py3/README.rst b/contrib/python/google-auth/py3/README.rst index cdd19bed50d..e058f24713b 100644 --- a/contrib/python/google-auth/py3/README.rst +++ b/contrib/python/google-auth/py3/README.rst @@ -20,7 +20,7 @@ You can install using `pip`_::  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 +.. _`Python Development Environment Setup Guide`: https://cloud.google.com/python/docs/setup  Extras  ------ @@ -37,6 +37,16 @@ Supported Python Versions  ^^^^^^^^^^^^^^^^^^^^^^^^^  Python >= 3.7 +**NOTE**: +Python 3.7 was marked as `unsupported`_ by the python community in June 2023. +We recommend that all developers upgrade to Python 3.8 and newer as soon as +they can. Support for Python 3.7 will be removed from this library after +January 1 2024. Previous releases that support Python 3.7 will continue to be available +for download, but releases after January 1 2024 will only target Python 3.8 and +newer. + +.. _unsupported: https://devguide.python.org/versions/#unsupported-versions +  Unsupported Python Versions  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  - Python == 2.7:  The last version of this library with support for Python 2.7 diff --git a/contrib/python/google-auth/py3/google/auth/__init__.py b/contrib/python/google-auth/py3/google/auth/__init__.py index 2875772b375..765bbd70581 100644 --- a/contrib/python/google-auth/py3/google/auth/__init__.py +++ b/contrib/python/google-auth/py3/google/auth/__init__.py @@ -15,6 +15,8 @@  """Google Auth Library for Python."""  import logging +import sys +import warnings  from google.auth import version as google_auth_version  from google.auth._default import ( @@ -29,5 +31,23 @@ __version__ = google_auth_version.__version__  __all__ = ["default", "load_credentials_from_file", "load_credentials_from_dict"] + +class Python37DeprecationWarning(DeprecationWarning):  # pragma: NO COVER +    """ +    Deprecation warning raised when Python 3.7 runtime is detected. +    Python 3.7 support will be dropped after January 1, 2024. +    """ + +    pass + + +# Checks if the current runtime is Python 3.7. +if sys.version_info.major == 3 and sys.version_info.minor == 7:  # pragma: NO COVER +    message = ( +        "After January 1, 2024, new releases of this library will drop support " +        "for Python 3.7." +    ) +    warnings.warn(message, Python37DeprecationWarning) +  # Set default logging handler to avoid "No handler found" warnings.  logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/contrib/python/google-auth/py3/google/auth/_helpers.py b/contrib/python/google-auth/py3/google/auth/_helpers.py index ad2c095f28d..a6c07f7d829 100644 --- a/contrib/python/google-auth/py3/google/auth/_helpers.py +++ b/contrib/python/google-auth/py3/google/auth/_helpers.py @@ -17,16 +17,15 @@  import base64  import calendar  import datetime +from email.message import Message  import sys  import urllib  from google.auth import exceptions -# Token server doesn't provide a new a token when doing refresh unless the -# token is expiring within 30 seconds, so refresh threshold should not be -# more than 30 seconds. Otherwise auth lib will send tons of refresh requests -# until 30 seconds before the expiration, and cause a spike of CPU usage. -REFRESH_THRESHOLD = datetime.timedelta(seconds=20) +# The smallest MDS cache used by this library stores tokens until 4 minutes from +# expiry. +REFRESH_THRESHOLD = datetime.timedelta(minutes=3, seconds=45)  def copy_docstring(source_class): @@ -63,13 +62,42 @@ def copy_docstring(source_class):      return decorator +def parse_content_type(header_value): +    """Parse a 'content-type' header value to get just the plain media-type (without parameters). + +    This is done using the class Message from email.message as suggested in PEP 594 +        (because the cgi is now deprecated and will be removed in python 3.13, +        see https://peps.python.org/pep-0594/#cgi). + +    Args: +        header_value (str): The value of a 'content-type' header as a string. + +    Returns: +        str: A string with just the lowercase media-type from the parsed 'content-type' header. +            If the provided content-type is not parsable, returns 'text/plain', +            the default value for textual files. +    """ +    m = Message() +    m["content-type"] = header_value +    return ( +        m.get_content_type() +    )  # Despite the name, actually returns just the media-type + +  def utcnow():      """Returns the current UTC datetime.      Returns:          datetime: The current time in UTC.      """ -    return datetime.datetime.utcnow() +    # We used datetime.utcnow() before, since it's deprecated from python 3.12, +    # we are using datetime.now(timezone.utc) now. "utcnow()" is offset-native +    # (no timezone info), but "now()" is offset-aware (with timezone info). +    # This will cause datetime comparison problem. For backward compatibility, +    # we need to remove the timezone info. +    now = datetime.datetime.now(datetime.timezone.utc) +    now = now.replace(tzinfo=None) +    return now  def datetime_to_secs(value): diff --git a/contrib/python/google-auth/py3/google/auth/compute_engine/__init__.py b/contrib/python/google-auth/py3/google/auth/compute_engine/__init__.py index 5c84234e937..7e1206fc1b2 100644 --- a/contrib/python/google-auth/py3/google/auth/compute_engine/__init__.py +++ b/contrib/python/google-auth/py3/google/auth/compute_engine/__init__.py @@ -14,8 +14,9 @@  """Google Compute Engine authentication.""" +from google.auth.compute_engine._metadata import detect_gce_residency_linux  from google.auth.compute_engine.credentials import Credentials  from google.auth.compute_engine.credentials import IDTokenCredentials -__all__ = ["Credentials", "IDTokenCredentials"] +__all__ = ["Credentials", "IDTokenCredentials", "detect_gce_residency_linux"] diff --git a/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py b/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py index 04abe178f58..1c884c3c43e 100644 --- a/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py +++ b/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py @@ -156,6 +156,7 @@ def get(      recursive=False,      retry_count=5,      headers=None, +    return_none_for_not_found_error=False,  ):      """Fetch a resource from the metadata server. @@ -173,6 +174,8 @@ def get(          retry_count (int): How many times to attempt connecting to metadata              server using above timeout.          headers (Optional[Mapping[str, str]]): Headers for the request. +        return_none_for_not_found_error (Optional[bool]): If True, returns None +            for 404 error instead of throwing an exception.      Returns:          Union[Mapping, str]: If the metadata server returns JSON, a mapping of @@ -216,9 +219,21 @@ def get(              "metadata service. Compute Engine Metadata server unavailable".format(url)          ) +    content = _helpers.from_bytes(response.data) + +    if response.status == http_client.NOT_FOUND and return_none_for_not_found_error: +        _LOGGER.info( +            "Compute Engine Metadata server call to %s returned 404, reason: %s", +            path, +            content, +        ) +        return None +      if response.status == http_client.OK: -        content = _helpers.from_bytes(response.data) -        if response.headers["content-type"] == "application/json": +        if ( +            _helpers.parse_content_type(response.headers["content-type"]) +            == "application/json" +        ):              try:                  return json.loads(content)              except ValueError as caught_exc: @@ -229,14 +244,14 @@ def get(                  raise new_exc from 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, -        ) + +    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): @@ -256,6 +271,29 @@ def get_project_id(request):      return get(request, "project/project-id") +def get_universe_domain(request): +    """Get the universe domain value from the metadata server. + +    Args: +        request (google.auth.transport.Request): A callable used to make +            HTTP requests. + +    Returns: +        str: The universe domain value. If the universe domain endpoint is not +        not found, return the default value, which is googleapis.com + +    Raises: +        google.auth.exceptions.TransportError: if an error other than +            404 occurs while retrieving metadata. +    """ +    universe_domain = get( +        request, "universe/universe_domain", return_none_for_not_found_error=True +    ) +    if not universe_domain: +        return "googleapis.com" +    return universe_domain + +  def get_service_account_info(request, service_account="default"):      """Get information about a service account from the metadata server. diff --git a/contrib/python/google-auth/py3/google/auth/compute_engine/credentials.py b/contrib/python/google-auth/py3/google/auth/compute_engine/credentials.py index 7ae673880f9..a035c7697af 100644 --- a/contrib/python/google-auth/py3/google/auth/compute_engine/credentials.py +++ b/contrib/python/google-auth/py3/google/auth/compute_engine/credentials.py @@ -28,6 +28,7 @@ from google.auth import iam  from google.auth import jwt  from google.auth import metrics  from google.auth.compute_engine import _metadata +from google.auth.transport import requests as google_auth_requests  from google.oauth2 import _client @@ -73,6 +74,8 @@ class Credentials(credentials.Scoped, credentials.CredentialsWithQuotaProject):          self._quota_project_id = quota_project_id          self._scopes = scopes          self._default_scopes = default_scopes +        self._universe_domain_cached = False +        self._universe_domain_request = google_auth_requests.Request()      def _retrieve_info(self, request):          """Retrieve information about the service account. @@ -131,6 +134,16 @@ class Credentials(credentials.Scoped, credentials.CredentialsWithQuotaProject):      def requires_scopes(self):          return not self._scopes +    @property +    def universe_domain(self): +        if self._universe_domain_cached: +            return self._universe_domain +        self._universe_domain = _metadata.get_universe_domain( +            self._universe_domain_request +        ) +        self._universe_domain_cached = True +        return self._universe_domain +      @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)      def with_quota_project(self, quota_project_id):          return self.__class__( diff --git a/contrib/python/google-auth/py3/google/auth/credentials.py b/contrib/python/google-auth/py3/google/auth/credentials.py index 80a2a5c0b4c..800781c4089 100644 --- a/contrib/python/google-auth/py3/google/auth/credentials.py +++ b/contrib/python/google-auth/py3/google/auth/credentials.py @@ -52,8 +52,9 @@ class Credentials(metaclass=abc.ABCMeta):          self._quota_project_id = None          """Optional[str]: Project to use for quota and billing purposes."""          self._trust_boundary = None -        """Optional[str]: Encoded string representation of credentials trust -        boundary.""" +        """Optional[dict]: Cache of a trust boundary response which has a list +        of allowed regions and an encoded string representation of credentials +        trust boundary."""          self._universe_domain = "googleapis.com"          """Optional[str]: The universe domain value, default is googleapis.com          """ @@ -135,8 +136,21 @@ class Credentials(metaclass=abc.ABCMeta):          headers["authorization"] = "Bearer {}".format(              _helpers.from_bytes(token or self.token)          ) +        """Trust boundary value will be a cached value from global lookup. + +        The response of trust boundary will be a list of regions and a hex +        encoded representation. + +        An example of global lookup response: +        { +          "locations": [ +            "us-central1", "us-east1", "europe-west1", "asia-east1" +          ] +          "encoded_locations": "0xA30" +        } +        """          if self._trust_boundary is not None: -            headers["x-identity-trust-boundary"] = self._trust_boundary +            headers["x-allowed-locations"] = self._trust_boundary["encoded_locations"]          if self.quota_project_id:              headers["x-goog-user-project"] = self.quota_project_id diff --git a/contrib/python/google-auth/py3/google/auth/crypt/_cryptography_rsa.py b/contrib/python/google-auth/py3/google/auth/crypt/_cryptography_rsa.py index 4f2d6116665..1a3e9ff52c6 100644 --- a/contrib/python/google-auth/py3/google/auth/crypt/_cryptography_rsa.py +++ b/contrib/python/google-auth/py3/google/auth/crypt/_cryptography_rsa.py @@ -134,3 +134,18 @@ class RSASigner(base.Signer, base.FromServiceAccountMixin):              key, password=None, backend=_BACKEND          )          return cls(private_key, key_id=key_id) + +    def __getstate__(self): +        """Pickle helper that serializes the _key attribute.""" +        state = self.__dict__.copy() +        state["_key"] = self._key.private_bytes( +            encoding=serialization.Encoding.PEM, +            format=serialization.PrivateFormat.PKCS8, +            encryption_algorithm=serialization.NoEncryption(), +        ) +        return state + +    def __setstate__(self, state): +        """Pickle helper that deserializes the _key attribute.""" +        state["_key"] = serialization.load_pem_private_key(state["_key"], None) +        self.__dict__.update(state) diff --git a/contrib/python/google-auth/py3/google/auth/crypt/es256.py b/contrib/python/google-auth/py3/google/auth/crypt/es256.py index 7920cc7ffba..820e4beccee 100644 --- a/contrib/python/google-auth/py3/google/auth/crypt/es256.py +++ b/contrib/python/google-auth/py3/google/auth/crypt/es256.py @@ -158,3 +158,18 @@ class ES256Signer(base.Signer, base.FromServiceAccountMixin):              key, password=None, backend=_BACKEND          )          return cls(private_key, key_id=key_id) + +    def __getstate__(self): +        """Pickle helper that serializes the _key attribute.""" +        state = self.__dict__.copy() +        state["_key"] = self._key.private_bytes( +            encoding=serialization.Encoding.PEM, +            format=serialization.PrivateFormat.PKCS8, +            encryption_algorithm=serialization.NoEncryption(), +        ) +        return state + +    def __setstate__(self, state): +        """Pickle helper that deserializes the _key attribute.""" +        state["_key"] = serialization.load_pem_private_key(state["_key"], None) +        self.__dict__.update(state) diff --git a/contrib/python/google-auth/py3/google/auth/external_account.py b/contrib/python/google-auth/py3/google/auth/external_account.py index c45e6f2133f..e7fed8695ad 100644 --- a/contrib/python/google-auth/py3/google/auth/external_account.py +++ b/contrib/python/google-auth/py3/google/auth/external_account.py @@ -132,7 +132,10 @@ class Credentials(          self._default_scopes = default_scopes          self._workforce_pool_user_project = workforce_pool_user_project          self._universe_domain = universe_domain or _DEFAULT_UNIVERSE_DOMAIN -        self._trust_boundary = "0"  # expose a placeholder trust boundary value. +        self._trust_boundary = { +            "locations": [], +            "encoded_locations": "0x0", +        }  # expose a placeholder trust boundary value.          if self._client_id:              self._client_auth = utils.ClientAuthentication( @@ -412,6 +415,22 @@ class Credentials(          new_cred._metrics_options = self._metrics_options          return new_cred +    def with_universe_domain(self, universe_domain): +        """Create a copy of these credentials with the given universe domain. + +        Args: +            universe_domain (str): The universe domain value. + +        Returns: +            google.auth.external_account.Credentials: A new credentials +                instance. +        """ +        kwargs = self._constructor_args() +        kwargs.update(universe_domain=universe_domain) +        new_cred = self.__class__(**kwargs) +        new_cred._metrics_options = self._metrics_options +        return new_cred +      def _initialize_impersonated_credentials(self):          """Generates an impersonated credentials. diff --git a/contrib/python/google-auth/py3/google/auth/transport/_custom_tls_signer.py b/contrib/python/google-auth/py3/google/auth/transport/_custom_tls_signer.py index 07f14df02de..57a563d03bd 100644 --- a/contrib/python/google-auth/py3/google/auth/transport/_custom_tls_signer.py +++ b/contrib/python/google-auth/py3/google/auth/transport/_custom_tls_signer.py @@ -107,6 +107,22 @@ def load_signer_lib(signer_lib_path):      return lib +def load_provider_lib(provider_lib_path): +    _LOGGER.debug("loading provider library from %s", provider_lib_path) + +    # winmode parameter is only available for python 3.8+. +    lib = ( +        ctypes.CDLL(provider_lib_path, winmode=0) +        if sys.version_info >= (3, 8) and os.name == "nt" +        else ctypes.CDLL(provider_lib_path) +    ) + +    lib.ECP_attach_to_ctx.argtypes = [ctypes.c_void_p, ctypes.c_char_p] +    lib.ECP_attach_to_ctx.restype = ctypes.c_int + +    return lib + +  # Computes SHA256 hash.  def _compute_sha256_digest(to_be_signed, to_be_signed_len):      from cryptography.hazmat.primitives import hashes @@ -199,21 +215,31 @@ class CustomTlsSigner(object):          self._enterprise_cert_file_path = enterprise_cert_file_path          self._cert = None          self._sign_callback = None +        self._provider_lib = None      def load_libraries(self): -        try: -            with open(self._enterprise_cert_file_path, "r") as f: -                enterprise_cert_json = json.load(f) -                libs = enterprise_cert_json["libs"] -                signer_library = libs["ecp_client"] -                offload_library = libs["tls_offload"] -        except (KeyError, ValueError) as caught_exc: -            new_exc = exceptions.MutualTLSChannelError( -                "enterprise cert file is invalid", caught_exc -            ) -            raise new_exc from caught_exc -        self._offload_lib = load_offload_lib(offload_library) -        self._signer_lib = load_signer_lib(signer_library) +        with open(self._enterprise_cert_file_path, "r") as f: +            enterprise_cert_json = json.load(f) +            libs = enterprise_cert_json.get("libs", {}) + +            signer_library = libs.get("ecp_client", None) +            offload_library = libs.get("tls_offload", None) +            provider_library = libs.get("ecp_provider", None) + +        # Using newer provider implementation. This is mutually exclusive to the +        # offload implementation. +        if provider_library: +            self._provider_lib = load_provider_lib(provider_library) +            return + +        # Using old offload implementation +        if offload_library and signer_library: +            self._offload_lib = load_offload_lib(offload_library) +            self._signer_lib = load_signer_lib(signer_library) +            self.set_up_custom_key() +            return + +        raise exceptions.MutualTLSChannelError("enterprise cert file is invalid")      def set_up_custom_key(self):          # We need to keep a reference of the cert and sign callback so it won't @@ -224,11 +250,22 @@ class CustomTlsSigner(object):          )      def attach_to_ssl_context(self, ctx): -        # In the TLS handshake, the signing operation will be done by the -        # sign_callback. -        if not self._offload_lib.ConfigureSslContext( -            self._sign_callback, -            ctypes.c_char_p(self._cert), -            _cast_ssl_ctx_to_void_p(ctx._ctx._context), -        ): -            raise exceptions.MutualTLSChannelError("failed to configure SSL context") +        if self._provider_lib: +            if not self._provider_lib.ECP_attach_to_ctx( +                _cast_ssl_ctx_to_void_p(ctx._ctx._context), +                self._enterprise_cert_file_path.encode("ascii"), +            ): +                raise exceptions.MutualTLSChannelError( +                    "failed to configure ECP Provider SSL context" +                ) +        elif self._offload_lib and self._signer_lib: +            if not self._offload_lib.ConfigureSslContext( +                self._sign_callback, +                ctypes.c_char_p(self._cert), +                _cast_ssl_ctx_to_void_p(ctx._ctx._context), +            ): +                raise exceptions.MutualTLSChannelError( +                    "failed to configure ECP Offload SSL context" +                ) +        else: +            raise exceptions.MutualTLSChannelError("Invalid ECP configuration.") diff --git a/contrib/python/google-auth/py3/google/auth/transport/requests.py b/contrib/python/google-auth/py3/google/auth/transport/requests.py index b9bcad359f0..aa161132262 100644 --- a/contrib/python/google-auth/py3/google/auth/transport/requests.py +++ b/contrib/python/google-auth/py3/google/auth/transport/requests.py @@ -274,7 +274,6 @@ class _MutualTlsOffloadAdapter(requests.adapters.HTTPAdapter):          self.signer = _custom_tls_signer.CustomTlsSigner(enterprise_cert_file_path)          self.signer.load_libraries() -        self.signer.set_up_custom_key()          poolmanager = create_urllib3_context()          poolmanager.load_verify_locations(cafile=certifi.where()) diff --git a/contrib/python/google-auth/py3/google/auth/transport/urllib3.py b/contrib/python/google-auth/py3/google/auth/transport/urllib3.py index 053d6f7b728..63144f5fffa 100644 --- a/contrib/python/google-auth/py3/google/auth/transport/urllib3.py +++ b/contrib/python/google-auth/py3/google/auth/transport/urllib3.py @@ -40,11 +40,18 @@ except ImportError as caught_exc:  # pragma: NO COVER          "urllib3 package to use the urllib3 transport."      ) from caught_exc +from packaging import version  # type: ignore +  from google.auth import environment_vars  from google.auth import exceptions  from google.auth import transport  from google.oauth2 import service_account +if version.parse(urllib3.__version__) >= version.parse("2.0.0"):  # pragma: NO COVER +    RequestMethods = urllib3._request_methods.RequestMethods  # type: ignore +else:  # pragma: NO COVER +    RequestMethods = urllib3.request.RequestMethods  # type: ignore +  _LOGGER = logging.getLogger(__name__) @@ -179,7 +186,7 @@ def _make_mutual_tls_http(cert, key):      return http -class AuthorizedHttp(urllib3.request.RequestMethods): +class AuthorizedHttp(RequestMethods):  # type: ignore      """A urllib3 HTTP class with credentials.      This class is used to perform requests to API endpoints that require diff --git a/contrib/python/google-auth/py3/google/auth/version.py b/contrib/python/google-auth/py3/google/auth/version.py index 491187e6d78..31cc30242a2 100644 --- a/contrib/python/google-auth/py3/google/auth/version.py +++ b/contrib/python/google-auth/py3/google/auth/version.py @@ -12,4 +12,4 @@  # See the License for the specific language governing permissions and  # limitations under the License. -__version__ = "2.23.0" +__version__ = "2.25.2" diff --git a/contrib/python/google-auth/py3/google/oauth2/__init__.py b/contrib/python/google-auth/py3/google/oauth2/__init__.py index 4fb71fd1ad3..accae96579d 100644 --- a/contrib/python/google-auth/py3/google/oauth2/__init__.py +++ b/contrib/python/google-auth/py3/google/oauth2/__init__.py @@ -13,3 +13,24 @@  # limitations under the License.  """Google OAuth 2.0 Library for Python.""" + +import sys +import warnings + + +class Python37DeprecationWarning(DeprecationWarning):  # pragma: NO COVER +    """ +    Deprecation warning raised when Python 3.7 runtime is detected. +    Python 3.7 support will be dropped after January 1, 2024. +    """ + +    pass + + +# Checks if the current runtime is Python 3.7. +if sys.version_info.major == 3 and sys.version_info.minor == 7:  # pragma: NO COVER +    message = ( +        "After January 1, 2024, new releases of this library will drop support " +        "for Python 3.7." +    ) +    warnings.warn(message, Python37DeprecationWarning) diff --git a/contrib/python/google-auth/py3/google/oauth2/_credentials_async.py b/contrib/python/google-auth/py3/google/oauth2/_credentials_async.py index e7b9637c82f..b5561aae022 100644 --- a/contrib/python/google-auth/py3/google/oauth2/_credentials_async.py +++ b/contrib/python/google-auth/py3/google/oauth2/_credentials_async.py @@ -96,6 +96,12 @@ class Credentials(oauth2_credentials.Credentials):                      )                  ) +    @_helpers.copy_docstring(credentials.Credentials) +    async def before_request(self, request, method, url, headers): +        if not self.valid: +            await self.refresh(request) +        self.apply(headers) +  class UserAccessTokenCredentials(oauth2_credentials.UserAccessTokenCredentials):      """Access token credentials for user account. diff --git a/contrib/python/google-auth/py3/google/oauth2/credentials.py b/contrib/python/google-auth/py3/google/oauth2/credentials.py index 4643fdbea67..a5c93ecc2f8 100644 --- a/contrib/python/google-auth/py3/google/oauth2/credentials.py +++ b/contrib/python/google-auth/py3/google/oauth2/credentials.py @@ -49,13 +49,15 @@ _LOGGER = logging.getLogger(__name__)  # The Google OAuth 2.0 token endpoint. Used for authorized user credentials.  _GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token" +_DEFAULT_UNIVERSE_DOMAIN = "googleapis.com"  class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaProject):      """Credentials using OAuth 2.0 access and refresh tokens. -    The credentials are considered immutable. If you want to modify the -    quota project, use :meth:`with_quota_project` or :: +    The credentials are considered immutable except the tokens and the token +    expiry, which are updated after refresh. If you want to modify the quota +    project, use :meth:`with_quota_project` or ::          credentials = credentials.with_quota_project('myproject-123') @@ -84,6 +86,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr          enable_reauth_refresh=False,          granted_scopes=None,          trust_boundary=None, +        universe_domain=_DEFAULT_UNIVERSE_DOMAIN,      ):          """          Args: @@ -125,6 +128,9 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr              granted_scopes (Optional[Sequence[str]]): The scopes that were consented/granted by the user.                  This could be different from the requested scopes and it could be empty if granted                  and requested scopes were same. +            trust_boundary (str): String representation of trust boundary meta. +            universe_domain (Optional[str]): The universe domain. The default +                universe domain is googleapis.com.          """          super(Credentials, self).__init__()          self.token = token @@ -142,6 +148,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr          self.refresh_handler = refresh_handler          self._enable_reauth_refresh = enable_reauth_refresh          self._trust_boundary = trust_boundary +        self._universe_domain = universe_domain or _DEFAULT_UNIVERSE_DOMAIN      def __getstate__(self):          """A __getstate__ method must exist for the __setstate__ to be called @@ -173,7 +180,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr          self._rapt_token = d.get("_rapt_token")          self._enable_reauth_refresh = d.get("_enable_reauth_refresh")          self._trust_boundary = d.get("_trust_boundary") -        self._universe_domain = d.get("_universe_domain") +        self._universe_domain = d.get("_universe_domain") or _DEFAULT_UNIVERSE_DOMAIN          # The refresh_handler setter should be used to repopulate this.          self._refresh_handler = None @@ -272,6 +279,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr              rapt_token=self.rapt_token,              enable_reauth_refresh=self._enable_reauth_refresh,              trust_boundary=self._trust_boundary, +            universe_domain=self._universe_domain,          )      @_helpers.copy_docstring(credentials.CredentialsWithTokenUri) @@ -291,6 +299,34 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr              rapt_token=self.rapt_token,              enable_reauth_refresh=self._enable_reauth_refresh,              trust_boundary=self._trust_boundary, +            universe_domain=self._universe_domain, +        ) + +    def with_universe_domain(self, universe_domain): +        """Create a copy of the credential with the given universe domain. + +        Args: +            universe_domain (str): The universe domain value. + +        Returns: +            google.oauth2.credentials.Credentials: A new credentials instance. +        """ + +        return self.__class__( +            self.token, +            refresh_token=self.refresh_token, +            id_token=self.id_token, +            token_uri=self._token_uri, +            client_id=self.client_id, +            client_secret=self.client_secret, +            scopes=self.scopes, +            default_scopes=self.default_scopes, +            granted_scopes=self.granted_scopes, +            quota_project_id=self.quota_project_id, +            rapt_token=self.rapt_token, +            enable_reauth_refresh=self._enable_reauth_refresh, +            trust_boundary=self._trust_boundary, +            universe_domain=universe_domain,          )      def _metric_header_for_usage(self): @@ -298,6 +334,17 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr      @_helpers.copy_docstring(credentials.Credentials)      def refresh(self, request): +        if self._universe_domain != _DEFAULT_UNIVERSE_DOMAIN: +            raise exceptions.RefreshError( +                "User credential refresh is only supported in the default " +                "googleapis.com universe domain, but the current universe " +                "domain is {}. If you created the credential with an access " +                "token, it's likely that the provided token is expired now, " +                "please update your code with a valid token.".format( +                    self._universe_domain +                ) +            ) +          scopes = self._scopes if self._scopes is not None else self._default_scopes          # Use refresh handler if available and no refresh token is          # available. This is useful in general when tokens are obtained by calling @@ -427,6 +474,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr              expiry=expiry,              rapt_token=info.get("rapt_token"),  # may not exist              trust_boundary=info.get("trust_boundary"),  # may not exist +            universe_domain=info.get("universe_domain"),  # may not exist          )      @classmethod @@ -470,6 +518,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr              "client_secret": self.client_secret,              "scopes": self.scopes,              "rapt_token": self.rapt_token, +            "universe_domain": self._universe_domain,          }          if self.expiry:  # flatten expiry timestamp              prep["expiry"] = self.expiry.isoformat() + "Z" diff --git a/contrib/python/google-auth/py3/google/oauth2/service_account.py b/contrib/python/google-auth/py3/google/oauth2/service_account.py index e08899f8e53..68db41af409 100644 --- a/contrib/python/google-auth/py3/google/oauth2/service_account.py +++ b/contrib/python/google-auth/py3/google/oauth2/service_account.py @@ -182,10 +182,7 @@ class Credentials(          self._quota_project_id = quota_project_id          self._token_uri = token_uri          self._always_use_jwt_access = always_use_jwt_access -        if not universe_domain: -            self._universe_domain = _DEFAULT_UNIVERSE_DOMAIN -        else: -            self._universe_domain = universe_domain +        self._universe_domain = universe_domain or _DEFAULT_UNIVERSE_DOMAIN          if universe_domain != _DEFAULT_UNIVERSE_DOMAIN:              self._always_use_jwt_access = True @@ -196,7 +193,7 @@ class Credentials(              self._additional_claims = additional_claims          else:              self._additional_claims = {} -        self._trust_boundary = "0" +        self._trust_boundary = {"locations": [], "encoded_locations": "0x0"}      @classmethod      def _from_signer_and_info(cls, signer, info, **kwargs): @@ -328,6 +325,22 @@ class Credentials(          cred._always_use_jwt_access = always_use_jwt_access          return cred +    def with_universe_domain(self, universe_domain): +        """Create a copy of these credentials with the given universe domain. + +        Args: +            universe_domain (str): The universe domain value. + +        Returns: +            google.auth.service_account.Credentials: A new credentials +                instance. +        """ +        cred = self._make_copy() +        cred._universe_domain = universe_domain +        if universe_domain != _DEFAULT_UNIVERSE_DOMAIN: +            cred._always_use_jwt_access = True +        return cred +      def with_subject(self, subject):          """Create a copy of these credentials with the specified subject. @@ -417,13 +430,11 @@ class Credentials(      @_helpers.copy_docstring(credentials.Credentials)      def refresh(self, request): -        if ( -            self._universe_domain != _DEFAULT_UNIVERSE_DOMAIN -            and not self._jwt_credentials -        ): -            raise exceptions.RefreshError( -                "self._jwt_credentials is missing for non-default universe domain" -            ) +        if self._always_use_jwt_access and not self._jwt_credentials: +            # If self signed jwt should be used but jwt credential is not +            # created, try to create one with scopes +            self._create_self_signed_jwt(None) +          if self._universe_domain != _DEFAULT_UNIVERSE_DOMAIN and self._subject:              raise exceptions.RefreshError(                  "domain wide delegation is not supported for non-default universe domain" diff --git a/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py b/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py index ddf84596aff..5e037a940bd 100644 --- a/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py +++ b/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py @@ -63,6 +63,7 @@ def make_request(data, status=http_client.OK, headers=None, retry=False):      return request  def test_detect_gce_residency_linux_success():      _metadata._GCE_PRODUCT_NAME_FILE = SMBIOS_PRODUCT_NAME_FILE      assert _metadata.detect_gce_residency_linux() @@ -89,6 +90,7 @@ def test_is_on_gce_windows_success():      assert not _metadata.is_on_gce(request)  @mock.patch("os.name", new="posix")  def test_is_on_gce_linux_success():      request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"}) @@ -176,6 +178,24 @@ def test_get_success_json():      assert result[key] == value +def test_get_success_json_content_type_charset(): +    key, value = "foo", "bar" + +    data = json.dumps({key: value}) +    request = make_request( +        data, headers={"content-type": "application/json; charset=UTF-8"} +    ) + +    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_retry():      key, value = "foo", "bar" @@ -307,6 +327,18 @@ def test_get_failure():      ) +def test_get_return_none_for_not_found_error(): +    request = make_request("Metadata error", status=http_client.NOT_FOUND) + +    assert _metadata.get(request, PATH, return_none_for_not_found_error=True) is None + +    request.assert_called_once_with( +        method="GET", +        url=_metadata._METADATA_ROOT + PATH, +        headers=_metadata._METADATA_HEADERS, +    ) + +  def test_get_failure_connection_failed():      request = make_request("")      request.side_effect = exceptions.TransportError() @@ -353,6 +385,53 @@ def test_get_project_id():      assert project_id == project +def test_get_universe_domain_success(): +    request = make_request( +        "fake_universe_domain", headers={"content-type": "text/plain"} +    ) + +    universe_domain = _metadata.get_universe_domain(request) + +    request.assert_called_once_with( +        method="GET", +        url=_metadata._METADATA_ROOT + "universe/universe_domain", +        headers=_metadata._METADATA_HEADERS, +    ) +    assert universe_domain == "fake_universe_domain" + + +def test_get_universe_domain_not_found(): +    # Test that if the universe domain endpoint returns 404 error, we should +    # use googleapis.com as the universe domain +    request = make_request("not found", status=http_client.NOT_FOUND) + +    universe_domain = _metadata.get_universe_domain(request) + +    request.assert_called_once_with( +        method="GET", +        url=_metadata._METADATA_ROOT + "universe/universe_domain", +        headers=_metadata._METADATA_HEADERS, +    ) +    assert universe_domain == "googleapis.com" + + +def test_get_universe_domain_other_error(): +    # Test that if the universe domain endpoint returns an error other than 404 +    # we should throw the error +    request = make_request("unauthorized", status=http_client.UNAUTHORIZED) + +    with pytest.raises(exceptions.TransportError) as excinfo: +        _metadata.get_universe_domain(request) + +    assert excinfo.match(r"unauthorized") + +    request.assert_called_once_with( +        method="GET", +        url=_metadata._METADATA_ROOT + "universe/universe_domain", +        headers=_metadata._METADATA_HEADERS, +    ) + +  @mock.patch(      "google.auth.metrics.token_request_access_token_mds",      return_value=ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, diff --git a/contrib/python/google-auth/py3/tests/compute_engine/test_credentials.py b/contrib/python/google-auth/py3/tests/compute_engine/test_credentials.py index 507fea9fcc9..5d6ccdcdec6 100644 --- a/contrib/python/google-auth/py3/tests/compute_engine/test_credentials.py +++ b/contrib/python/google-auth/py3/tests/compute_engine/test_credentials.py @@ -208,6 +208,30 @@ class TestCredentials(object):          assert headers["authorization"] == "Bearer token"          assert headers["x-goog-api-client"] == "cred-type/mds" +    @mock.patch( +        "google.auth.compute_engine._metadata.get_universe_domain", +        return_value="fake_universe_domain", +    ) +    def test_universe_domain(self, get_universe_domain): +        self.credentials._universe_domain_cached = False +        self.credentials._universe_domain = "googleapis.com" + +        # calling the universe_domain property should trigger a call to +        # get_universe_domain to fetch the value. The value should be cached. +        assert self.credentials.universe_domain == "fake_universe_domain" +        assert self.credentials._universe_domain == "fake_universe_domain" +        assert self.credentials._universe_domain_cached +        get_universe_domain.assert_called_once_with( +            self.credentials._universe_domain_request +        ) + +        # calling the universe_domain property the second time should use the +        # cached value instead of calling get_universe_domain +        assert self.credentials.universe_domain == "fake_universe_domain" +        get_universe_domain.assert_called_once_with( +            self.credentials._universe_domain_request +        ) +  class TestIDTokenCredentials(object):      credentials = None diff --git a/contrib/python/google-auth/py3/tests/conftest.py b/contrib/python/google-auth/py3/tests/conftest.py index 08896b0f828..7658d8f4567 100644 --- a/contrib/python/google-auth/py3/tests/conftest.py +++ b/contrib/python/google-auth/py3/tests/conftest.py @@ -21,9 +21,14 @@ import pytest  # type: ignore  def pytest_configure():      """Load public certificate and private key.""" -    import __res -    pytest.private_key_bytes = __res.find("data/privatekey.pem") -    pytest.public_cert_bytes = __res.find("data/public_cert.pem") +    import yatest.common as yc +    pytest.data_dir = os.path.join(os.path.dirname(yc.source_path("contrib/python/google-auth/py3/tests/conftest.py")), "data") + +    with open(os.path.join(pytest.data_dir, "privatekey.pem"), "rb") as fh: +        pytest.private_key_bytes = fh.read() + +    with open(os.path.join(pytest.data_dir, "public_cert.pem"), "rb") as fh: +        pytest.public_cert_bytes = fh.read()  @pytest.fixture diff --git a/contrib/python/google-auth/py3/tests/crypt/test__cryptography_rsa.py b/contrib/python/google-auth/py3/tests/crypt/test__cryptography_rsa.py index d19154b61b8..2c4cebe0d71 100644 --- a/contrib/python/google-auth/py3/tests/crypt/test__cryptography_rsa.py +++ b/contrib/python/google-auth/py3/tests/crypt/test__cryptography_rsa.py @@ -14,6 +14,7 @@  import json  import os +import pickle  from cryptography.hazmat.primitives.asymmetric import rsa  import pytest  # type: ignore @@ -23,8 +24,8 @@ from google.auth.crypt import _cryptography_rsa  from google.auth.crypt import base -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  # To generate privatekey.pem, privatekey.pub, and public_cert.pem:  #   $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ @@ -160,3 +161,17 @@ class TestRSASigner(object):          assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID]          assert isinstance(signer._key, rsa.RSAPrivateKey) + +    def test_pickle(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) + +        pickled_signer = pickle.dumps(signer) +        signer = pickle.loads(pickled_signer) + +        assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] +        assert isinstance(signer._key, rsa.RSAPrivateKey) diff --git a/contrib/python/google-auth/py3/tests/crypt/test__python_rsa.py b/contrib/python/google-auth/py3/tests/crypt/test__python_rsa.py index 592b523d927..75dcb314f73 100644 --- a/contrib/python/google-auth/py3/tests/crypt/test__python_rsa.py +++ b/contrib/python/google-auth/py3/tests/crypt/test__python_rsa.py @@ -26,8 +26,8 @@ from google.auth.crypt import _python_rsa  from google.auth.crypt import base -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  # To generate privatekey.pem, privatekey.pub, and public_cert.pem:  #   $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ diff --git a/contrib/python/google-auth/py3/tests/crypt/test_crypt.py b/contrib/python/google-auth/py3/tests/crypt/test_crypt.py index 97c2abc2574..30de18a5dd3 100644 --- a/contrib/python/google-auth/py3/tests/crypt/test_crypt.py +++ b/contrib/python/google-auth/py3/tests/crypt/test_crypt.py @@ -17,8 +17,8 @@ import os  from google.auth import crypt -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  # To generate privatekey.pem, privatekey.pub, and public_cert.pem:  #   $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ diff --git a/contrib/python/google-auth/py3/tests/crypt/test_es256.py b/contrib/python/google-auth/py3/tests/crypt/test_es256.py index 1a43a2f01be..3ba5b64fadc 100644 --- a/contrib/python/google-auth/py3/tests/crypt/test_es256.py +++ b/contrib/python/google-auth/py3/tests/crypt/test_es256.py @@ -15,6 +15,7 @@  import base64  import json  import os +import pickle  from cryptography.hazmat.primitives.asymmetric import ec  import pytest  # type: ignore @@ -24,8 +25,8 @@ from google.auth.crypt import base  from google.auth.crypt import es256 -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  # To generate es256_privatekey.pem, es256_privatekey.pub, and  # es256_public_cert.pem: @@ -142,3 +143,15 @@ class TestES256Signer(object):          assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID]          assert isinstance(signer._key, ec.EllipticCurvePrivateKey) + +    def test_pickle(self): +        signer = es256.ES256Signer.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, ec.EllipticCurvePrivateKey) + +        pickled_signer = pickle.dumps(signer) +        signer = pickle.loads(pickled_signer) + +        assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] +        assert isinstance(signer._key, ec.EllipticCurvePrivateKey) diff --git a/contrib/python/google-auth/py3/tests/data/enterprise_cert_valid_provider.json b/contrib/python/google-auth/py3/tests/data/enterprise_cert_valid_provider.json new file mode 100644 index 00000000000..9b7adf8bc30 --- /dev/null +++ b/contrib/python/google-auth/py3/tests/data/enterprise_cert_valid_provider.json @@ -0,0 +1,6 @@ +{ +    "libs": { +        "ecp_client": "/path/to/signer/lib", +        "ecp_provider": "/path/to/provider/lib" +    } +} diff --git a/contrib/python/google-auth/py3/tests/oauth2/test__client.py b/contrib/python/google-auth/py3/tests/oauth2/test__client.py index 54179269bde..444232f3967 100644 --- a/contrib/python/google-auth/py3/tests/oauth2/test__client.py +++ b/contrib/python/google-auth/py3/tests/oauth2/test__client.py @@ -29,8 +29,8 @@ from google.auth import transport  from google.oauth2 import _client -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:      PRIVATE_KEY_BYTES = fh.read() diff --git a/contrib/python/google-auth/py3/tests/oauth2/test_credentials.py b/contrib/python/google-auth/py3/tests/oauth2/test_credentials.py index f2604a5f187..d6a19158627 100644 --- a/contrib/python/google-auth/py3/tests/oauth2/test_credentials.py +++ b/contrib/python/google-auth/py3/tests/oauth2/test_credentials.py @@ -27,8 +27,8 @@ from google.auth import transport  from google.oauth2 import credentials -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  AUTH_USER_JSON_FILE = os.path.join(DATA_DIR, "authorized_user.json") @@ -123,6 +123,17 @@ class TestCredentials(object):          assert excinfo.match("The provided refresh_handler is not a callable or None.") +    def test_refresh_with_non_default_universe_domain(self): +        creds = credentials.Credentials( +            token="token", universe_domain="dummy_universe.com" +        ) +        with pytest.raises(exceptions.RefreshError) as excinfo: +            creds.refresh(mock.Mock()) + +        assert excinfo.match( +            "refresh is only supported in the default googleapis.com universe domain" +        ) +      @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)      @mock.patch(          "google.auth._helpers.utcnow", @@ -775,6 +786,12 @@ class TestCredentials(object):          creds.apply(headers)          assert "x-goog-user-project" in headers +    def test_with_universe_domain(self): +        creds = credentials.Credentials(token="token") +        assert creds.universe_domain == "googleapis.com" +        new_creds = creds.with_universe_domain("dummy_universe.com") +        assert new_creds.universe_domain == "dummy_universe.com" +      def test_with_token_uri(self):          info = AUTH_USER_INFO.copy() @@ -869,6 +886,7 @@ class TestCredentials(object):          assert json_asdict.get("scopes") == creds.scopes          assert json_asdict.get("client_secret") == creds.client_secret          assert json_asdict.get("expiry") == info["expiry"] +        assert json_asdict.get("universe_domain") == creds.universe_domain          # Test with a `strip` arg          json_output = creds.to_json(strip=["client_secret"]) @@ -896,6 +914,17 @@ class TestCredentials(object):          for attr in list(creds.__dict__):              assert getattr(creds, attr) == getattr(unpickled, attr) +    def test_pickle_and_unpickle_universe_domain(self): +        # old version of auth lib doesn't have _universe_domain, so the pickled +        # cred doesn't have such a field. +        creds = self.make_credentials() +        del creds._universe_domain + +        unpickled = pickle.loads(pickle.dumps(creds)) + +        # make sure the unpickled cred sets _universe_domain to default. +        assert unpickled.universe_domain == "googleapis.com" +      def test_pickle_and_unpickle_with_refresh_handler(self):          expected_expiry = _helpers.utcnow() + datetime.timedelta(seconds=2800)          refresh_handler = mock.Mock(return_value=("TOKEN", expected_expiry)) diff --git a/contrib/python/google-auth/py3/tests/oauth2/test_gdch_credentials.py b/contrib/python/google-auth/py3/tests/oauth2/test_gdch_credentials.py index 1ff61d86832..9a67a073458 100644 --- a/contrib/python/google-auth/py3/tests/oauth2/test_gdch_credentials.py +++ b/contrib/python/google-auth/py3/tests/oauth2/test_gdch_credentials.py @@ -27,7 +27,7 @@ import google.auth.transport.requests  from google.oauth2 import gdch_credentials  from google.oauth2.gdch_credentials import ServiceAccountCredentials -import yatest.common +import yatest.common as yc  class TestServiceAccountCredentials(object): @@ -39,7 +39,7 @@ class TestServiceAccountCredentials(object):      TOKEN_URI = "https://service-identity.<Domain>/authenticate"      JSON_PATH = os.path.join( -        yatest.common.test_source_path(), "data", "gdch_service_account.json" +        os.path.dirname(yc.source_path(__file__)), "..", "data", "gdch_service_account.json"      )      with open(JSON_PATH, "rb") as fh:          INFO = json.load(fh) diff --git a/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py b/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py index 861f76ce4f6..8657bdfb7eb 100644 --- a/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py +++ b/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py @@ -24,9 +24,9 @@ from google.auth import transport  from google.oauth2 import id_token  from google.oauth2 import service_account -import yatest.common +import yatest.common as yc  SERVICE_ACCOUNT_FILE = os.path.join( -    yatest.common.test_source_path(), "data/service_account.json" +    os.path.dirname(yc.source_path(__file__)), "../data/service_account.json"  )  ID_TOKEN_AUDIENCE = "https://pubsub.googleapis.com" @@ -263,7 +263,7 @@ def test_fetch_id_token_credentials_no_cred_exists(monkeypatch):  def test_fetch_id_token_credentials_invalid_cred_file_type(monkeypatch):      user_credentials_file = os.path.join( -        yatest.common.test_source_path(), "data/authorized_user.json" +        os.path.dirname(yc.source_path(__file__)), "../data/authorized_user.json"      )      monkeypatch.setenv(environment_vars.CREDENTIALS, user_credentials_file) @@ -276,7 +276,7 @@ def test_fetch_id_token_credentials_invalid_cred_file_type(monkeypatch):  def test_fetch_id_token_credentials_invalid_json(monkeypatch): -    not_json_file = os.path.join(yatest.common.test_source_path(), "data/public_cert.pem") +    not_json_file = os.path.join(os.path.dirname(yc.source_path(__file__)), "../data/public_cert.pem")      monkeypatch.setenv(environment_vars.CREDENTIALS, not_json_file)      with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: @@ -287,7 +287,7 @@ def test_fetch_id_token_credentials_invalid_json(monkeypatch):  def test_fetch_id_token_credentials_invalid_cred_path(monkeypatch): -    not_json_file = os.path.join(yatest.common.test_source_path(), "data/not_exists.json") +    not_json_file = os.path.join(os.path.dirname(yc.source_path(__file__)), "../data/not_exists.json")      monkeypatch.setenv(environment_vars.CREDENTIALS, not_json_file)      with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: diff --git a/contrib/python/google-auth/py3/tests/oauth2/test_service_account.py b/contrib/python/google-auth/py3/tests/oauth2/test_service_account.py index c474c90e6bb..8dd5f219be1 100644 --- a/contrib/python/google-auth/py3/tests/oauth2/test_service_account.py +++ b/contrib/python/google-auth/py3/tests/oauth2/test_service_account.py @@ -27,8 +27,8 @@ from google.auth import transport  from google.oauth2 import service_account -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:      PRIVATE_KEY_BYTES = fh.read() @@ -206,6 +206,17 @@ class TestCredentials(object):          creds_with_new_token_uri = credentials.with_token_uri(new_token_uri)          assert creds_with_new_token_uri._token_uri == new_token_uri +    def test_with_universe_domain(self): +        credentials = self.make_credentials() + +        new_credentials = credentials.with_universe_domain("dummy_universe.com") +        assert new_credentials.universe_domain == "dummy_universe.com" +        assert new_credentials._always_use_jwt_access + +        new_credentials = credentials.with_universe_domain("googleapis.com") +        assert new_credentials.universe_domain == "googleapis.com" +        assert not new_credentials._always_use_jwt_access +      def test__with_always_use_jwt_access(self):          credentials = self.make_credentials()          assert not credentials._always_use_jwt_access @@ -558,12 +569,16 @@ class TestCredentials(object):          assert jwt_grant.called          assert not self_signed_jwt_refresh.called -    def test_refresh_non_gdu_missing_jwt_credentials(self): -        credentials = self.make_credentials(universe_domain="foo") +    def test_refresh_missing_jwt_credentials(self): +        credentials = self.make_credentials() +        credentials = credentials.with_scopes(["foo", "bar"]) +        credentials = credentials.with_always_use_jwt_access(True) +        assert not credentials._jwt_credentials -        with pytest.raises(exceptions.RefreshError) as excinfo: -            credentials.refresh(None) -        assert excinfo.match("self._jwt_credentials is missing") +        credentials.refresh(mock.Mock()) + +        # jwt credentials should have been automatically created with scopes +        assert credentials._jwt_credentials is not None      def test_refresh_non_gdu_domain_wide_delegation_not_supported(self):          credentials = self.make_credentials(universe_domain="foo") diff --git a/contrib/python/google-auth/py3/tests/test__cloud_sdk.py b/contrib/python/google-auth/py3/tests/test__cloud_sdk.py index 18ac18fa35a..d46621a7f30 100644 --- a/contrib/python/google-auth/py3/tests/test__cloud_sdk.py +++ b/contrib/python/google-auth/py3/tests/test__cloud_sdk.py @@ -26,8 +26,8 @@ from google.auth import environment_vars  from google.auth import exceptions -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")  AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, "authorized_user.json")  with io.open(AUTHORIZED_USER_FILE, "rb") as fh: @@ -66,8 +66,7 @@ def test_get_project_id_call_error(check_output):      assert check_output.called -def test__run_subprocess_ignore_stderr(): +def _test__run_subprocess_ignore_stderr():      command = [          sys.executable,          "-c", diff --git a/contrib/python/google-auth/py3/tests/test__default.py b/contrib/python/google-auth/py3/tests/test__default.py index 29904ec7aaf..d619614790c 100644 --- a/contrib/python/google-auth/py3/tests/test__default.py +++ b/contrib/python/google-auth/py3/tests/test__default.py @@ -36,8 +36,8 @@ from google.oauth2 import service_account  import google.oauth2.credentials -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")  AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, "authorized_user.json")  with open(AUTHORIZED_USER_FILE) as fh: diff --git a/contrib/python/google-auth/py3/tests/test__helpers.py b/contrib/python/google-auth/py3/tests/test__helpers.py index c1f1d812e57..c9a3847ac48 100644 --- a/contrib/python/google-auth/py3/tests/test__helpers.py +++ b/contrib/python/google-auth/py3/tests/test__helpers.py @@ -51,6 +51,32 @@ def test_copy_docstring_non_existing():          _helpers.copy_docstring(SourceClass)(func2) +def test_parse_content_type_plain(): +    assert _helpers.parse_content_type("text/html") == "text/html" +    assert _helpers.parse_content_type("application/xml") == "application/xml" +    assert _helpers.parse_content_type("application/json") == "application/json" + + +def test_parse_content_type_with_parameters(): +    content_type_html = "text/html; charset=UTF-8" +    content_type_xml = "application/xml; charset=UTF-16; version=1.0" +    content_type_json = "application/json; charset=UTF-8; indent=2" +    assert _helpers.parse_content_type(content_type_html) == "text/html" +    assert _helpers.parse_content_type(content_type_xml) == "application/xml" +    assert _helpers.parse_content_type(content_type_json) == "application/json" + + +def test_parse_content_type_missing_or_broken(): +    content_type_foo = None +    content_type_bar = "" +    content_type_baz = "1234" +    content_type_qux = " ; charset=UTF-8" +    assert _helpers.parse_content_type(content_type_foo) == "text/plain" +    assert _helpers.parse_content_type(content_type_bar) == "text/plain" +    assert _helpers.parse_content_type(content_type_baz) == "text/plain" +    assert _helpers.parse_content_type(content_type_qux) == "text/plain" + +  def test_utcnow():      assert isinstance(_helpers.utcnow(), datetime.datetime) diff --git a/contrib/python/google-auth/py3/tests/test__oauth2client.py b/contrib/python/google-auth/py3/tests/test__oauth2client.py index 72db6535bc6..1db595fd9ac 100644 --- a/contrib/python/google-auth/py3/tests/test__oauth2client.py +++ b/contrib/python/google-auth/py3/tests/test__oauth2client.py @@ -33,8 +33,8 @@ except ImportError:  # pragma: NO COVER  from google.auth import _oauth2client -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")  SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") diff --git a/contrib/python/google-auth/py3/tests/test__service_account_info.py b/contrib/python/google-auth/py3/tests/test__service_account_info.py index db8106081ce..2335765bb42 100644 --- a/contrib/python/google-auth/py3/tests/test__service_account_info.py +++ b/contrib/python/google-auth/py3/tests/test__service_account_info.py @@ -21,8 +21,8 @@ from google.auth import _service_account_info  from google.auth import crypt -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")  SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json")  GDCH_SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "gdch_service_account.json") diff --git a/contrib/python/google-auth/py3/tests/test_aws.py b/contrib/python/google-auth/py3/tests/test_aws.py index 39138ab12e0..db2e984100f 100644 --- a/contrib/python/google-auth/py3/tests/test_aws.py +++ b/contrib/python/google-auth/py3/tests/test_aws.py @@ -1969,7 +1969,7 @@ class TestCredentials(object):              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]),              "x-goog-user-project": QUOTA_PROJECT_ID,              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, @@ -2066,7 +2066,7 @@ class TestCredentials(object):              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]),              "x-goog-user-project": QUOTA_PROJECT_ID,              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, diff --git a/contrib/python/google-auth/py3/tests/test_credentials.py b/contrib/python/google-auth/py3/tests/test_credentials.py index 99235cda615..d64f3abb506 100644 --- a/contrib/python/google-auth/py3/tests/test_credentials.py +++ b/contrib/python/google-auth/py3/tests/test_credentials.py @@ -55,9 +55,7 @@ def test_expired_and_valid():      # 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.REFRESH_THRESHOLD -        + datetime.timedelta(seconds=1) +        _helpers.utcnow() + _helpers.REFRESH_THRESHOLD + datetime.timedelta(seconds=1)      )      assert credentials.valid @@ -65,7 +63,7 @@ def test_expired_and_valid():      # Set the credentials expiration to now. Because of the clock skew      # accomodation, these credentials should report as expired. -    credentials.expiry = datetime.datetime.utcnow() +    credentials.expiry = _helpers.utcnow()      assert not credentials.valid      assert credentials.expired @@ -81,7 +79,7 @@ def test_before_request():      assert credentials.valid      assert credentials.token == "token"      assert headers["authorization"] == "Bearer token" -    assert "x-identity-trust-boundary" not in headers +    assert "x-allowed-locations" not in headers      request = "token2"      headers = {} @@ -91,13 +89,13 @@ def test_before_request():      assert credentials.valid      assert credentials.token == "token"      assert headers["authorization"] == "Bearer token" -    assert "x-identity-trust-boundary" not in headers +    assert "x-allowed-locations" not in headers  def test_before_request_with_trust_boundary(): -    DUMMY_BOUNDARY = "00110101" +    DUMMY_BOUNDARY = "0xA30"      credentials = CredentialsImpl() -    credentials._trust_boundary = DUMMY_BOUNDARY +    credentials._trust_boundary = {"locations": [], "encoded_locations": DUMMY_BOUNDARY}      request = "token"      headers = {} @@ -106,7 +104,7 @@ def test_before_request_with_trust_boundary():      assert credentials.valid      assert credentials.token == "token"      assert headers["authorization"] == "Bearer token" -    assert headers["x-identity-trust-boundary"] == DUMMY_BOUNDARY +    assert headers["x-allowed-locations"] == DUMMY_BOUNDARY      request = "token2"      headers = {} @@ -116,7 +114,7 @@ def test_before_request_with_trust_boundary():      assert credentials.valid      assert credentials.token == "token"      assert headers["authorization"] == "Bearer token" -    assert headers["x-identity-trust-boundary"] == DUMMY_BOUNDARY +    assert headers["x-allowed-locations"] == DUMMY_BOUNDARY  def test_before_request_metrics(): diff --git a/contrib/python/google-auth/py3/tests/test_external_account.py b/contrib/python/google-auth/py3/tests/test_external_account.py index 0b165bc70bb..5225dcf3424 100644 --- a/contrib/python/google-auth/py3/tests/test_external_account.py +++ b/contrib/python/google-auth/py3/tests/test_external_account.py @@ -505,6 +505,11 @@ class TestCredentials(object):          credentials = self.make_credentials()          assert credentials.universe_domain == external_account._DEFAULT_UNIVERSE_DOMAIN +    def test_with_universe_domain(self): +        credentials = self.make_credentials() +        new_credentials = credentials.with_universe_domain("dummy_universe.com") +        assert new_credentials.universe_domain == "dummy_universe.com" +      def test_info_workforce_pool(self):          credentials = self.make_workforce_pool_credentials(              workforce_pool_user_project=self.WORKFORCE_POOL_USER_PROJECT @@ -833,7 +838,7 @@ class TestCredentials(object):              "Content-Type": "application/json",              "authorization": "Bearer {}".format(token_response["access_token"]),              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, @@ -915,7 +920,7 @@ class TestCredentials(object):              "Content-Type": "application/json",              "authorization": "Bearer {}".format(token_response["access_token"]),              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, @@ -1134,7 +1139,7 @@ class TestCredentials(object):              "Content-Type": "application/json",              "authorization": "Bearer {}".format(token_response["access_token"]),              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, @@ -1218,7 +1223,7 @@ class TestCredentials(object):              "Content-Type": "application/json",              "authorization": "Bearer {}".format(token_response["access_token"]),              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, @@ -1274,7 +1279,7 @@ class TestCredentials(object):          assert headers == {              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      def test_apply_workforce_without_quota_project_id(self): @@ -1291,7 +1296,7 @@ class TestCredentials(object):          assert headers == {              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      def test_apply_impersonation_without_quota_project_id(self): @@ -1323,7 +1328,7 @@ class TestCredentials(object):          assert headers == {              "authorization": "Bearer {}".format(impersonation_response["accessToken"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      def test_apply_with_quota_project_id(self): @@ -1340,7 +1345,7 @@ class TestCredentials(object):              "other": "header-value",              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]),              "x-goog-user-project": self.QUOTA_PROJECT_ID, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      def test_apply_impersonation_with_quota_project_id(self): @@ -1375,7 +1380,7 @@ class TestCredentials(object):              "other": "header-value",              "authorization": "Bearer {}".format(impersonation_response["accessToken"]),              "x-goog-user-project": self.QUOTA_PROJECT_ID, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      def test_before_request(self): @@ -1391,7 +1396,7 @@ class TestCredentials(object):          assert headers == {              "other": "header-value",              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          # Second call shouldn't call refresh. @@ -1400,7 +1405,7 @@ class TestCredentials(object):          assert headers == {              "other": "header-value",              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      def test_before_request_workforce(self): @@ -1418,7 +1423,7 @@ class TestCredentials(object):          assert headers == {              "other": "header-value",              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          # Second call shouldn't call refresh. @@ -1427,7 +1432,7 @@ class TestCredentials(object):          assert headers == {              "other": "header-value",              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      def test_before_request_impersonation(self): @@ -1458,7 +1463,7 @@ class TestCredentials(object):          assert headers == {              "other": "header-value",              "authorization": "Bearer {}".format(impersonation_response["accessToken"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          # Second call shouldn't call refresh. @@ -1467,7 +1472,7 @@ class TestCredentials(object):          assert headers == {              "other": "header-value",              "authorization": "Bearer {}".format(impersonation_response["accessToken"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      @mock.patch("google.auth._helpers.utcnow") @@ -1495,7 +1500,7 @@ class TestCredentials(object):          # Cached token should be used.          assert headers == {              "authorization": "Bearer token", -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          # Next call should simulate 1 second passed. @@ -1509,7 +1514,7 @@ class TestCredentials(object):          # New token should be retrieved.          assert headers == {              "authorization": "Bearer {}".format(self.SUCCESS_RESPONSE["access_token"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      @mock.patch("google.auth._helpers.utcnow") @@ -1552,7 +1557,7 @@ class TestCredentials(object):          # Cached token should be used.          assert headers == {              "authorization": "Bearer token", -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          # Next call should simulate 1 second passed. This will trigger the expiration @@ -1567,7 +1572,7 @@ class TestCredentials(object):          # New token should be retrieved.          assert headers == {              "authorization": "Bearer {}".format(impersonation_response["accessToken"]), -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }      @pytest.mark.parametrize( @@ -1666,7 +1671,7 @@ class TestCredentials(object):              "x-goog-user-project": self.QUOTA_PROJECT_ID,              "authorization": "Bearer {}".format(token_response["access_token"]),              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, @@ -1720,7 +1725,7 @@ class TestCredentials(object):                  "authorization": "Bearer {}".format(                      impersonation_response["accessToken"]                  ), -                "x-identity-trust-boundary": "0", +                "x-allowed-locations": "0x0",              },          ) @@ -1792,7 +1797,7 @@ class TestCredentials(object):                  "authorization": "Bearer {}".format(                      self.SUCCESS_RESPONSE["access_token"]                  ), -                "x-identity-trust-boundary": "0", +                "x-allowed-locations": "0x0",              },          ) @@ -1842,7 +1847,7 @@ class TestCredentials(object):              "Content-Type": "application/json",              "authorization": "Bearer {}".format(token_response["access_token"]),              "x-goog-api-client": IMPERSONATE_ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE, -            "x-identity-trust-boundary": "0", +            "x-allowed-locations": "0x0",          }          impersonation_request_data = {              "delegates": None, diff --git a/contrib/python/google-auth/py3/tests/test_identity_pool.py b/contrib/python/google-auth/py3/tests/test_identity_pool.py index d126a579bd8..2d10a5d2683 100644 --- a/contrib/python/google-auth/py3/tests/test_identity_pool.py +++ b/contrib/python/google-auth/py3/tests/test_identity_pool.py @@ -45,8 +45,8 @@ SERVICE_ACCOUNT_IMPERSONATION_URL = (  QUOTA_PROJECT_ID = "QUOTA_PROJECT_ID"  SCOPES = ["scope1", "scope2"] -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")  SUBJECT_TOKEN_TEXT_FILE = os.path.join(DATA_DIR, "external_subject_token.txt")  SUBJECT_TOKEN_JSON_FILE = os.path.join(DATA_DIR, "external_subject_token.json")  SUBJECT_TOKEN_FIELD_NAME = "access_token" @@ -320,7 +320,7 @@ class TestCredentials(object):                  "Content-Type": "application/json",                  "authorization": "Bearer {}".format(token_response["access_token"]),                  "x-goog-api-client": metrics_header_value, -                "x-identity-trust-boundary": "0", +                "x-allowed-locations": "0x0",              }              impersonation_request_data = {                  "delegates": None, diff --git a/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py b/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py index d63d2d5d3b3..9696e823fff 100644 --- a/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py +++ b/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py @@ -29,8 +29,8 @@ from google.auth.impersonated_credentials import Credentials  from google.oauth2 import credentials  from google.oauth2 import service_account -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")  with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:      PRIVATE_KEY_BYTES = fh.read() diff --git a/contrib/python/google-auth/py3/tests/test_jwt.py b/contrib/python/google-auth/py3/tests/test_jwt.py index 62f310606dd..ff8fd67da65 100644 --- a/contrib/python/google-auth/py3/tests/test_jwt.py +++ b/contrib/python/google-auth/py3/tests/test_jwt.py @@ -26,8 +26,8 @@ from google.auth import exceptions  from google.auth import jwt -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")  with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:      PRIVATE_KEY_BYTES = fh.read() diff --git a/contrib/python/google-auth/py3/tests/transport/test__custom_tls_signer.py b/contrib/python/google-auth/py3/tests/transport/test__custom_tls_signer.py index 5836b325add..d2907bad297 100644 --- a/contrib/python/google-auth/py3/tests/transport/test__custom_tls_signer.py +++ b/contrib/python/google-auth/py3/tests/transport/test__custom_tls_signer.py @@ -11,7 +11,6 @@  # 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 ctypes  import os @@ -30,11 +29,19 @@ FAKE_ENTERPRISE_CERT_FILE_PATH = "/path/to/enterprise/cert/file"  ENTERPRISE_CERT_FILE = os.path.join(      os.path.dirname(__file__), "../data/enterprise_cert_valid.json"  ) +ENTERPRISE_CERT_FILE_PROVIDER = os.path.join( +    os.path.dirname(__file__), "../data/enterprise_cert_valid_provider.json" +)  INVALID_ENTERPRISE_CERT_FILE = os.path.join(      os.path.dirname(__file__), "../data/enterprise_cert_invalid.json"  ) +def test_load_provider_lib(): +    with mock.patch("ctypes.CDLL", return_value=mock.MagicMock()): +        _custom_tls_signer.load_provider_lib("/path/to/provider/lib") + +  def test_load_offload_lib():      with mock.patch("ctypes.CDLL", return_value=mock.MagicMock()):          lib = _custom_tls_signer.load_offload_lib("/path/to/offload/lib") @@ -173,62 +180,81 @@ def test_custom_tls_signer():          ) as load_offload_lib:              load_offload_lib.return_value = offload_lib              load_signer_lib.return_value = signer_lib -            signer_object = _custom_tls_signer.CustomTlsSigner(ENTERPRISE_CERT_FILE) -            signer_object.load_libraries() -    assert signer_object._cert is None +            with mock.patch( +                "google.auth.transport._custom_tls_signer.get_cert" +            ) as get_cert: +                with mock.patch( +                    "google.auth.transport._custom_tls_signer.get_sign_callback" +                ) as get_sign_callback: +                    get_cert.return_value = b"mock_cert" +                    signer_object = _custom_tls_signer.CustomTlsSigner( +                        ENTERPRISE_CERT_FILE +                    ) +                    signer_object.load_libraries() +                    signer_object.attach_to_ssl_context(create_urllib3_context()) +                    get_cert.assert_called_once() +                    get_sign_callback.assert_called_once() +                    offload_lib.ConfigureSslContext.assert_called_once()      assert signer_object._enterprise_cert_file_path == ENTERPRISE_CERT_FILE      assert signer_object._offload_lib == offload_lib      assert signer_object._signer_lib == signer_lib      load_signer_lib.assert_called_with("/path/to/signer/lib")      load_offload_lib.assert_called_with("/path/to/offload/lib") -    # Test set_up_custom_key and set_up_ssl_context methods -    with mock.patch("google.auth.transport._custom_tls_signer.get_cert") as get_cert: -        with mock.patch( -            "google.auth.transport._custom_tls_signer.get_sign_callback" -        ) as get_sign_callback: -            get_cert.return_value = b"mock_cert" -            signer_object.set_up_custom_key() -            signer_object.attach_to_ssl_context(create_urllib3_context()) -    get_cert.assert_called_once() -    get_sign_callback.assert_called_once() -    offload_lib.ConfigureSslContext.assert_called_once() +def test_custom_tls_signer_provider(): +    provider_lib = mock.MagicMock() -def test_custom_tls_signer_failed_to_load_libraries():      # Test load_libraries method +    with mock.patch( +        "google.auth.transport._custom_tls_signer.load_provider_lib" +    ) as load_provider_lib: +        load_provider_lib.return_value = provider_lib +        signer_object = _custom_tls_signer.CustomTlsSigner( +            ENTERPRISE_CERT_FILE_PROVIDER +        ) +        signer_object.load_libraries() +        signer_object.attach_to_ssl_context(mock.MagicMock()) + +    assert signer_object._enterprise_cert_file_path == ENTERPRISE_CERT_FILE_PROVIDER +    assert signer_object._provider_lib == provider_lib +    load_provider_lib.assert_called_with("/path/to/provider/lib") + + +def test_custom_tls_signer_failed_to_load_libraries():      with pytest.raises(exceptions.MutualTLSChannelError) as excinfo:          signer_object = _custom_tls_signer.CustomTlsSigner(INVALID_ENTERPRISE_CERT_FILE)          signer_object.load_libraries()      assert excinfo.match("enterprise cert file is invalid") -def test_custom_tls_signer_fail_to_offload(): -    offload_lib = mock.MagicMock() -    signer_lib = mock.MagicMock() +def test_custom_tls_signer_failed_to_attach(): +    with pytest.raises(exceptions.MutualTLSChannelError) as excinfo: +        signer_object = _custom_tls_signer.CustomTlsSigner(ENTERPRISE_CERT_FILE) +        signer_object._offload_lib = mock.MagicMock() +        signer_object._signer_lib = mock.MagicMock() +        signer_object._sign_callback = mock.MagicMock() +        signer_object._cert = b"mock cert" +        signer_object._offload_lib.ConfigureSslContext.return_value = False +        signer_object.attach_to_ssl_context(mock.MagicMock()) +    assert excinfo.match("failed to configure ECP Offload SSL context") -    with mock.patch( -        "google.auth.transport._custom_tls_signer.load_signer_lib" -    ) as load_signer_lib: -        with mock.patch( -            "google.auth.transport._custom_tls_signer.load_offload_lib" -        ) as load_offload_lib: -            load_offload_lib.return_value = offload_lib -            load_signer_lib.return_value = signer_lib -            signer_object = _custom_tls_signer.CustomTlsSigner(ENTERPRISE_CERT_FILE) -            signer_object.load_libraries() -    # set the return value to be 0 which indicts offload fails -    offload_lib.ConfigureSslContext.return_value = 0 +def test_custom_tls_signer_failed_to_attach_provider(): +    with pytest.raises(exceptions.MutualTLSChannelError) as excinfo: +        signer_object = _custom_tls_signer.CustomTlsSigner( +            ENTERPRISE_CERT_FILE_PROVIDER +        ) +        signer_object._provider_lib = mock.MagicMock() +        signer_object._provider_lib.ECP_attach_to_ctx.return_value = False +        signer_object.attach_to_ssl_context(mock.MagicMock()) +    assert excinfo.match("failed to configure ECP Provider SSL context") + +def test_custom_tls_signer_failed_to_attach_no_libs():      with pytest.raises(exceptions.MutualTLSChannelError) as excinfo: -        with mock.patch( -            "google.auth.transport._custom_tls_signer.get_cert" -        ) as get_cert: -            with mock.patch( -                "google.auth.transport._custom_tls_signer.get_sign_callback" -            ): -                get_cert.return_value = b"mock_cert" -                signer_object.set_up_custom_key() -                signer_object.attach_to_ssl_context(create_urllib3_context()) -    assert excinfo.match("failed to configure SSL context") +        signer_object = _custom_tls_signer.CustomTlsSigner(ENTERPRISE_CERT_FILE) +        signer_object._offload_lib = None +        signer_object._signer_lib = None +        signer_object.attach_to_ssl_context(mock.MagicMock()) +    assert excinfo.match("Invalid ECP configuration.") diff --git a/contrib/python/google-auth/py3/tests/transport/test__mtls_helper.py b/contrib/python/google-auth/py3/tests/transport/test__mtls_helper.py index 642283a5c50..1621a053021 100644 --- a/contrib/python/google-auth/py3/tests/transport/test__mtls_helper.py +++ b/contrib/python/google-auth/py3/tests/transport/test__mtls_helper.py @@ -22,9 +22,6 @@ import pytest  # type: ignore  from google.auth import exceptions  from google.auth.transport import _mtls_helper -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") -  CONTEXT_AWARE_METADATA = {"cert_provider_command": ["some command"]}  ENCRYPTED_EC_PRIVATE_KEY = b"""-----BEGIN ENCRYPTED PRIVATE KEY----- @@ -116,26 +113,26 @@ class TestCertAndKeyRegex(object):  class TestCheckaMetadataPath(object):      def test_success(self): -        metadata_path = os.path.join(DATA_DIR, "context_aware_metadata.json") +        metadata_path = os.path.join(pytest.data_dir, "context_aware_metadata.json")          returned_path = _mtls_helper._check_dca_metadata_path(metadata_path)          assert returned_path is not None      def test_failure(self): -        metadata_path = os.path.join(DATA_DIR, "not_exists.json") +        metadata_path = os.path.join(pytest.data_dir, "not_exists.json")          returned_path = _mtls_helper._check_dca_metadata_path(metadata_path)          assert returned_path is None  class TestReadMetadataFile(object):      def test_success(self): -        metadata_path = os.path.join(DATA_DIR, "context_aware_metadata.json") +        metadata_path = os.path.join(pytest.data_dir, "context_aware_metadata.json")          metadata = _mtls_helper._read_dca_metadata_file(metadata_path)          assert "cert_provider_command" in metadata      def test_file_not_json(self):          # read a file which is not json format. -        metadata_path = os.path.join(DATA_DIR, "privatekey.pem") +        metadata_path = os.path.join(pytest.data_dir, "privatekey.pem")          with pytest.raises(exceptions.ClientCertError):              _mtls_helper._read_dca_metadata_file(metadata_path) diff --git a/contrib/python/google-auth/py3/tests/transport/test_grpc.py b/contrib/python/google-auth/py3/tests/transport/test_grpc.py index 05dc5fad0e6..29fae4cdf65 100644 --- a/contrib/python/google-auth/py3/tests/transport/test_grpc.py +++ b/contrib/python/google-auth/py3/tests/transport/test_grpc.py @@ -35,8 +35,8 @@ try:  except ImportError:  # pragma: NO COVER      HAS_GRPC = False -import yatest.common -DATA_DIR = os.path.join(yatest.common.test_source_path(), "data") +import yatest.common as yc +DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")  METADATA_PATH = os.path.join(DATA_DIR, "context_aware_metadata.json")  with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:      PRIVATE_KEY_BYTES = fh.read() diff --git a/contrib/python/google-auth/py3/tests/transport/test_requests.py b/contrib/python/google-auth/py3/tests/transport/test_requests.py index d9628143460..aadc1ddbfd0 100644 --- a/contrib/python/google-auth/py3/tests/transport/test_requests.py +++ b/contrib/python/google-auth/py3/tests/transport/test_requests.py @@ -545,16 +545,12 @@ class TestMutualTlsOffloadAdapter(object):          google.auth.transport._custom_tls_signer.CustomTlsSigner, "load_libraries"      )      @mock.patch.object( -        google.auth.transport._custom_tls_signer.CustomTlsSigner, "set_up_custom_key" -    ) -    @mock.patch.object(          google.auth.transport._custom_tls_signer.CustomTlsSigner,          "attach_to_ssl_context",      )      def test_success(          self,          mock_attach_to_ssl_context, -        mock_set_up_custom_key,          mock_load_libraries,          mock_proxy_manager_for,          mock_init_poolmanager, @@ -565,7 +561,6 @@ class TestMutualTlsOffloadAdapter(object):          )          mock_load_libraries.assert_called_once() -        mock_set_up_custom_key.assert_called_once()          assert mock_attach_to_ssl_context.call_count == 2          adapter.init_poolmanager() diff --git a/contrib/python/google-auth/py3/tests/ya.make b/contrib/python/google-auth/py3/tests/ya.make index e7a1b3b272f..dfcabf5bfb6 100644 --- a/contrib/python/google-auth/py3/tests/ya.make +++ b/contrib/python/google-auth/py3/tests/ya.make @@ -67,11 +67,6 @@ TEST_SRCS(      # transport/test_urllib3.py  ) -RESOURCE( -    data/privatekey.pem data/privatekey.pem -    data/public_cert.pem data/public_cert.pem -) -  NO_LINT()  END() diff --git a/contrib/python/google-auth/py3/ya.make b/contrib/python/google-auth/py3/ya.make index 77b6e5f741a..ec71907cc6b 100644 --- a/contrib/python/google-auth/py3/ya.make +++ b/contrib/python/google-auth/py3/ya.make @@ -2,7 +2,7 @@  PY3_LIBRARY() -VERSION(2.23.0) +VERSION(2.25.2)  LICENSE(Apache-2.0) @@ -10,10 +10,10 @@ PEERDIR(      contrib/python/cachetools      contrib/python/cryptography      contrib/python/grpcio +    contrib/python/packaging      contrib/python/pyasn1-modules      contrib/python/requests      contrib/python/rsa -    contrib/python/urllib3  )  NO_LINT()  | 
