From 3c09d57f60d77bf5caf85f271c2c6ed06fb848d0 Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Mon, 27 Oct 2025 16:47:37 -0700 Subject: [PATCH 01/11] feat: Autoupdate the GOOGLE_API_USE_CLIENT_CERTIFICATE flag to true if not set, if the MWID/X.509 cert sources detected Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 12 ++++++++++++ google/auth/transport/requests.py | 18 +++++++++++++----- google/auth/transport/urllib3.py | 16 +++++++++++++--- tests/transport/test__mtls_helper.py | 26 ++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 68568dd60..f0ebf18fd 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -17,6 +17,7 @@ import json import logging from os import environ, path +import os import re import subprocess @@ -405,3 +406,14 @@ def client_cert_callback(): # Then dump the decrypted key bytes return crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey) + +def check_use_client_cert_for_workload(use_client_cert): + """Checks if the workload should use client cert for mutual TLS.""" + if use_client_cert == "": + cert_path = os.getenv("GOOGLE_API_CERTIFICATE_CONFIG") + if cert_path: + with open(cert_path, "r") as f: + content = f.read() + if "workload" in content: + return True + return False diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py index 2753912c6..de954dc60 100644 --- a/google/auth/transport/requests.py +++ b/google/auth/transport/requests.py @@ -445,12 +445,20 @@ def configure_mtls_channel(self, client_cert_callback=None): creation failed for any reason. """ use_client_cert = os.getenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false" - ) + environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE) if use_client_cert != "true": - self._is_mtls = False - return - + ## Checking if the GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. + if _mtls_helper.check_use_client_cert_for_workload( + use_client_cert + ): + os.putenv( + environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "true" + ) + use_client_cert = "true" + else: + use_client_cert = "false" + self._is_mtls = False + return try: import OpenSSL except ImportError as caught_exc: diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py index 03ed75aa2..4ac84a96c 100644 --- a/google/auth/transport/urllib3.py +++ b/google/auth/transport/urllib3.py @@ -336,10 +336,20 @@ def configure_mtls_channel(self, client_cert_callback=None): creation failed for any reason. """ use_client_cert = os.getenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false" - ) + environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE) if use_client_cert != "true": - return False + ## Check if workload is present in the certificate config file + ## and GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. + if _mtls_helper.check_use_client_cert_for_workload( + use_client_cert + ): + os.putenv( + environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "true" + ) + use_client_cert = "true" + else: + use_client_cert = "false" + return False try: import OpenSSL diff --git a/tests/transport/test__mtls_helper.py b/tests/transport/test__mtls_helper.py index f6e20b726..504d0b869 100644 --- a/tests/transport/test__mtls_helper.py +++ b/tests/transport/test__mtls_helper.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json import os import re @@ -638,3 +639,28 @@ def test_crypto_error(self): _mtls_helper.decrypt_private_key( ENCRYPTED_EC_PRIVATE_KEY, b"wrong_password" ) + + def test_check_use_client_cert_for_workload(self): + use_client_cert = _mtls_helper.check_use_client_cert_for_workload("") + assert use_client_cert == False + + def test_check_use_client_cert_for_workload_with_config_file(self): + config_data = { + "version": 1, + "cert_configs": { + "workload": { + "cert_path": "path/to/cert/file", + "key_path": "path/to/key/file", + } + }, + } + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + # Use mock_open to simulate the file in memory + m = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", m): + os.environ["GOOGLE_API_CERTIFICATE_CONFIG"] = config_filename + use_client_cert = _mtls_helper.check_use_client_cert_for_workload( + "" + ) + assert use_client_cert == True From de40fef51623a4cc6124db454a932e99f2a9c3ef Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Tue, 28 Oct 2025 14:01:40 -0700 Subject: [PATCH 02/11] feat: Update the helper for use_client_cert and add support for grpc.py Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 39 +++++++++++++++++++++++---- google/auth/transport/grpc.py | 6 ++--- google/auth/transport/requests.py | 19 +++---------- google/auth/transport/urllib3.py | 19 +++---------- tests/transport/test__mtls_helper.py | 15 ++++++----- 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index f0ebf18fd..59c7630d3 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -407,13 +407,42 @@ def client_cert_callback(): # Then dump the decrypted key bytes return crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey) -def check_use_client_cert_for_workload(use_client_cert): - """Checks if the workload should use client cert for mutual TLS.""" - if use_client_cert == "": +def check_use_client_cert(): + """Returns the effective value of use_client_cert to be used. + + Returns: + str: + A boolean indicating if client certificate should be used. + The value is "true" or "false" or unset. + If unset, the function checks if the GOOGLE_API_CERTIFICATE_CONFIG + environment variable is set. If it is set, the function returns + "true" if the certificate config file contains "workload" section + and "false" otherwise. + + Raises: + ValueError: if GOOGLE_API_USE_CLIENT_CERTIFICATE is set to unsupported + value, which is not "true" or "false". + """ + use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") + ### Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. + if use_client_cert == "" or use_client_cert is None: cert_path = os.getenv("GOOGLE_API_CERTIFICATE_CONFIG") if cert_path: with open(cert_path, "r") as f: content = f.read() if "workload" in content: - return True - return False + return "true" + return "false" + else: + ### Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is set but to an + ### invalid value. + use_client_cert = use_client_cert.lower() + if use_client_cert not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" + " either `true` or `false`" + ) + else: + ### Return the value of GOOGLE_API_USE_CLIENT_CERTIFICATE which is set. + return use_client_cert + diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py index 1ebe13795..971e2db0b 100644 --- a/google/auth/transport/grpc.py +++ b/google/auth/transport/grpc.py @@ -256,8 +256,7 @@ def my_client_cert_callback(): # If SSL credentials are not explicitly set, try client_cert_callback and ADC. if not ssl_credentials: - use_client_cert = os.getenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false" + use_client_cert = _mtls_helper.check_use_client_cert() ) if use_client_cert == "true" and client_cert_callback: # Use the callback if provided. @@ -295,8 +294,7 @@ class SslCredentials: """ def __init__(self): - use_client_cert = os.getenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false" + use_client_cert = _mtls_helper.check_use_client_cert() ) if use_client_cert != "true": self._is_mtls = False diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py index de954dc60..56be8a0a9 100644 --- a/google/auth/transport/requests.py +++ b/google/auth/transport/requests.py @@ -444,21 +444,10 @@ def configure_mtls_channel(self, client_cert_callback=None): google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. """ - use_client_cert = os.getenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE) - if use_client_cert != "true": - ## Checking if the GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. - if _mtls_helper.check_use_client_cert_for_workload( - use_client_cert - ): - os.putenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "true" - ) - use_client_cert = "true" - else: - use_client_cert = "false" - self._is_mtls = False - return + use_client_cert = _mtls_helper.check_use_client_cert() + if use_client_cert != "true": + self._is_mtls = False + return try: import OpenSSL except ImportError as caught_exc: diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py index 4ac84a96c..cffddbdc9 100644 --- a/google/auth/transport/urllib3.py +++ b/google/auth/transport/urllib3.py @@ -335,22 +335,9 @@ def configure_mtls_channel(self, client_cert_callback=None): google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. """ - use_client_cert = os.getenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE) - if use_client_cert != "true": - ## Check if workload is present in the certificate config file - ## and GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. - if _mtls_helper.check_use_client_cert_for_workload( - use_client_cert - ): - os.putenv( - environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "true" - ) - use_client_cert = "true" - else: - use_client_cert = "false" - return False - + use_client_cert = _mtls_helper.check_use_client_cert() + if use_client_cert != "true": + return False try: import OpenSSL except ImportError as caught_exc: diff --git a/tests/transport/test__mtls_helper.py b/tests/transport/test__mtls_helper.py index 504d0b869..d8fabc0ab 100644 --- a/tests/transport/test__mtls_helper.py +++ b/tests/transport/test__mtls_helper.py @@ -640,9 +640,10 @@ def test_crypto_error(self): ENCRYPTED_EC_PRIVATE_KEY, b"wrong_password" ) - def test_check_use_client_cert_for_workload(self): - use_client_cert = _mtls_helper.check_use_client_cert_for_workload("") - assert use_client_cert == False + def test_check_use_client_cert(self): + os.environ["GOOGLE_API_USE_CLIENT_CERTIFICATE"] = "true" + use_client_cert = _mtls_helper.check_use_client_cert() + assert use_client_cert == "true" def test_check_use_client_cert_for_workload_with_config_file(self): config_data = { @@ -660,7 +661,7 @@ def test_check_use_client_cert_for_workload_with_config_file(self): m = mock.mock_open(read_data=config_file_content) with mock.patch("builtins.open", m): os.environ["GOOGLE_API_CERTIFICATE_CONFIG"] = config_filename - use_client_cert = _mtls_helper.check_use_client_cert_for_workload( - "" - ) - assert use_client_cert == True + os.environ["GOOGLE_API_USE_CLIENT_CERTIFICATE"] = "" + use_client_cert = _mtls_helper.check_use_client_cert() + assert use_client_cert == "true" + From 798a0f5e431c4d99400156c2510d997929706ced Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Tue, 28 Oct 2025 14:19:12 -0700 Subject: [PATCH 03/11] fix: Minor typo in grpc.py Signed-off-by: Radhika Agrawal --- google/auth/transport/grpc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py index 971e2db0b..bbe9b8ae6 100644 --- a/google/auth/transport/grpc.py +++ b/google/auth/transport/grpc.py @@ -257,7 +257,6 @@ def my_client_cert_callback(): # If SSL credentials are not explicitly set, try client_cert_callback and ADC. if not ssl_credentials: use_client_cert = _mtls_helper.check_use_client_cert() - ) if use_client_cert == "true" and client_cert_callback: # Use the callback if provided. cert, key = client_cert_callback() @@ -295,7 +294,6 @@ class SslCredentials: def __init__(self): use_client_cert = _mtls_helper.check_use_client_cert() - ) if use_client_cert != "true": self._is_mtls = False else: From 67811002754b3051065a0a88f513aff509c6c364 Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Thu, 30 Oct 2025 08:59:59 -0700 Subject: [PATCH 04/11] chore: Use json.load to read the config, instead of file.read and minor updates to docsting and indentation Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 7 ++++--- google/auth/transport/grpc.py | 6 ++++-- google/auth/transport/requests.py | 4 ++-- google/auth/transport/urllib3.py | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 59c7630d3..5d38ed26e 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -408,7 +408,7 @@ def client_cert_callback(): return crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey) def check_use_client_cert(): - """Returns the effective value of use_client_cert to be used. + """Returns whether the client certificate should to be used for mTLS. Returns: str: @@ -429,8 +429,9 @@ def check_use_client_cert(): cert_path = os.getenv("GOOGLE_API_CERTIFICATE_CONFIG") if cert_path: with open(cert_path, "r") as f: - content = f.read() - if "workload" in content: + content = json.load(f) + print("content: ", content) + if "workload" in str(content): return "true" return "false" else: diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py index bbe9b8ae6..a8a105b7c 100644 --- a/google/auth/transport/grpc.py +++ b/google/auth/transport/grpc.py @@ -241,6 +241,8 @@ def my_client_cert_callback(): Raises: google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. + google.auth.exceptions.ValueError: If invalid value is + set for `GOOGLE_API_USE_CLIENT_CERTIFICATE`, i.e. not "true" or "false". """ # Create the metadata plugin for inserting the authorization header. metadata_plugin = AuthMetadataPlugin(credentials, request) @@ -256,7 +258,7 @@ def my_client_cert_callback(): # If SSL credentials are not explicitly set, try client_cert_callback and ADC. if not ssl_credentials: - use_client_cert = _mtls_helper.check_use_client_cert() + use_client_cert = _mtls_helper.check_use_client_cert() if use_client_cert == "true" and client_cert_callback: # Use the callback if provided. cert, key = client_cert_callback() @@ -293,7 +295,7 @@ class SslCredentials: """ def __init__(self): - use_client_cert = _mtls_helper.check_use_client_cert() + use_client_cert = _mtls_helper.check_use_client_cert() if use_client_cert != "true": self._is_mtls = False else: diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py index 56be8a0a9..2b360bd01 100644 --- a/google/auth/transport/requests.py +++ b/google/auth/transport/requests.py @@ -444,8 +444,8 @@ def configure_mtls_channel(self, client_cert_callback=None): google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. """ - use_client_cert = _mtls_helper.check_use_client_cert() - if use_client_cert != "true": + use_client_cert = _mtls_helper.check_use_client_cert() + if use_client_cert != "true": self._is_mtls = False return try: diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py index cffddbdc9..e0afa4cb3 100644 --- a/google/auth/transport/urllib3.py +++ b/google/auth/transport/urllib3.py @@ -335,8 +335,8 @@ def configure_mtls_channel(self, client_cert_callback=None): google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. """ - use_client_cert = _mtls_helper.check_use_client_cert() - if use_client_cert != "true": + use_client_cert = _mtls_helper.check_use_client_cert() + if use_client_cert != "true": return False try: import OpenSSL From c33c192bae34c7e1e4b30a13fba139519db50cbe Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Thu, 30 Oct 2025 12:36:22 -0700 Subject: [PATCH 05/11] feat: Update the check_use_client_cert helper method to make use of the json and return the exact value set by user, and not handling the case when user is setting value to an unsupported value Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 5d38ed26e..b7c079be0 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -430,20 +430,10 @@ def check_use_client_cert(): if cert_path: with open(cert_path, "r") as f: content = json.load(f) - print("content: ", content) - if "workload" in str(content): + if "cert_configs" in content and "workload" in content['cert_configs']: return "true" return "false" else: - ### Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is set but to an - ### invalid value. - use_client_cert = use_client_cert.lower() - if use_client_cert not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" - " either `true` or `false`" - ) - else: ### Return the value of GOOGLE_API_USE_CLIENT_CERTIFICATE which is set. return use_client_cert From 30d75d1bf05d3da7059d8f88934b9290de9d76ae Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Thu, 30 Oct 2025 12:40:54 -0700 Subject: [PATCH 06/11] ore: Update the docstring to remove mention of ValueError exception Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 4 ---- google/auth/transport/grpc.py | 2 -- 2 files changed, 6 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index b7c079be0..be33e2e6d 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -418,10 +418,6 @@ def check_use_client_cert(): environment variable is set. If it is set, the function returns "true" if the certificate config file contains "workload" section and "false" otherwise. - - Raises: - ValueError: if GOOGLE_API_USE_CLIENT_CERTIFICATE is set to unsupported - value, which is not "true" or "false". """ use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") ### Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py index a8a105b7c..a4592e7d8 100644 --- a/google/auth/transport/grpc.py +++ b/google/auth/transport/grpc.py @@ -241,8 +241,6 @@ def my_client_cert_callback(): Raises: google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. - google.auth.exceptions.ValueError: If invalid value is - set for `GOOGLE_API_USE_CLIENT_CERTIFICATE`, i.e. not "true" or "false". """ # Create the metadata plugin for inserting the authorization header. metadata_plugin = AuthMetadataPlugin(credentials, request) From 18fc66cdbb4e81d363fc011733dcd772124c4434 Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Thu, 30 Oct 2025 12:46:54 -0700 Subject: [PATCH 07/11] fix: fix an indentation in helper method Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index be33e2e6d..7b63d210a 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -431,5 +431,5 @@ def check_use_client_cert(): return "false" else: ### Return the value of GOOGLE_API_USE_CLIENT_CERTIFICATE which is set. - return use_client_cert + return use_client_cert From a06bcb18e86d7e7b7f7657a4988283fc022511e6 Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Thu, 30 Oct 2025 15:55:23 -0700 Subject: [PATCH 08/11] fix: As both conditions are checked, added falsy check Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 7b63d210a..7030fdb8a 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -421,7 +421,7 @@ def check_use_client_cert(): """ use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") ### Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. - if use_client_cert == "" or use_client_cert is None: + if not use_client_cert: cert_path = os.getenv("GOOGLE_API_CERTIFICATE_CONFIG") if cert_path: with open(cert_path, "r") as f: From 09b6a1eeb02a134c9382b62174181339d9b02723 Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Mon, 3 Nov 2025 08:30:44 -0800 Subject: [PATCH 09/11] fix: Use monkeypatch in test to update env variable, update helper method to catch exceptions and update docstring Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 74 +++++++++++++++++---------- tests/transport/test__mtls_helper.py | 59 ++++++++++++++++++--- 2 files changed, 100 insertions(+), 33 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 7030fdb8a..cf1437d56 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -16,8 +16,7 @@ import json import logging -from os import environ, path -import os +from os import environ, path, getenv import re import subprocess @@ -407,29 +406,52 @@ def client_cert_callback(): # Then dump the decrypted key bytes return crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey) + def check_use_client_cert(): - """Returns whether the client certificate should to be used for mTLS. - - Returns: - str: - A boolean indicating if client certificate should be used. - The value is "true" or "false" or unset. - If unset, the function checks if the GOOGLE_API_CERTIFICATE_CONFIG - environment variable is set. If it is set, the function returns - "true" if the certificate config file contains "workload" section - and "false" otherwise. - """ - use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") - ### Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. - if not use_client_cert: - cert_path = os.getenv("GOOGLE_API_CERTIFICATE_CONFIG") - if cert_path: - with open(cert_path, "r") as f: - content = json.load(f) - if "cert_configs" in content and "workload" in content['cert_configs']: - return "true" - return "false" - else: - ### Return the value of GOOGLE_API_USE_CLIENT_CERTIFICATE which is set. - return use_client_cert + """Returns whether the client certificate should to be used for mTLS. + + The function checks the value of GOOGLE_API_USE_CLIENT_CERTIFICATE + environment variable, and GOOGLE_API_CERTIFICATE_CONFIG environment variable + if the former is not set. If GOOGLE_API_USE_CLIENT_CERTIFICATE is set, + this helper function returns the value of the former. If it is unset, this + helper function checks if GOOGLE_API_CERTIFICATE_CONFIG is set. If it is set, + this helper function returns "true" if the certificate config file contains + "workload" section and "false" otherwise. + Returns: + str: A string("true" or "false") indicating if client certificate should + be used. + """ + use_client_cert = getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") + # Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is set. + if use_client_cert: + return use_client_cert.lower() + else: + # Check if the value of GOOGLE_API_CERTIFICATE_CONFIG is set. + cert_path = getenv("GOOGLE_API_CERTIFICATE_CONFIG") + if cert_path: + try: + with open(cert_path, "r") as f: + content = json.load(f) + except json.JSONDecodeError: + _LOGGER.debug("JSON decode error.") + return "false" + except FileNotFoundError: + _LOGGER.debug("Certificate config file not found.") + return "false" + except OSError: + _LOGGER.debug("OS error.") + return "false" + try: + if content["cert_configs"]["workload"]: + return "true" + except KeyError: + _LOGGER.debug( + "Certificate config file content does not contain 'workload'" + " section in 'cert_configs'." + ) + return "false" + except TypeError: + _LOGGER.debug("Certificate config file content is not a JSON object.") + return "false" + return "false" diff --git a/tests/transport/test__mtls_helper.py b/tests/transport/test__mtls_helper.py index d8fabc0ab..c4959c1bc 100644 --- a/tests/transport/test__mtls_helper.py +++ b/tests/transport/test__mtls_helper.py @@ -640,12 +640,12 @@ def test_crypto_error(self): ENCRYPTED_EC_PRIVATE_KEY, b"wrong_password" ) - def test_check_use_client_cert(self): - os.environ["GOOGLE_API_USE_CLIENT_CERTIFICATE"] = "true" + def test_check_use_client_cert(self, monkeypatch): + monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "true") use_client_cert = _mtls_helper.check_use_client_cert() assert use_client_cert == "true" - def test_check_use_client_cert_for_workload_with_config_file(self): + def test_check_use_client_cert_for_workload_with_config_file(self, monkeypatch): config_data = { "version": 1, "cert_configs": { @@ -657,11 +657,56 @@ def test_check_use_client_cert_for_workload_with_config_file(self): } config_filename = "mock_certificate_config.json" config_file_content = json.dumps(config_data) + monkeypatch.setenv("GOOGLE_API_CERTIFICATE_CONFIG", config_filename) + monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") # Use mock_open to simulate the file in memory - m = mock.mock_open(read_data=config_file_content) - with mock.patch("builtins.open", m): - os.environ["GOOGLE_API_CERTIFICATE_CONFIG"] = config_filename - os.environ["GOOGLE_API_USE_CLIENT_CERTIFICATE"] = "" + mock_file_handle = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", mock_file_handle): use_client_cert = _mtls_helper.check_use_client_cert() assert use_client_cert == "true" + def test_check_use_client_cert_false(self, monkeypatch): + monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") + use_client_cert = _mtls_helper.check_use_client_cert() + assert use_client_cert == "false" + + def test_check_use_client_cert_for_workload_with_config_file_not_found( + self, monkeypatch + ): + monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") + use_client_cert = _mtls_helper.check_use_client_cert() + assert use_client_cert == "false" + + def test_check_use_client_cert_for_workload_with_config_file_not_json( + self, monkeypatch + ): + config_filename = "mock_certificate_config.json" + config_file_content = "not_valid_json" + monkeypatch.setenv("GOOGLE_API_CERTIFICATE_CONFIG", config_filename) + monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") + # Use mock_open to simulate the file in memory + mock_file_handle = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", mock_file_handle): + use_client_cert = _mtls_helper.check_use_client_cert() + assert use_client_cert == "false" + + def test_check_use_client_cert_for_workload_with_config_file_no_workload( + self, monkeypatch + ): + config_data = {"version": 1, "cert_configs": {"dummy_key": {}}} + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + monkeypatch.setenv("GOOGLE_API_CERTIFICATE_CONFIG", config_filename) + monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") + # Use mock_open to simulate the file in memory + mock_file_handle = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", mock_file_handle): + use_client_cert = _mtls_helper.check_use_client_cert() + assert use_client_cert == "false" + + def test_check_use_client_cert_when_file_does_not_exist(self, monkeypatch): + config_filename = "mock_certificate_config.json" + monkeypatch.setenv("GOOGLE_API_CERTIFICATE_CONFIG", config_filename) + monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") + use_client_cert = _mtls_helper.check_use_client_cert() + assert use_client_cert == "false" From 2a0a71a4f265009c0975476fa6ccb051b82ebca7 Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Mon, 3 Nov 2025 12:10:15 -0800 Subject: [PATCH 10/11] fix: Update the dosctring and refine the try-catch blocks Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 39 +++++++++++---------------- google/auth/transport/requests.py | 2 +- google/auth/transport/urllib3.py | 2 +- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index cf1437d56..8242ab6a0 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -16,7 +16,7 @@ import json import logging -from os import environ, path, getenv +from os import environ, getenv, path import re import subprocess @@ -408,7 +408,8 @@ def client_cert_callback(): def check_use_client_cert(): - """Returns whether the client certificate should to be used for mTLS. + """Returns the value of the GOOGLE_API_USE_CLIENT_CERTIFICATE variable, + or an inferred 'true' or 'false' value if unset. The function checks the value of GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable, and GOOGLE_API_CERTIFICATE_CONFIG environment variable @@ -419,8 +420,9 @@ def check_use_client_cert(): "workload" section and "false" otherwise. Returns: - str: A string("true" or "false") indicating if client certificate should - be used. + str: A string("true" or "false" or value of the + GOOGLE_API_USE_CLIENT_CERTIFICATE variable set) indicating if client + certificate should be used. """ use_client_cert = getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") # Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is set. @@ -433,25 +435,16 @@ def check_use_client_cert(): try: with open(cert_path, "r") as f: content = json.load(f) - except json.JSONDecodeError: - _LOGGER.debug("JSON decode error.") - return "false" - except FileNotFoundError: - _LOGGER.debug("Certificate config file not found.") - return "false" - except OSError: - _LOGGER.debug("OS error.") - return "false" - try: - if content["cert_configs"]["workload"]: + # verify json has workload key + content["cert_configs"]["workload"] return "true" - except KeyError: - _LOGGER.debug( - "Certificate config file content does not contain 'workload'" - " section in 'cert_configs'." - ) - return "false" - except TypeError: - _LOGGER.debug("Certificate config file content is not a JSON object.") + except ( + FileNotFoundError, + OSError, + KeyError, + TypeError, + json.JSONDecodeError, + ) as e: + _LOGGER.debug("error decoding certificate: %s", e) return "false" return "false" diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py index 2b360bd01..34fd46e91 100644 --- a/google/auth/transport/requests.py +++ b/google/auth/transport/requests.py @@ -444,7 +444,7 @@ def configure_mtls_channel(self, client_cert_callback=None): google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. """ - use_client_cert = _mtls_helper.check_use_client_cert() + use_client_cert = google.auth.transport._mtls_helper.check_use_client_cert() if use_client_cert != "true": self._is_mtls = False return diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py index e0afa4cb3..850e5ccd7 100644 --- a/google/auth/transport/urllib3.py +++ b/google/auth/transport/urllib3.py @@ -335,7 +335,7 @@ def configure_mtls_channel(self, client_cert_callback=None): google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel creation failed for any reason. """ - use_client_cert = _mtls_helper.check_use_client_cert() + use_client_cert = transport._mtls_helper.check_use_client_cert() if use_client_cert != "true": return False try: From 63e678fb38260ff3ae0b9e1ee543ceb06df1c3ed Mon Sep 17 00:00:00 2001 From: Radhika Agrawal Date: Mon, 3 Nov 2025 14:27:51 -0800 Subject: [PATCH 11/11] fix: docstring update and remove unsed imports Signed-off-by: Radhika Agrawal --- google/auth/transport/_mtls_helper.py | 25 +++++++++++++------------ google/auth/transport/grpc.py | 2 -- google/auth/transport/requests.py | 2 -- google/auth/transport/urllib3.py | 2 -- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py index 8242ab6a0..5ad105a52 100644 --- a/google/auth/transport/_mtls_helper.py +++ b/google/auth/transport/_mtls_helper.py @@ -409,20 +409,22 @@ def client_cert_callback(): def check_use_client_cert(): """Returns the value of the GOOGLE_API_USE_CLIENT_CERTIFICATE variable, - or an inferred 'true' or 'false' value if unset. + or an inferred value('true' or 'false') if unset. - The function checks the value of GOOGLE_API_USE_CLIENT_CERTIFICATE - environment variable, and GOOGLE_API_CERTIFICATE_CONFIG environment variable - if the former is not set. If GOOGLE_API_USE_CLIENT_CERTIFICATE is set, - this helper function returns the value of the former. If it is unset, this - helper function checks if GOOGLE_API_CERTIFICATE_CONFIG is set. If it is set, - this helper function returns "true" if the certificate config file contains - "workload" section and "false" otherwise. + This value is meant to be interpreted as a "true" or "false" value + representing whether the client certificate should be used, but could be any + arbitrary string. + + If GOOGLE_API_USE_CLIENT_CERTIFICATE is unset, the value value will be + inferred by reading a file pointed at by GOOGLE_API_CERTIFICATE_CONFIG, and + verifying it contains a "workload" section. If so, the function will return + "true", otherwise "false". Returns: - str: A string("true" or "false" or value of the - GOOGLE_API_USE_CLIENT_CERTIFICATE variable set) indicating if client - certificate should be used. + str: The value of GOOGLE_API_USE_CLIENT_CERTIFICATE, or an inferred value + ("true" or "false") if unset. This string should contain a value, but may + be an any arbitrary string read from the user's set + GOOGLE_API_USE_CLIENT_CERTIFICATE. """ use_client_cert = getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") # Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is set. @@ -446,5 +448,4 @@ def check_use_client_cert(): json.JSONDecodeError, ) as e: _LOGGER.debug("error decoding certificate: %s", e) - return "false" return "false" diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py index a4592e7d8..747c1dcb2 100644 --- a/google/auth/transport/grpc.py +++ b/google/auth/transport/grpc.py @@ -17,9 +17,7 @@ from __future__ import absolute_import import logging -import os -from google.auth import environment_vars from google.auth import exceptions from google.auth.transport import _mtls_helper from google.oauth2 import service_account diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py index 34fd46e91..9e1f15751 100644 --- a/google/auth/transport/requests.py +++ b/google/auth/transport/requests.py @@ -19,7 +19,6 @@ import functools import logging import numbers -import os import time try: @@ -35,7 +34,6 @@ ) # pylint: disable=ungrouped-imports from google.auth import _helpers -from google.auth import environment_vars from google.auth import exceptions from google.auth import transport import google.auth.transport._mtls_helper diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py index 850e5ccd7..01be1dd05 100644 --- a/google/auth/transport/urllib3.py +++ b/google/auth/transport/urllib3.py @@ -17,7 +17,6 @@ from __future__ import absolute_import import logging -import os import warnings # Certifi is Mozilla's certificate bundle. Urllib3 needs a certificate bundle @@ -51,7 +50,6 @@ from google.auth import _helpers -from google.auth import environment_vars from google.auth import exceptions from google.auth import transport from google.oauth2 import service_account