Skip to content

Commit 1535ecc

Browse files
feat: Add public wrapper for _mtls_helper.check_use_client_cert which enables mTLS if GOOGLE_API_USE_CLIENT_CERTIFICATE is not set, when the MWID/X.509 cert sources detected (#1859)
Add public wrapper for check_use_client_cert which enables mTLS if GOOGLE_API_USE_CLIENT_CERTIFICATE is not set, when the MWID/X.509 cert sources detected. Also, fix check_use_client_cert to return boolean value. Change #1848 added the check_use_client_cert method that helps know if client cert should be used for mTLS connection. However, that was in a private class, thus, created a public wrapper of the same function so that it can be used by python Client Libraries. Also, updated check_use_client_cert to return a boolean value instead of existing string value for better readability and future scope. --------- Signed-off-by: Radhika Agrawal <agrawalradhika@google.com> Co-authored-by: Daniel Sanche <d.sanche14@gmail.com>
1 parent 54502a7 commit 1535ecc

File tree

7 files changed

+55
-29
lines changed

7 files changed

+55
-29
lines changed

google/auth/transport/_mtls_helper.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -408,28 +408,23 @@ def client_cert_callback():
408408

409409

410410
def check_use_client_cert():
411-
"""Returns the value of the GOOGLE_API_USE_CLIENT_CERTIFICATE variable,
412-
or an inferred value('true' or 'false') if unset.
411+
"""Returns boolean for whether the client certificate should be used for mTLS.
413412
414-
This value is meant to be interpreted as a "true" or "false" value
415-
representing whether the client certificate should be used, but could be any
416-
arbitrary string.
417-
418-
If GOOGLE_API_USE_CLIENT_CERTIFICATE is unset, the value value will be
419-
inferred by reading a file pointed at by GOOGLE_API_CERTIFICATE_CONFIG, and
420-
verifying it contains a "workload" section. If so, the function will return
421-
"true", otherwise "false".
413+
If GOOGLE_API_USE_CLIENT_CERTIFICATE is set to true or false, a corresponding
414+
bool value will be returned. If the value is set to an unexpected string, it
415+
will default to False.
416+
If GOOGLE_API_USE_CLIENT_CERTIFICATE is unset, the value will be inferred
417+
by reading a file pointed at by GOOGLE_API_CERTIFICATE_CONFIG, and verifying
418+
it contains a "workload" section. If so, the function will return True,
419+
otherwise False.
422420
423421
Returns:
424-
str: The value of GOOGLE_API_USE_CLIENT_CERTIFICATE, or an inferred value
425-
("true" or "false") if unset. This string should contain a value, but may
426-
be an any arbitrary string read from the user's set
427-
GOOGLE_API_USE_CLIENT_CERTIFICATE.
422+
bool: Whether the client certificate should be used for mTLS connection.
428423
"""
429424
use_client_cert = getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE")
430425
# Check if the value of GOOGLE_API_USE_CLIENT_CERTIFICATE is set.
431426
if use_client_cert:
432-
return use_client_cert.lower()
427+
return use_client_cert.lower() == "true"
433428
else:
434429
# Check if the value of GOOGLE_API_CERTIFICATE_CONFIG is set.
435430
cert_path = getenv("GOOGLE_API_CERTIFICATE_CONFIG")
@@ -439,7 +434,7 @@ def check_use_client_cert():
439434
content = json.load(f)
440435
# verify json has workload key
441436
content["cert_configs"]["workload"]
442-
return "true"
437+
return True
443438
except (
444439
FileNotFoundError,
445440
OSError,
@@ -448,4 +443,4 @@ def check_use_client_cert():
448443
json.JSONDecodeError,
449444
) as e:
450445
_LOGGER.debug("error decoding certificate: %s", e)
451-
return "false"
446+
return False

google/auth/transport/grpc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,13 @@ def my_client_cert_callback():
255255
# If SSL credentials are not explicitly set, try client_cert_callback and ADC.
256256
if not ssl_credentials:
257257
use_client_cert = _mtls_helper.check_use_client_cert()
258-
if use_client_cert == "true" and client_cert_callback:
258+
if use_client_cert and client_cert_callback:
259259
# Use the callback if provided.
260260
cert, key = client_cert_callback()
261261
ssl_credentials = grpc.ssl_channel_credentials(
262262
certificate_chain=cert, private_key=key
263263
)
264-
elif use_client_cert == "true":
264+
elif use_client_cert:
265265
# Use application default SSL credentials.
266266
adc_ssl_credentils = SslCredentials()
267267
ssl_credentials = adc_ssl_credentils.ssl_credentials
@@ -292,7 +292,7 @@ class SslCredentials:
292292

293293
def __init__(self):
294294
use_client_cert = _mtls_helper.check_use_client_cert()
295-
if use_client_cert != "true":
295+
if not use_client_cert:
296296
self._is_mtls = False
297297
else:
298298
# Load client SSL credentials.

google/auth/transport/mtls.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,20 @@ def callback():
110110
return cert_path, key_path, passphrase_bytes
111111

112112
return callback
113+
114+
115+
def should_use_client_cert():
116+
"""Returns boolean for whether the client certificate should be used for mTLS.
117+
118+
This is a wrapper around _mtls_helper.check_use_client_cert().
119+
If GOOGLE_API_USE_CLIENT_CERTIFICATE is set to true or false, a corresponding
120+
bool value will be returned
121+
If GOOGLE_API_USE_CLIENT_CERTIFICATE is unset, the value will be inferred by
122+
reading a file pointed at by GOOGLE_API_CERTIFICATE_CONFIG, and verifying it
123+
contains a "workload" section. If so, the function will return True,
124+
otherwise False.
125+
126+
Returns:
127+
bool: indicating whether the client certificate should be used for mTLS.
128+
"""
129+
return _mtls_helper.check_use_client_cert()

google/auth/transport/requests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ def configure_mtls_channel(self, client_cert_callback=None):
443443
creation failed for any reason.
444444
"""
445445
use_client_cert = google.auth.transport._mtls_helper.check_use_client_cert()
446-
if use_client_cert != "true":
446+
if not use_client_cert:
447447
self._is_mtls = False
448448
return
449449
try:

google/auth/transport/urllib3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ def configure_mtls_channel(self, client_cert_callback=None):
334334
creation failed for any reason.
335335
"""
336336
use_client_cert = transport._mtls_helper.check_use_client_cert()
337-
if use_client_cert != "true":
337+
if not use_client_cert:
338338
return False
339339
try:
340340
import OpenSSL

tests/transport/test__mtls_helper.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ def test_crypto_error(self):
643643
def test_check_use_client_cert(self, monkeypatch):
644644
monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "true")
645645
use_client_cert = _mtls_helper.check_use_client_cert()
646-
assert use_client_cert == "true"
646+
assert use_client_cert is True
647647

648648
def test_check_use_client_cert_for_workload_with_config_file(self, monkeypatch):
649649
config_data = {
@@ -663,19 +663,24 @@ def test_check_use_client_cert_for_workload_with_config_file(self, monkeypatch):
663663
mock_file_handle = mock.mock_open(read_data=config_file_content)
664664
with mock.patch("builtins.open", mock_file_handle):
665665
use_client_cert = _mtls_helper.check_use_client_cert()
666-
assert use_client_cert == "true"
666+
assert use_client_cert is True
667667

668668
def test_check_use_client_cert_false(self, monkeypatch):
669669
monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
670670
use_client_cert = _mtls_helper.check_use_client_cert()
671-
assert use_client_cert == "false"
671+
assert use_client_cert is False
672+
673+
def test_check_use_client_cert_unsupported_value(self, monkeypatch):
674+
monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "dummy")
675+
use_client_cert = _mtls_helper.check_use_client_cert()
676+
assert use_client_cert is False
672677

673678
def test_check_use_client_cert_for_workload_with_config_file_not_found(
674679
self, monkeypatch
675680
):
676681
monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "")
677682
use_client_cert = _mtls_helper.check_use_client_cert()
678-
assert use_client_cert == "false"
683+
assert use_client_cert is False
679684

680685
def test_check_use_client_cert_for_workload_with_config_file_not_json(
681686
self, monkeypatch
@@ -688,7 +693,7 @@ def test_check_use_client_cert_for_workload_with_config_file_not_json(
688693
mock_file_handle = mock.mock_open(read_data=config_file_content)
689694
with mock.patch("builtins.open", mock_file_handle):
690695
use_client_cert = _mtls_helper.check_use_client_cert()
691-
assert use_client_cert == "false"
696+
assert use_client_cert is False
692697

693698
def test_check_use_client_cert_for_workload_with_config_file_no_workload(
694699
self, monkeypatch
@@ -702,11 +707,11 @@ def test_check_use_client_cert_for_workload_with_config_file_no_workload(
702707
mock_file_handle = mock.mock_open(read_data=config_file_content)
703708
with mock.patch("builtins.open", mock_file_handle):
704709
use_client_cert = _mtls_helper.check_use_client_cert()
705-
assert use_client_cert == "false"
710+
assert use_client_cert is False
706711

707712
def test_check_use_client_cert_when_file_does_not_exist(self, monkeypatch):
708713
config_filename = "mock_certificate_config.json"
709714
monkeypatch.setenv("GOOGLE_API_CERTIFICATE_CONFIG", config_filename)
710715
monkeypatch.setenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "")
711716
use_client_cert = _mtls_helper.check_use_client_cert()
712-
assert use_client_cert == "false"
717+
assert use_client_cert is False

tests/transport/test_mtls.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,12 @@ def test_default_client_encrypted_cert_source(
9494
callback = mtls.default_client_encrypted_cert_source("cert_path", "key_path")
9595
with pytest.raises(exceptions.MutualTLSChannelError):
9696
callback()
97+
98+
99+
@mock.patch("google.auth.transport._mtls_helper.check_use_client_cert", autospec=True)
100+
def test_should_use_client_cert(check_use_client_cert):
101+
check_use_client_cert.return_value = mock.Mock()
102+
assert mtls.should_use_client_cert()
103+
104+
check_use_client_cert.return_value = False
105+
assert not mtls.should_use_client_cert()

0 commit comments

Comments
 (0)