Skip to content

Commit

Permalink
feat: consolidate mTLS channel errors (#480)
Browse files Browse the repository at this point in the history
feat: consolidate mTLS channel errors
  • Loading branch information
arithmetic1728 authored Apr 1, 2020
1 parent 506c565 commit e83d446
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 55 deletions.
4 changes: 4 additions & 0 deletions google/auth/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ class UserAccessTokenError(GoogleAuthError):

class DefaultCredentialsError(GoogleAuthError):
"""Used to indicate that acquiring default credentials failed."""


class MutualTLSChannelError(GoogleAuthError):
"""Used to indicate that mutual TLS channel creation is failed."""
39 changes: 16 additions & 23 deletions google/auth/transport/grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import six

from google.auth import exceptions
from google.auth.transport import _mtls_helper

try:
Expand Down Expand Up @@ -217,17 +218,8 @@ def my_client_cert_callback():
grpc.Channel: The created gRPC channel.
Raises:
OSError: If the cert provider command launch fails during the application
default SSL credentials loading process on devices with endpoint
verification support.
RuntimeError: If the cert provider command has a runtime error during the
application default SSL credentials loading process on devices with
endpoint verification support.
ValueError:
If the context aware metadata file is malformed or if the cert provider
command doesn't produce both client certificate and key during the
application default SSL credentials loading process on devices with
endpoint verification support.
google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
creation failed for any reason.
"""
# Create the metadata plugin for inserting the authorization header.
metadata_plugin = AuthMetadataPlugin(credentials, request)
Expand Down Expand Up @@ -293,20 +285,21 @@ def ssl_credentials(self):
grpc.ChannelCredentials: The created grpc channel credentials.
Raises:
OSError: If the cert provider command launch fails.
RuntimeError: If the cert provider command has a runtime error.
ValueError:
If the context aware metadata file is malformed or if the cert provider
command doesn't produce both the client certificate and key.
google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
creation failed for any reason.
"""
if self._context_aware_metadata_path:
metadata = _mtls_helper._read_dca_metadata_file(
self._context_aware_metadata_path
)
cert, key = _mtls_helper.get_client_ssl_credentials(metadata)
self._ssl_credentials = grpc.ssl_channel_credentials(
certificate_chain=cert, private_key=key
)
try:
metadata = _mtls_helper._read_dca_metadata_file(
self._context_aware_metadata_path
)
cert, key = _mtls_helper.get_client_ssl_credentials(metadata)
self._ssl_credentials = grpc.ssl_channel_credentials(
certificate_chain=cert, private_key=key
)
except (OSError, RuntimeError, ValueError) as caught_exc:
new_exc = exceptions.MutualTLSChannelError(caught_exc)
six.raise_from(new_exc, caught_exc)
else:
self._ssl_credentials = grpc.ssl_channel_credentials()

Expand Down
39 changes: 24 additions & 15 deletions google/auth/transport/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,23 +355,32 @@ def configure_mtls_channel(self, client_cert_callback=None):
will be used.
Raises:
ImportError: If certifi or pyOpenSSL is not installed.
OpenSSL.crypto.Error: If client cert or key is invalid.
OSError: If the cert provider command launch fails during the
application default SSL credentials loading process.
RuntimeError: If the cert provider command has a runtime error during
the application default SSL credentials loading process.
ValueError: If the context aware metadata file is malformed or the
cert provider command doesn't produce both client certicate and
key during the application default SSL credentials loading process.
google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
creation failed for any reason.
"""
self._is_mtls, cert, key = google.auth.transport._mtls_helper.get_client_cert_and_key(
client_cert_callback
)
try:
import OpenSSL
except ImportError as caught_exc:
new_exc = exceptions.MutualTLSChannelError(caught_exc)
six.raise_from(new_exc, caught_exc)

if self._is_mtls:
mtls_adapter = _MutualTlsAdapter(cert, key)
self.mount("https://", mtls_adapter)
try:
self._is_mtls, cert, key = google.auth.transport._mtls_helper.get_client_cert_and_key(
client_cert_callback
)

if self._is_mtls:
mtls_adapter = _MutualTlsAdapter(cert, key)
self.mount("https://", mtls_adapter)
except (
ImportError,
OpenSSL.crypto.Error,
OSError,
RuntimeError,
ValueError,
) as caught_exc:
new_exc = exceptions.MutualTLSChannelError(caught_exc)
six.raise_from(new_exc, caught_exc)

def request(
self,
Expand Down
41 changes: 25 additions & 16 deletions google/auth/transport/urllib3.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,24 +297,33 @@ def configure_mtls_channel(self, client_cert_callabck=None):
True if the channel is mutual TLS and False otherwise.
Raises:
ImportError: If certifi or pyOpenSSL is not installed.
OpenSSL.crypto.Error: If client cert or key is invalid.
OSError: If the cert provider command launch fails during the
application default SSL credentials loading process.
RuntimeError: If the cert provider command has a runtime error during
the application default SSL credentials loading process.
ValueError: If the context aware metadata file is malformed or the
cert provider command doesn't produce both client certicate and
key during the application default SSL credentials loading process.
google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
creation failed for any reason.
"""
found_cert_key, cert, key = transport._mtls_helper.get_client_cert_and_key(
client_cert_callabck
)
try:
import OpenSSL
except ImportError as caught_exc:
new_exc = exceptions.MutualTLSChannelError(caught_exc)
six.raise_from(new_exc, caught_exc)

if found_cert_key:
self.http = _make_mutual_tls_http(cert, key)
else:
self.http = _make_default_http()
try:
found_cert_key, cert, key = transport._mtls_helper.get_client_cert_and_key(
client_cert_callabck
)

if found_cert_key:
self.http = _make_mutual_tls_http(cert, key)
else:
self.http = _make_default_http()
except (
ImportError,
OpenSSL.crypto.Error,
OSError,
RuntimeError,
ValueError,
) as caught_exc:
new_exc = exceptions.MutualTLSChannelError(caught_exc)
six.raise_from(new_exc, caught_exc)

if self._has_user_provided_http:
self._has_user_provided_http = False
Expand Down
3 changes: 2 additions & 1 deletion tests/transport/test_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from google.auth import _helpers
from google.auth import credentials
from google.auth import exceptions
from google.auth import transport

try:
Expand Down Expand Up @@ -315,7 +316,7 @@ def test_get_client_ssl_credentials_failure(
# Mock that client cert and key are not loaded and exception is raised.
mock_get_client_ssl_credentials.side_effect = ValueError()

with pytest.raises(ValueError):
with pytest.raises(exceptions.MutualTLSChannelError):
assert google.auth.transport.grpc.SslCredentials().ssl_credentials

def test_get_client_ssl_credentials_success(
Expand Down
20 changes: 20 additions & 0 deletions tests/transport/test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import datetime
import functools
import sys

import freezegun
import mock
Expand All @@ -23,6 +24,7 @@
import requests.adapters
from six.moves import http_client

from google.auth import exceptions
import google.auth.credentials
import google.auth.transport._mtls_helper
import google.auth.transport.requests
Expand Down Expand Up @@ -414,3 +416,21 @@ def test_configure_mtls_channel_non_mtls(

# Assert _MutualTlsAdapter constructor is not called.
mock_adapter_ctor.assert_not_called()

@mock.patch(
"google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
)
def test_configure_mtls_channel_exceptions(self, mock_get_client_cert_and_key):
mock_get_client_cert_and_key.side_effect = ValueError()

auth_session = google.auth.transport.requests.AuthorizedSession(
credentials=mock.Mock()
)
with pytest.raises(exceptions.MutualTLSChannelError):
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()
21 changes: 21 additions & 0 deletions tests/transport/test_urllib3.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import sys

import mock
import OpenSSL
import pytest
from six.moves import http_client
import urllib3

from google.auth import exceptions
import google.auth.credentials
import google.auth.transport._mtls_helper
import google.auth.transport.urllib3
Expand Down Expand Up @@ -221,3 +224,21 @@ def test_configure_mtls_channel_non_mtls(
assert not is_mtls
mock_get_client_cert_and_key.assert_called_once()
mock_make_mutual_tls_http.assert_not_called()

@mock.patch(
"google.auth.transport._mtls_helper.get_client_cert_and_key", autospec=True
)
def test_configure_mtls_channel_exceptions(self, mock_get_client_cert_and_key):
authed_http = google.auth.transport.urllib3.AuthorizedHttp(
credentials=mock.Mock()
)

mock_get_client_cert_and_key.side_effect = ValueError()
with pytest.raises(exceptions.MutualTLSChannelError):
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()

0 comments on commit e83d446

Please sign in to comment.