feat: add GOOGLE_API_USE_CLIENT_CERTIFICATE support (#592)
diff --git a/docs/reference/google.auth.transport.mtls.rst b/docs/reference/google.auth.transport.mtls.rst
new file mode 100644
index 0000000..11b50e2
--- /dev/null
+++ b/docs/reference/google.auth.transport.mtls.rst
@@ -0,0 +1,7 @@
+google.auth.transport.mtls module
+=================================
+
+.. automodule:: google.auth.transport.mtls
+ :members:
+ :inherited-members:
+ :show-inheritance:
diff --git a/google/auth/environment_vars.py b/google/auth/environment_vars.py
index 9c1367f..46a8926 100644
--- a/google/auth/environment_vars.py
+++ b/google/auth/environment_vars.py
@@ -53,3 +53,9 @@
GCE_METADATA_IP = "GCE_METADATA_IP"
"""Environment variable providing an alternate ip:port to be used for ip-only
GCE metadata requests."""
+
+GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"
+"""Environment variable controlling whether to use client certificate or not.
+
+The default value is false. Users have to explicitly set this value to true
+in order to use client certificate to establish a mutual TLS channel."""
diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py
index 13234a3..ab7d0db 100644
--- a/google/auth/transport/grpc.py
+++ b/google/auth/transport/grpc.py
@@ -17,9 +17,11 @@
from __future__ import absolute_import
import logging
+import os
import six
+from google.auth import environment_vars
from google.auth import exceptions
from google.auth.transport import _mtls_helper
@@ -96,6 +98,9 @@
This creates a channel with SSL and :class:`AuthMetadataPlugin`. This
channel can be used to create a stub that can make authorized requests.
+ Users can configure client certificate or rely on device certificates to
+ establish a mutual TLS channel, if the `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+ variable is explicitly set to `true`.
Example::
@@ -138,7 +143,9 @@
ssl_credentials=regular_ssl_credentials)
Option 2: create a mutual TLS channel by calling a callback which returns
- the client side certificate and the key::
+ the client side certificate and the key (Note that
+ `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly
+ set to `true`)::
def my_client_cert_callback():
code_to_load_client_cert_and_key()
@@ -155,7 +162,9 @@
Option 3: use application default SSL credentials. It searches and uses
the command in a context aware metadata file, which is available on devices
- with endpoint verification support.
+ with endpoint verification support (Note that
+ `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly
+ set to `true`).
See https://cloud.google.com/endpoint-verification/docs/overview::
try:
@@ -174,7 +183,8 @@
ssl_credentials=default_ssl_credentials)
Option 4: not setting ssl_credentials and client_cert_callback. For devices
- without endpoint verification support, a regular TLS channel is created;
+ without endpoint verification support or `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+ environment variable is not `true`, a regular TLS channel is created;
otherwise, a mutual TLS channel is created, however, the call should be
wrapped in a try/except block in case of malformed context aware metadata.
@@ -205,13 +215,15 @@
This argument is mutually exclusive with client_cert_callback;
providing both will raise an exception.
If ssl_credentials and client_cert_callback are None, application
- default SSL credentials will be used.
+ default SSL credentials are used if `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+ environment variable is explicitly set to `true`, otherwise one way TLS
+ SSL credentials are used.
client_cert_callback (Callable[[], (bytes, bytes)]): Optional
callback function to obtain client certicate and key for mutual TLS
connection. This argument is mutually exclusive with
ssl_credentials; providing both will raise an exception.
- If ssl_credentials and client_cert_callback are None, application
- default SSL credentials will be used.
+ This argument does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+ environment variable is explicitly set to `true`.
kwargs: Additional arguments to pass to :func:`grpc.secure_channel`.
Returns:
@@ -235,16 +247,21 @@
# If SSL credentials are not explicitly set, try client_cert_callback and ADC.
if not ssl_credentials:
- if client_cert_callback:
+ use_client_cert = os.getenv(
+ environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false"
+ )
+ if use_client_cert == "true" and client_cert_callback:
# Use the callback if provided.
cert, key = client_cert_callback()
ssl_credentials = grpc.ssl_channel_credentials(
certificate_chain=cert, private_key=key
)
- else:
+ elif use_client_cert == "true":
# Use application default SSL credentials.
adc_ssl_credentils = SslCredentials()
ssl_credentials = adc_ssl_credentils.ssl_credentials
+ else:
+ ssl_credentials = grpc.ssl_channel_credentials()
# Combine the ssl credentials and the authorization credentials.
composite_credentials = grpc.composite_channel_credentials(
@@ -257,17 +274,29 @@
class SslCredentials:
"""Class for application default SSL credentials.
- For devices with endpoint verification support, a device certificate will be
- automatically loaded and mutual TLS will be established.
+ The behavior is controlled by `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment
+ variable whose default value is `false`. Client certificate will not be used
+ unless the environment variable is explicitly set to `true`. See
+ https://google.aip.dev/auth/4114
+
+ If the environment variable is `true`, then for devices with endpoint verification
+ support, a device certificate will be automatically loaded and mutual TLS will
+ be established.
See https://cloud.google.com/endpoint-verification/docs/overview.
"""
def __init__(self):
- # Load client SSL credentials.
- metadata_path = _mtls_helper._check_dca_metadata_path(
- _mtls_helper.CONTEXT_AWARE_METADATA_PATH
+ use_client_cert = os.getenv(
+ environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false"
)
- self._is_mtls = metadata_path is not None
+ if use_client_cert != "true":
+ self._is_mtls = False
+ else:
+ # Load client SSL credentials.
+ metadata_path = _mtls_helper._check_dca_metadata_path(
+ _mtls_helper.CONTEXT_AWARE_METADATA_PATH
+ )
+ self._is_mtls = metadata_path is not None
@property
def ssl_credentials(self):
diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py
index 4f5af7d..9a2f3af 100644
--- a/google/auth/transport/requests.py
+++ b/google/auth/transport/requests.py
@@ -19,6 +19,7 @@
import functools
import logging
import numbers
+import os
import time
try:
@@ -40,6 +41,7 @@
) # pylint: disable=ungrouped-imports
import six # pylint: disable=ungrouped-imports
+from google.auth import environment_vars
from google.auth import exceptions
from google.auth import transport
import google.auth.transport._mtls_helper
@@ -249,13 +251,18 @@
credentials' headers to the request and refreshing credentials as needed.
This class also supports mutual TLS via :meth:`configure_mtls_channel`
- method. If client_cert_callback is provided, client certificate and private
+ method. In order to use this method, the `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+ environment variable must be explicitly set to `true`, otherwise it does
+ nothing. Assume the environment is set to `true`, the method behaves in the
+ following manner:
+ If client_cert_callback is provided, client certificate and private
key are loaded using the callback; if client_cert_callback is None,
application default SSL credentials will be used. Exceptions are raised if
there are problems with the certificate, private key, or the loading process,
so it should be called within a try/except block.
- First we create an :class:`AuthorizedSession` instance and specify the endpoints::
+ First we set the environment variable to `true`, then create an :class:`AuthorizedSession`
+ instance and specify the endpoints::
regular_endpoint = 'https://pubsub.googleapis.com/v1/projects/{my_project_id}/topics'
mtls_endpoint = 'https://pubsub.mtls.googleapis.com/v1/projects/{my_project_id}/topics'
@@ -343,9 +350,11 @@
def configure_mtls_channel(self, client_cert_callback=None):
"""Configure the client certificate and key for SSL connection.
- If client certificate and key are successfully obtained (from the given
- client_cert_callback or from application default SSL credentials), a
- :class:`_MutualTlsAdapter` instance will be mounted to "https://" prefix.
+ The function does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE` is
+ explicitly set to `true`. In this case if client certificate and key are
+ successfully obtained (from the given client_cert_callback or from application
+ default SSL credentials), a :class:`_MutualTlsAdapter` instance will be mounted
+ to "https://" prefix.
Args:
client_cert_callback (Optional[Callable[[], (bytes, bytes)]]):
@@ -358,6 +367,13 @@
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, "false"
+ )
+ 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 3742f1a..209fc51 100644
--- a/google/auth/transport/urllib3.py
+++ b/google/auth/transport/urllib3.py
@@ -17,6 +17,7 @@
from __future__ import absolute_import
import logging
+import os
import warnings
# Certifi is Mozilla's certificate bundle. Urllib3 needs a certificate bundle
@@ -45,6 +46,7 @@
import six
import urllib3.exceptions # pylint: disable=ungrouped-imports
+from google.auth import environment_vars
from google.auth import exceptions
from google.auth import transport
@@ -202,13 +204,18 @@
credentials' headers to the request and refreshing credentials as needed.
This class also supports mutual TLS via :meth:`configure_mtls_channel`
- method. If client_cert_callback is provided, client certificate and private
+ method. In order to use this method, the `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+ environment variable must be explicitly set to `true`, otherwise it does
+ nothing. Assume the environment is set to `true`, the method behaves in the
+ following manner:
+ If client_cert_callback is provided, client certificate and private
key are loaded using the callback; if client_cert_callback is None,
application default SSL credentials will be used. Exceptions are raised if
there are problems with the certificate, private key, or the loading process,
so it should be called within a try/except block.
- First we create an :class:`AuthorizedHttp` instance and specify the endpoints::
+ First we set the environment variable to `true`, then create an :class:`AuthorizedHttp`
+ instance and specify the endpoints::
regular_endpoint = 'https://pubsub.googleapis.com/v1/projects/{my_project_id}/topics'
mtls_endpoint = 'https://pubsub.mtls.googleapis.com/v1/projects/{my_project_id}/topics'
@@ -282,9 +289,13 @@
def configure_mtls_channel(self, client_cert_callback=None):
"""Configures mutual TLS channel using the given client_cert_callback or
- application default SSL credentials. Returns True if the channel is
- mutual TLS and False otherwise. Note that the `http` provided in the
- constructor will be overwritten.
+ application default SSL credentials. The behavior is controlled by
+ `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable.
+ (1) If the environment variable value is `true`, the function returns True
+ if the channel is mutual TLS and False otherwise. The `http` provided
+ in the constructor will be overwritten.
+ (2) If the environment variable is not set or `false`, the function does
+ nothing and it always return False.
Args:
client_cert_callback (Optional[Callable[[], (bytes, bytes)]]):
@@ -300,6 +311,12 @@
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, "false"
+ )
+ if use_client_cert != "true":
+ return False
+
try:
import OpenSSL
except ImportError as caught_exc:
diff --git a/system_tests/test_mtls_http.py b/system_tests/test_mtls_http.py
index 4a6a9c4..7c56496 100644
--- a/system_tests/test_mtls_http.py
+++ b/system_tests/test_mtls_http.py
@@ -18,6 +18,7 @@
import google.auth
import google.auth.credentials
+from google.auth import environment_vars
from google.auth.transport import mtls
import google.auth.transport.requests
import google.auth.transport.urllib3
@@ -33,7 +34,8 @@
)
authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
- authed_session.configure_mtls_channel()
+ with mock.patch.dict(os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}):
+ authed_session.configure_mtls_channel()
# If the devices has default client cert source, then a mutual TLS channel
# is supposed to be created.
@@ -57,7 +59,8 @@
)
authed_http = google.auth.transport.urllib3.AuthorizedHttp(credentials)
- is_mtls = authed_http.configure_mtls_channel()
+ with mock.patch.dict(os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}):
+ is_mtls = authed_http.configure_mtls_channel()
# If the devices has default client cert source, then a mutual TLS channel
# is supposed to be created.
@@ -83,9 +86,10 @@
authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
if mtls.has_default_client_cert_source():
- authed_session.configure_mtls_channel(
- client_cert_callback=mtls.default_client_cert_source()
- )
+ with mock.patch.dict(os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}):
+ authed_session.configure_mtls_channel(
+ client_cert_callback=mtls.default_client_cert_source()
+ )
assert authed_session.is_mtls
@@ -105,9 +109,10 @@
authed_http = google.auth.transport.urllib3.AuthorizedHttp(credentials)
if mtls.has_default_client_cert_source():
- assert authed_http.configure_mtls_channel(
- client_cert_callback=mtls.default_client_cert_source()
- )
+ with mock.patch.dict(os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}):
+ assert authed_http.configure_mtls_channel(
+ client_cert_callback=mtls.default_client_cert_source()
+ )
# Sleep 1 second to avoid 503 error.
time.sleep(1)
diff --git a/tests/transport/test_grpc.py b/tests/transport/test_grpc.py
index ef2e2e2..39f8b11 100644
--- a/tests/transport/test_grpc.py
+++ b/tests/transport/test_grpc.py
@@ -21,6 +21,7 @@
from google.auth import _helpers
from google.auth import credentials
+from google.auth import environment_vars
from google.auth import exceptions
from google.auth import transport
@@ -139,9 +140,13 @@
None,
)
- channel = google.auth.transport.grpc.secure_authorized_channel(
- credentials, request, target, options=mock.sentinel.options
- )
+ channel = None
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ channel = google.auth.transport.grpc.secure_authorized_channel(
+ credentials, request, target, options=mock.sentinel.options
+ )
# Check the auth plugin construction.
auth_plugin = metadata_call_credentials.call_args[0][0]
@@ -167,6 +172,49 @@
)
assert channel == secure_channel.return_value
+ @mock.patch("google.auth.transport.grpc.SslCredentials", autospec=True)
+ def test_secure_authorized_channel_adc_without_client_cert_env(
+ self,
+ ssl_credentials_adc_method,
+ secure_channel,
+ ssl_channel_credentials,
+ metadata_call_credentials,
+ composite_channel_credentials,
+ get_client_ssl_credentials,
+ ):
+ # Test client cert won't be used if GOOGLE_API_USE_CLIENT_CERTIFICATE
+ # environment variable is not set.
+ credentials = CredentialsStub()
+ request = mock.create_autospec(transport.Request)
+ target = "example.com:80"
+
+ channel = google.auth.transport.grpc.secure_authorized_channel(
+ credentials, request, target, options=mock.sentinel.options
+ )
+
+ # Check the auth plugin construction.
+ auth_plugin = metadata_call_credentials.call_args[0][0]
+ assert isinstance(auth_plugin, google.auth.transport.grpc.AuthMetadataPlugin)
+ assert auth_plugin._credentials == credentials
+ assert auth_plugin._request == request
+
+ # Check the ssl channel call.
+ ssl_channel_credentials.assert_called_once()
+ ssl_credentials_adc_method.assert_not_called()
+
+ # Check the composite credentials call.
+ composite_channel_credentials.assert_called_once_with(
+ ssl_channel_credentials.return_value, metadata_call_credentials.return_value
+ )
+
+ # Check the channel call.
+ secure_channel.assert_called_once_with(
+ target,
+ composite_channel_credentials.return_value,
+ options=mock.sentinel.options,
+ )
+ assert channel == secure_channel.return_value
+
def test_secure_authorized_channel_explicit_ssl(
self,
secure_channel,
@@ -233,9 +281,12 @@
client_cert_callback = mock.Mock()
client_cert_callback.return_value = (PUBLIC_CERT_BYTES, PRIVATE_KEY_BYTES)
- google.auth.transport.grpc.secure_authorized_channel(
- credentials, request, target, client_cert_callback=client_cert_callback
- )
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ google.auth.transport.grpc.secure_authorized_channel(
+ credentials, request, target, client_cert_callback=client_cert_callback
+ )
client_cert_callback.assert_called_once()
@@ -273,12 +324,48 @@
client_cert_callback.side_effect = Exception("callback exception")
with pytest.raises(Exception) as excinfo:
- google.auth.transport.grpc.secure_authorized_channel(
- credentials, request, target, client_cert_callback=client_cert_callback
- )
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ google.auth.transport.grpc.secure_authorized_channel(
+ credentials,
+ request,
+ target,
+ client_cert_callback=client_cert_callback,
+ )
assert str(excinfo.value) == "callback exception"
+ def test_secure_authorized_channel_cert_callback_without_client_cert_env(
+ self,
+ secure_channel,
+ ssl_channel_credentials,
+ metadata_call_credentials,
+ composite_channel_credentials,
+ get_client_ssl_credentials,
+ ):
+ # Test client cert won't be used if GOOGLE_API_USE_CLIENT_CERTIFICATE
+ # environment variable is not set.
+ credentials = mock.Mock()
+ request = mock.Mock()
+ target = "example.com:80"
+ client_cert_callback = mock.Mock()
+
+ google.auth.transport.grpc.secure_authorized_channel(
+ credentials, request, target, client_cert_callback=client_cert_callback
+ )
+
+ # Check client_cert_callback is not called because GOOGLE_API_USE_CLIENT_CERTIFICATE
+ # is not set.
+ client_cert_callback.assert_not_called()
+
+ ssl_channel_credentials.assert_called_once()
+
+ # Check the composite credentials call.
+ composite_channel_credentials.assert_called_once_with(
+ ssl_channel_credentials.return_value, metadata_call_credentials.return_value
+ )
+
@mock.patch("grpc.ssl_channel_credentials", autospec=True)
@mock.patch(
@@ -299,7 +386,10 @@
# Mock that the metadata file doesn't exist.
mock_check_dca_metadata_path.return_value = None
- ssl_credentials = google.auth.transport.grpc.SslCredentials()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ ssl_credentials = google.auth.transport.grpc.SslCredentials()
# Since no context aware metadata is found, we wouldn't call
# get_client_ssl_credentials, and the SSL channel credentials created is
@@ -325,7 +415,10 @@
mock_get_client_ssl_credentials.side_effect = exceptions.ClientCertError()
with pytest.raises(exceptions.MutualTLSChannelError):
- assert google.auth.transport.grpc.SslCredentials().ssl_credentials
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ assert google.auth.transport.grpc.SslCredentials().ssl_credentials
def test_get_client_ssl_credentials_success(
self,
@@ -345,7 +438,10 @@
None,
)
- ssl_credentials = google.auth.transport.grpc.SslCredentials()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ ssl_credentials = google.auth.transport.grpc.SslCredentials()
assert ssl_credentials.ssl_credentials is not None
assert ssl_credentials.is_mtls
@@ -353,3 +449,20 @@
mock_ssl_channel_credentials.assert_called_once_with(
certificate_chain=PUBLIC_CERT_BYTES, private_key=PRIVATE_KEY_BYTES
)
+
+ def test_get_client_ssl_credentials_without_client_cert_env(
+ self,
+ mock_check_dca_metadata_path,
+ mock_read_dca_metadata_file,
+ mock_get_client_ssl_credentials,
+ mock_ssl_channel_credentials,
+ ):
+ # Test client cert won't be used if GOOGLE_API_USE_CLIENT_CERTIFICATE is not set.
+ ssl_credentials = google.auth.transport.grpc.SslCredentials()
+
+ assert ssl_credentials.ssl_credentials is not None
+ assert not ssl_credentials.is_mtls
+ mock_check_dca_metadata_path.assert_not_called()
+ mock_read_dca_metadata_file.assert_not_called()
+ mock_get_client_ssl_credentials.assert_not_called()
+ mock_ssl_channel_credentials.assert_called_once()
diff --git a/tests/transport/test_requests.py b/tests/transport/test_requests.py
index 7ac55ce..d56c2be 100644
--- a/tests/transport/test_requests.py
+++ b/tests/transport/test_requests.py
@@ -14,6 +14,7 @@
import datetime
import functools
+import os
import sys
import freezegun
@@ -24,6 +25,7 @@
import requests.adapters
from six.moves import http_client
+from google.auth import environment_vars
from google.auth import exceptions
import google.auth.credentials
import google.auth.transport._mtls_helper
@@ -380,7 +382,10 @@
auth_session = google.auth.transport.requests.AuthorizedSession(
credentials=mock.Mock()
)
- auth_session.configure_mtls_channel(mock_callback)
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ auth_session.configure_mtls_channel(mock_callback)
assert auth_session.is_mtls
assert isinstance(
@@ -401,7 +406,10 @@
auth_session = google.auth.transport.requests.AuthorizedSession(
credentials=mock.Mock()
)
- auth_session.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ auth_session.configure_mtls_channel()
assert auth_session.is_mtls
assert isinstance(
@@ -421,7 +429,10 @@
auth_session = google.auth.transport.requests.AuthorizedSession(
credentials=mock.Mock()
)
- auth_session.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ auth_session.configure_mtls_channel()
assert not auth_session.is_mtls
@@ -438,10 +449,38 @@
credentials=mock.Mock()
)
with pytest.raises(exceptions.MutualTLSChannelError):
- auth_session.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ auth_session.configure_mtls_channel()
mock_get_client_cert_and_key.return_value = (False, None, None)
with mock.patch.dict("sys.modules"):
sys.modules["OpenSSL"] = None
with pytest.raises(exceptions.MutualTLSChannelError):
- auth_session.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ,
+ {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"},
+ ):
+ auth_session.configure_mtls_channel()
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
+ )
+ def test_configure_mtls_channel_without_client_cert_env(
+ self, get_client_cert_and_key
+ ):
+ # Test client cert won't be used if GOOGLE_API_USE_CLIENT_CERTIFICATE
+ # environment variable is not set.
+ auth_session = google.auth.transport.requests.AuthorizedSession(
+ credentials=mock.Mock()
+ )
+
+ auth_session.configure_mtls_channel()
+ assert not auth_session.is_mtls
+ get_client_cert_and_key.assert_not_called()
+
+ mock_callback = mock.Mock()
+ auth_session.configure_mtls_channel(mock_callback)
+ assert not auth_session.is_mtls
+ mock_callback.assert_not_called()
diff --git a/tests/transport/test_urllib3.py b/tests/transport/test_urllib3.py
index 3158b92..29561f6 100644
--- a/tests/transport/test_urllib3.py
+++ b/tests/transport/test_urllib3.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import os
import sys
import mock
@@ -20,6 +21,7 @@
from six.moves import http_client
import urllib3
+from google.auth import environment_vars
from google.auth import exceptions
import google.auth.credentials
import google.auth.transport._mtls_helper
@@ -179,7 +181,10 @@
)
with pytest.warns(UserWarning):
- is_mtls = authed_http.configure_mtls_channel(callback)
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ is_mtls = authed_http.configure_mtls_channel(callback)
assert is_mtls
mock_make_mutual_tls_http.assert_called_once_with(
@@ -202,7 +207,10 @@
pytest.public_cert_bytes,
pytest.private_key_bytes,
)
- is_mtls = authed_http.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ is_mtls = authed_http.configure_mtls_channel()
assert is_mtls
mock_get_client_cert_and_key.assert_called_once()
@@ -222,7 +230,10 @@
)
mock_get_client_cert_and_key.return_value = (False, None, None)
- is_mtls = authed_http.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ is_mtls = authed_http.configure_mtls_channel()
assert not is_mtls
mock_get_client_cert_and_key.assert_called_once()
@@ -238,10 +249,39 @@
mock_get_client_cert_and_key.side_effect = exceptions.ClientCertError()
with pytest.raises(exceptions.MutualTLSChannelError):
- authed_http.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ, {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"}
+ ):
+ authed_http.configure_mtls_channel()
mock_get_client_cert_and_key.return_value = (False, None, None)
with mock.patch.dict("sys.modules"):
sys.modules["OpenSSL"] = None
with pytest.raises(exceptions.MutualTLSChannelError):
- authed_http.configure_mtls_channel()
+ with mock.patch.dict(
+ os.environ,
+ {environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE: "true"},
+ ):
+ authed_http.configure_mtls_channel()
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
+ )
+ def test_configure_mtls_channel_without_client_cert_env(
+ self, get_client_cert_and_key
+ ):
+ callback = mock.Mock()
+
+ authed_http = google.auth.transport.urllib3.AuthorizedHttp(
+ credentials=mock.Mock(), http=mock.Mock()
+ )
+
+ # Test the callback is not called if GOOGLE_API_USE_CLIENT_CERTIFICATE is not set.
+ is_mtls = authed_http.configure_mtls_channel(callback)
+ assert not is_mtls
+ callback.assert_not_called()
+
+ # Test ADC client cert is not used if GOOGLE_API_USE_CLIENT_CERTIFICATE is not set.
+ is_mtls = authed_http.configure_mtls_channel(callback)
+ assert not is_mtls
+ get_client_cert_and_key.assert_not_called()