Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ falling back to the local implementation if not present. #}
{# TODO(https://github.com/googleapis/google-cloud-python/issues/17883):
Backfill compatibility functions being removed from the client layer. #}

import os

from typing import Optional
from urllib.parse import urlparse, urlunparse

from google.auth.exceptions import MutualTLSChannelError
from google.auth.transport import mtls
from google.api_core.universe import EmptyUniverseError

{% if has_auto_populated_fields %}
Expand Down Expand Up @@ -142,6 +145,21 @@ def get_universe_domain(
raise EmptyUniverseError()
return resolved

def use_client_cert_effective() -> bool:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mtls implementation is called should_use_client_cert. Should we use the same name here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed updated name

"""Returns whether client certificate should be used for mTLS."""
if hasattr(mtls, "should_use_client_cert"):
return mtls.should_use_client_cert()
else:

@daniel-sanche daniel-sanche Jul 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the pattern you were using for this file was to try importing from the helper library, and provide the backup implementation if there's an ImportError. Do you plan to use hasattr for everything instead now?

use_client_cert_str = os.getenv(
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
).lower()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mtls implementation is a bit different, using two env vars. Should we try to match here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed matched the implementation

if use_client_cert_str not in ("true", "false"):
raise ValueError(
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
" either `true` or `false`"
)
return use_client_cert_str == "true"

{% if has_auto_populated_fields %}

def setup_request_id(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ from google.api_core import exceptions as core_exceptions
from google.api_core import extended_operation
{% endif %}
from google.api_core import gapic_v1
from {{package_path}}._compat import get_universe_domain, get_api_endpoint, get_default_mtls_endpoint
from {{package_path}}._compat import get_universe_domain, get_api_endpoint, get_default_mtls_endpoint, use_client_cert_effective
{% if has_auto_populated_fields %}
from {{package_path}}._compat import setup_request_id
{% endif %}
Expand Down Expand Up @@ -156,32 +156,6 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):

_DEFAULT_UNIVERSE = "googleapis.com"

@staticmethod
def _use_client_cert_effective():
"""Returns whether client certificate should be used for mTLS if the
google-auth version supports should_use_client_cert automatic mTLS enablement.

Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.

Returns:
bool: whether client certificate should be used for mTLS
Raises:
ValueError: (If using a version of google-auth without should_use_client_cert and
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
"""
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
return mtls.should_use_client_cert()
else: # pragma: NO COVER
# if unsupported, fallback to reading from env var
use_client_cert_str = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false").lower()
if use_client_cert_str not in ("true", "false"):
raise ValueError(
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
" either `true` or `false`"
)
return use_client_cert_str == "true"

@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
Expand Down Expand Up @@ -296,7 +270,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
DeprecationWarning)
if client_options is None:
client_options = client_options_lib.ClientOptions()
use_client_cert = {{ service.client_name }}._use_client_cert_effective()
use_client_cert = use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError("Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`")
Expand Down Expand Up @@ -333,7 +307,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
is not any of ["auto", "never", "always"].
"""
use_client_cert = {{ service.client_name }}._use_client_cert_effective()
use_client_cert = use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
if use_mtls_endpoint not in ("auto", "never", "always"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,90 +212,6 @@ def test__read_environment_variables():
assert {{ service.client_name }}._read_environment_variables() == (False, "auto", "foo.com")


def test_use_client_cert_effective():
# Test case 1: Test when `should_use_client_cert` returns True.
# We mock the `should_use_client_cert` function to simulate a scenario where
# the google-auth library supports automatic mTLS and determines that a
# client certificate should be used.
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch("google.auth.transport.mtls.should_use_client_cert", return_value=True):
assert {{ service.client_name }}._use_client_cert_effective() is True

# Test case 2: Test when `should_use_client_cert` returns False.
# We mock the `should_use_client_cert` function to simulate a scenario where
# the google-auth library supports automatic mTLS and determines that a
# client certificate should NOT be used.
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch("google.auth.transport.mtls.should_use_client_cert", return_value=False):
assert {{ service.client_name }}._use_client_cert_effective() is False

# Test case 3: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true".
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
assert {{ service.client_name }}._use_client_cert_effective() is True

# Test case 4: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false".
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
assert {{ service.client_name }}._use_client_cert_effective() is False

# Test case 5: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True".
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}):
assert {{ service.client_name }}._use_client_cert_effective() is True

# Test case 6: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False".
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"}):
assert {{ service.client_name }}._use_client_cert_effective() is False

# Test case 7: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE".
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}):
assert {{ service.client_name }}._use_client_cert_effective() is True

# Test case 8: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE".
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"}):
assert {{ service.client_name }}._use_client_cert_effective() is False

# Test case 9: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set.
# In this case, the method should return False, which is the default value.
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, clear=True):
assert {{ service.client_name }}._use_client_cert_effective() is False

# Test case 10: Test when `should_use_client_cert` is unavailable and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
# The method should raise a ValueError as the environment variable must be either
# "true" or "false".
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}):
with pytest.raises(ValueError):
{{ service.client_name }}._use_client_cert_effective()

# Test case 11: Test when `should_use_client_cert` is available and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
# The method should return False as the environment variable is set to an invalid value.
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}):
assert {{ service.client_name }}._use_client_cert_effective() is False

# Test case 12: Test when `should_use_client_cert` is available and the
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also,
# the GOOGLE_API_CONFIG environment variable is unset.
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}):
with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}):
assert {{ service.client_name }}._use_client_cert_effective() is False

def test__get_client_cert_source():
mock_provided_cert_source = mock.Mock()
mock_default_cert_source = mock.Mock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Backfill compatibility functions tests being removed from the client layer. #}

import re
import pytest
import os

{% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %}
from {{package_path}} import _compat as universe
Expand All @@ -20,6 +21,8 @@ from {{package_path}}._compat import setup_request_id
{% endif %}

from google.auth.exceptions import MutualTLSChannelError
import google.auth.transport.mtls
from unittest import mock


def test_get_universe_domain():
Expand Down Expand Up @@ -218,6 +221,33 @@ def test_get_api_endpoint(
)


def test_use_client_cert_effective_with_should_use_client_cert():
mock_mtls = mock.Mock()
mock_mtls.should_use_client_cert.return_value = True
with mock.patch(f"{universe.__name__}.mtls", mock_mtls):
assert universe.use_client_cert_effective() is True

mock_mtls.should_use_client_cert.return_value = False
with mock.patch(f"{universe.__name__}.mtls", mock_mtls):
assert universe.use_client_cert_effective() is False


def test_use_client_cert_effective_fallback_env():
mock_mtls = mock.Mock(spec=[])
with mock.patch(f"{universe.__name__}.mtls", mock_mtls):
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
assert universe.use_client_cert_effective() is True

with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
assert universe.use_client_cert_effective() is False

with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "invalid"}):
with pytest.raises(
ValueError,
match="Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`",
):
universe.use_client_cert_effective()

{% if has_auto_populated_fields %}

class MockRequest:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
#
"""A compatibility module for older versions of google-api-core."""

import os

from typing import Optional
from urllib.parse import urlparse, urlunparse

from google.auth.exceptions import MutualTLSChannelError
from google.auth.transport import mtls
from google.api_core.universe import EmptyUniverseError


Expand Down Expand Up @@ -138,3 +141,18 @@ def get_universe_domain(
if not resolved:
raise EmptyUniverseError()
return resolved

def use_client_cert_effective() -> bool:
"""Returns whether client certificate should be used for mTLS."""
if hasattr(mtls, "should_use_client_cert"):
return mtls.should_use_client_cert()
else:
use_client_cert_str = os.getenv(
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
).lower()
if use_client_cert_str not in ("true", "false"):
raise ValueError(
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
" either `true` or `false`"
)
return use_client_cert_str == "true"
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from google.api_core import client_options as client_options_lib
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
from google.cloud.asset_v1._compat import get_universe_domain, get_api_endpoint, get_default_mtls_endpoint
from google.cloud.asset_v1._compat import get_universe_domain, get_api_endpoint, get_default_mtls_endpoint, use_client_cert_effective
from google.api_core import retry as retries
from google.auth import credentials as ga_credentials # type: ignore
from google.auth.transport import mtls # type: ignore
Expand Down Expand Up @@ -108,32 +108,6 @@ class AssetServiceClient(metaclass=AssetServiceClientMeta):
_DEFAULT_ENDPOINT_TEMPLATE = "cloudasset.{UNIVERSE_DOMAIN}"
_DEFAULT_UNIVERSE = "googleapis.com"

@staticmethod
def _use_client_cert_effective():
"""Returns whether client certificate should be used for mTLS if the
google-auth version supports should_use_client_cert automatic mTLS enablement.

Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.

Returns:
bool: whether client certificate should be used for mTLS
Raises:
ValueError: (If using a version of google-auth without should_use_client_cert and
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
"""
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
return mtls.should_use_client_cert()
else: # pragma: NO COVER
# if unsupported, fallback to reading from env var
use_client_cert_str = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false").lower()
if use_client_cert_str not in ("true", "false"):
raise ValueError(
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
" either `true` or `false`"
)
return use_client_cert_str == "true"

@classmethod
def from_service_account_info(cls, info: dict, *args, **kwargs):
"""Creates an instance of this client using the provided credentials
Expand Down Expand Up @@ -351,7 +325,7 @@ def get_mtls_endpoint_and_cert_source(cls, client_options: Optional[client_optio
DeprecationWarning)
if client_options is None:
client_options = client_options_lib.ClientOptions()
use_client_cert = AssetServiceClient._use_client_cert_effective()
use_client_cert = use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
if use_mtls_endpoint not in ("auto", "never", "always"):
raise MutualTLSChannelError("Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`")
Expand Down Expand Up @@ -388,7 +362,7 @@ def _read_environment_variables():
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
is not any of ["auto", "never", "always"].
"""
use_client_cert = AssetServiceClient._use_client_cert_effective()
use_client_cert = use_client_cert_effective()
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
if use_mtls_endpoint not in ("auto", "never", "always"):
Expand Down
Loading
Loading