From 92c4429308d387dbd3bcfa1dec3f673da321083a Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Tue, 25 Feb 2025 14:40:17 -0800 Subject: [PATCH 1/6] SSL --- docs/advanced/iron_python.rst | 4 +- shotgun_api3/shotgun.py | 107 +++++----------------------------- tests/test_api.py | 85 +++++---------------------- tests/test_client.py | 12 ++-- 4 files changed, 32 insertions(+), 176 deletions(-) diff --git a/docs/advanced/iron_python.rst b/docs/advanced/iron_python.rst index 62ad6d79..a0176207 100644 --- a/docs/advanced/iron_python.rst +++ b/docs/advanced/iron_python.rst @@ -22,9 +22,7 @@ v3.0.20 can be used with IronPython with a little bit of added work: - If you encounter any SSL errors like ``unknown field: SERIALNUMBER=0123456789`` or ``:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed``. - For now you can workaround this problem by disabling ssl certificate validation which we've - encountered some intermittent issues with. Set ``NO_SSL_VALIDATION = True`` for either case. - See :const:`shotgun_api3.shotgun.NO_SSL_VALIDATION` + Inject you own CA cert <> - If you encounter ``LookupError: unknown encoding: idna``, you can force utf-8 by changing iri2uri.py ~ln 71 from ``authority = authority.encode('idna')`` to diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index be7c2d46..1eccfa2b 100644 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -78,14 +78,6 @@ SHOTGUN_API_DISABLE_ENTITY_OPTIMIZATION = False -NO_SSL_VALIDATION = False -""" -Turns off hostname matching validation for SSL certificates - -Sometimes there are cases where certificate validation should be disabled. For example, if you -have a self-signed internal certificate that isn't included in our certificate bundle, you may -not require the added security provided by enforcing this. -""" # ---------------------------------------------------------------------------- # Version @@ -351,12 +343,11 @@ def __init__(self): self.py_version = ".".join(str(x) for x in sys.version_info[:2]) - # extract the OpenSSL version if we can. The version is only available in Python 2.7 and - # only if we successfully imported ssl + # extract the OpenSSL version if we can. self.ssl_version = "unknown" try: self.ssl_version = ssl.OPENSSL_VERSION - except (AttributeError, NameError): + except AttributeError: pass def __str__(self): @@ -425,7 +416,6 @@ def __init__(self, sg): self.proxy_pass = None self.session_token = None self.authorization = None - self.no_ssl_validation = False self.localized = False def set_server_params(self, base_url): @@ -616,7 +606,6 @@ def __init__( self.config.session_token = session_token self.config.sudo_as_login = sudo_as_login self.config.convert_datetimes_to_utc = convert_datetimes_to_utc - self.config.no_ssl_validation = NO_SSL_VALIDATION self.config.raw_http_proxy = http_proxy try: @@ -2264,14 +2253,10 @@ def reset_user_agent(self): ua_platform = self.client_caps.platform.capitalize() # create ssl validation string based on settings - validation_str = "validate" - if self.config.no_ssl_validation: - validation_str = "no-validate" - self._user_agents = [ - "shotgun-json (%s)" % __version__, - "Python %s (%s)" % (self.client_caps.py_version, ua_platform), - "ssl %s (%s)" % (self.client_caps.ssl_version, validation_str), + f"shotgun-json ({__version__})", + f"Python {self.client_caps.py_version} ({ua_platform})", + f"ssl {self.client_caps.ssl_version}", ] def set_session_uuid(self, session_uuid): @@ -3543,8 +3528,14 @@ def _build_opener(self, handler): Build urllib2 opener with appropriate proxy handler. """ handlers = [] - if self.__ca_certs and not NO_SSL_VALIDATION: - handlers.append(CACertsHTTPSHandler(self.__ca_certs)) + if self.__ca_certs: + handlers.append( + urllib.request.HTTPSHandler( + context=ssl.create_default_context( + cafile=self.__ca_certs, + ), + ), + ) if self.config.proxy_handler: handlers.append(self.config.proxy_handler) @@ -3613,23 +3604,6 @@ def _get_certs_file(cls, ca_certs): cert_file = os.path.join(cur_dir, "lib", "certifi", "cacert.pem") return cert_file - def _turn_off_ssl_validation(self): - """ - Turn off SSL certificate validation. - """ - global NO_SSL_VALIDATION - self.config.no_ssl_validation = True - NO_SSL_VALIDATION = True - # reset ssl-validation in user-agents - self._user_agents = [ - ( - "ssl %s (no-validate)" % self.client_caps.ssl_version - if ua.startswith("ssl ") - else ua - ) - for ua in self._user_agents - ] - # Deprecated methods from old wrapper def schema(self, entity_type): """ @@ -3832,59 +3806,6 @@ def _make_call(self, verb, path, body, headers): attempt += 1 try: return self._http_request(verb, path, body, req_headers) - except ssl.SSLEOFError as e: - # SG-34910 - EOF occurred in violation of protocol (_ssl.c:2426) - # This issue seems to be related to proxy and keep alive. - # It looks like, sometimes, the proxy drops the connection on - # the TCP/TLS level despites the keep-alive. So we need to close - # the connection and make a new attempt. - LOG.debug("SSLEOFError: {}".format(e)) - self._close_connection() - if attempt == max_rpc_attempts: - LOG.debug("Request failed. Giving up after %d attempts." % attempt) - raise - # This is the exact same block as the "except Exception" bellow. - # We need to do it here because the next except will match it - # otherwise and will not re-attempt. - # When we drop support of Python 2 and we will probably drop the - # next except, we might want to remove this except too. - except (ssl.SSLError, ssl.CertificateError) as e: - # Test whether the exception is due to the fact that this is an older version of - # Python that cannot validate certificates encrypted with SHA-2. If it is, then - # fall back on disabling the certificate validation and try again - unless the - # SHOTGUN_FORCE_CERTIFICATE_VALIDATION environment variable has been set by the - # user. In that case we simply raise the exception. Any other exceptions simply - # get raised as well. - # - # For more info see: - # https://www.shotgridsoftware.com/blog/important-ssl-certificate-renewal-and-sha-2/ - # - # SHA-2 errors look like this: - # [Errno 1] _ssl.c:480: error:0D0C50A1:asn1 encoding routines:ASN1_item_verify: - # unknown message digest algorithm - # - # Any other exceptions simply get raised. - if ( - "unknown message digest algorithm" not in str(e) - or "SHOTGUN_FORCE_CERTIFICATE_VALIDATION" in os.environ - ): - raise - - if self.config.no_ssl_validation is False: - LOG.warning( - "SSL Error: this Python installation is incompatible with " - "certificates signed with SHA-2. Disabling certificate validation. " - "For more information, see https://www.shotgridsoftware.com/blog/" - "important-ssl-certificate-renewal-and-sha-2/" - ) - self._turn_off_ssl_validation() - # reload user agent to reflect that we have turned off ssl validation - req_headers["user-agent"] = "; ".join(self._user_agents) - - self._close_connection() - if attempt == max_rpc_attempts: - LOG.debug("Request failed. Giving up after %d attempts." % attempt) - raise except Exception: self._close_connection() if attempt == max_rpc_attempts: @@ -4143,14 +4064,12 @@ def _get_connection(self): timeout=self.config.timeout_secs, ca_certs=self.__ca_certs, proxy_info=pi, - disable_ssl_certificate_validation=self.config.no_ssl_validation, ) else: self._connection = Http( timeout=self.config.timeout_secs, ca_certs=self.__ca_certs, proxy_info=None, - disable_ssl_certificate_validation=self.config.no_ssl_validation, ) return self._connection diff --git a/tests/test_api.py b/tests/test_api.py index 0302dfe0..8808e812 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -288,6 +288,10 @@ def test_upload_to_sg(self, mock_send_form): "attachments", tag_list="monkeys, everywhere, send, help", ) +<<<<<<< HEAD +======= + +>>>>>>> 413afc6 (SSL) mock_send_form.assert_called_once() mock_send_form_args, _ = mock_send_form.call_args display_name_to_send = mock_send_form_args[1].get("display_name", "") @@ -311,7 +315,11 @@ def test_upload_to_sg(self, mock_send_form): display_name_to_send.startswith("b'") and display_name_to_send.endswith("'") ) +<<<<<<< HEAD mock_send_form.reset_mock() +======= + # mock_send_form.method.assert_called_once() ## CANOT work because was already called earlier.... +>>>>>>> 413afc6 (SSL) mock_send_form.return_value = "2\nIt can't be upload" self.assertRaises( shotgun_api3.ShotgunError, @@ -693,6 +701,10 @@ def test_share_thumbnail_not_ready(self, mock_send_form): def test_share_thumbnail_returns_error(self, mock_send_form): """throw an exception if server returns an error code""" +<<<<<<< HEAD +======= + # mock_send_form.method.assert_called_once() # never worked..... +>>>>>>> 413afc6 (SSL) mock_send_form.return_value = "1\nerror message.\n" self.assertRaises( @@ -2248,78 +2260,7 @@ def my_side_effect2(*args, **kwargs): finally: self.sg.config.rpc_attempt_interval = bak_rpc_attempt_interval - @unittest.mock.patch("shotgun_api3.shotgun.Http.request") - def test_sha2_error(self, mock_request): - # Simulate the exception raised with SHA-2 errors - mock_request.side_effect = ssl.SSLError( - "[Errno 1] _ssl.c:480: error:0D0C50A1:asn1 " - "encoding routines:ASN1_item_verify: unknown message digest " - "algorithm" - ) - - # save the original state - original_env_val = os.environ.pop("SHOTGUN_FORCE_CERTIFICATE_VALIDATION", None) - - # ensure we're starting with the right values - self.sg.reset_user_agent() - - # ensure the initial settings are correct. These will be different depending on whether - # the ssl module imported successfully or not. - if "ssl" in sys.modules: - self.assertFalse(self.sg.config.no_ssl_validation) - self.assertFalse(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertTrue("(validate)" in " ".join(self.sg._user_agents)) - self.assertFalse("(no-validate)" in " ".join(self.sg._user_agents)) - else: - self.assertTrue(self.sg.config.no_ssl_validation) - self.assertTrue(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertFalse("(validate)" in " ".join(self.sg._user_agents)) - self.assertTrue("(no-validate)" in " ".join(self.sg._user_agents)) - - try: - self.sg.info() - except ssl.SSLError: - # ensure the api has reset the values in the correct fallback behavior - self.assertTrue(self.sg.config.no_ssl_validation) - self.assertTrue(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertFalse("(validate)" in " ".join(self.sg._user_agents)) - self.assertTrue("(no-validate)" in " ".join(self.sg._user_agents)) - - if original_env_val is not None: - os.environ["SHOTGUN_FORCE_CERTIFICATE_VALIDATION"] = original_env_val - - @unittest.mock.patch("shotgun_api3.shotgun.Http.request") - def test_sha2_error_with_strict(self, mock_request): - # Simulate the exception raised with SHA-2 errors - mock_request.side_effect = ssl.SSLError( - "[Errno 1] _ssl.c:480: error:0D0C50A1:asn1 " - "encoding routines:ASN1_item_verify: unknown message digest " - "algorithm" - ) - - # save the original state - original_env_val = os.environ.pop("SHOTGUN_FORCE_CERTIFICATE_VALIDATION", None) - os.environ["SHOTGUN_FORCE_CERTIFICATE_VALIDATION"] = "1" - - # ensure we're starting with the right values - self.sg.config.no_ssl_validation = False - shotgun_api3.shotgun.NO_SSL_VALIDATION = False - self.sg.reset_user_agent() - - try: - self.sg.info() - except ssl.SSLError: - # ensure the api has NOT reset the values in the fallback behavior because we have - # set the env variable to force validation - self.assertFalse(self.sg.config.no_ssl_validation) - self.assertFalse(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertFalse("(no-validate)" in " ".join(self.sg._user_agents)) - self.assertTrue("(validate)" in " ".join(self.sg._user_agents)) - - if original_env_val is not None: - os.environ["SHOTGUN_FORCE_CERTIFICATE_VALIDATION"] = original_env_val - - @unittest.mock.patch.object(urllib.request.OpenerDirector, "open") + @unittest.mock.patch.object(urllib.request.OpenerDirector, 'open') def test_sanitized_auth_params(self, mock_open): # Simulate the server blowing up and giving us a 500 error mock_open.side_effect = urllib.error.HTTPError("url", 500, "message", {}, None) diff --git a/tests/test_client.py b/tests/test_client.py index 43766d2d..ae4ae030 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -29,7 +29,8 @@ import shotgun_api3.lib.httplib2 as httplib2 import shotgun_api3 as api from shotgun_api3.shotgun import ServerCapabilities, SG_TIMEZONE -from . import base +import shotgun_api3 as api +import shotgun_api3.lib.httplib2 as httplib2 def b64encode(val): @@ -269,12 +270,11 @@ def test_user_agent(self): args, _ = self.sg._http_request.call_args (_, _, _, headers) = args ssl_validate_lut = {True: "no-validate", False: "validate"} - expected = "shotgun-json (%s); Python %s (%s); ssl %s (%s)" % ( + expected = "shotgun-json (%s); Python %s (%s); ssl %s" % ( api.__version__, client_caps.py_version, client_caps.platform.capitalize(), client_caps.ssl_version, - ssl_validate_lut[config.no_ssl_validation], ) self.assertEqual(expected, headers.get("user-agent")) @@ -283,12 +283,11 @@ def test_user_agent(self): self.sg.info() args, _ = self.sg._http_request.call_args (_, _, _, headers) = args - expected = "shotgun-json (%s); Python %s (%s); ssl %s (%s); test-agent" % ( + expected = "shotgun-json (%s); Python %s (%s); ssl %s; test-agent" % ( api.__version__, client_caps.py_version, client_caps.platform.capitalize(), client_caps.ssl_version, - ssl_validate_lut[config.no_ssl_validation], ) self.assertEqual(expected, headers.get("user-agent")) @@ -297,12 +296,11 @@ def test_user_agent(self): self.sg.info() args, _ = self.sg._http_request.call_args (_, _, _, headers) = args - expected = "shotgun-json (%s); Python %s (%s); ssl %s (%s)" % ( + expected = "shotgun-json (%s); Python %s (%s); ssl %s" % ( api.__version__, client_caps.py_version, client_caps.platform.capitalize(), client_caps.ssl_version, - ssl_validate_lut[config.no_ssl_validation], ) self.assertEqual(expected, headers.get("user-agent")) From 74a0494d3c09360547623497caf18b4f0d0ab0e1 Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Thu, 17 Jul 2025 21:34:18 +0200 Subject: [PATCH 2/6] More SSL cleanups --- shotgun_api3/shotgun.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 1eccfa2b..300a2408 100644 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -319,9 +319,7 @@ class ClientCapabilities(object): :ivar str local_path_field: The PTR field used for local file paths. This is calculated using the value of ``platform``. Ex. ``local_path_mac``. :ivar str py_version: Simple version of Python executable as a string. Eg. ``3.9``. - :ivar str ssl_version: Version of OpenSSL installed. Eg. ``OpenSSL 1.0.2g 1 Mar 2016``. This - info is only available in Python 2.7+ if the ssl module was imported successfully. - Defaults to ``unknown`` + :ivar str ssl_version: Version of OpenSSL installed. Eg. ``OpenSSL 1.0.2g 1 Mar 2016``. """ def __init__(self): @@ -342,13 +340,7 @@ def __init__(self): self.local_path_field = None self.py_version = ".".join(str(x) for x in sys.version_info[:2]) - - # extract the OpenSSL version if we can. - self.ssl_version = "unknown" - try: - self.ssl_version = ssl.OPENSSL_VERSION - except AttributeError: - pass + self.ssl_version = ssl.OPENSSL_VERSION def __str__(self): return ( From 99d3a1ef66dc4843eb0280b3521daddbdbea52e4 Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Thu, 17 Jul 2025 21:40:18 +0200 Subject: [PATCH 3/6] fixup! More SSL cleanups --- shotgun_api3/shotgun.py | 6 +++--- tests/test_api.py | 14 +------------- tests/test_client.py | 2 -- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 300a2408..8017b050 100644 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -2246,9 +2246,9 @@ def reset_user_agent(self): # create ssl validation string based on settings self._user_agents = [ - f"shotgun-json ({__version__})", - f"Python {self.client_caps.py_version} ({ua_platform})", - f"ssl {self.client_caps.ssl_version}", + "shotgun-json (%s)" % __version__, + "Python %s (%s)" % (self.client_caps.py_version, ua_platform), + "ssl %s" % (self.client_caps.ssl_version), ] def set_session_uuid(self, session_uuid): diff --git a/tests/test_api.py b/tests/test_api.py index 8808e812..99850120 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -288,10 +288,6 @@ def test_upload_to_sg(self, mock_send_form): "attachments", tag_list="monkeys, everywhere, send, help", ) -<<<<<<< HEAD -======= - ->>>>>>> 413afc6 (SSL) mock_send_form.assert_called_once() mock_send_form_args, _ = mock_send_form.call_args display_name_to_send = mock_send_form_args[1].get("display_name", "") @@ -315,11 +311,7 @@ def test_upload_to_sg(self, mock_send_form): display_name_to_send.startswith("b'") and display_name_to_send.endswith("'") ) -<<<<<<< HEAD mock_send_form.reset_mock() -======= - # mock_send_form.method.assert_called_once() ## CANOT work because was already called earlier.... ->>>>>>> 413afc6 (SSL) mock_send_form.return_value = "2\nIt can't be upload" self.assertRaises( shotgun_api3.ShotgunError, @@ -701,10 +693,6 @@ def test_share_thumbnail_not_ready(self, mock_send_form): def test_share_thumbnail_returns_error(self, mock_send_form): """throw an exception if server returns an error code""" -<<<<<<< HEAD -======= - # mock_send_form.method.assert_called_once() # never worked..... ->>>>>>> 413afc6 (SSL) mock_send_form.return_value = "1\nerror message.\n" self.assertRaises( @@ -2260,7 +2248,7 @@ def my_side_effect2(*args, **kwargs): finally: self.sg.config.rpc_attempt_interval = bak_rpc_attempt_interval - @unittest.mock.patch.object(urllib.request.OpenerDirector, 'open') + @unittest.mock.patch.object(urllib.request.OpenerDirector, "open") def test_sanitized_auth_params(self, mock_open): # Simulate the server blowing up and giving us a 500 error mock_open.side_effect = urllib.error.HTTPError("url", 500, "message", {}, None) diff --git a/tests/test_client.py b/tests/test_client.py index ae4ae030..ea10b392 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -29,8 +29,6 @@ import shotgun_api3.lib.httplib2 as httplib2 import shotgun_api3 as api from shotgun_api3.shotgun import ServerCapabilities, SG_TIMEZONE -import shotgun_api3 as api -import shotgun_api3.lib.httplib2 as httplib2 def b64encode(val): From cfc0c985d3906e59cdb8b376d19dac8572dbb981 Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Thu, 17 Jul 2025 21:45:23 +0200 Subject: [PATCH 4/6] fixup! fixup! More SSL cleanups --- tests/test_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_client.py b/tests/test_client.py index ea10b392..8ae3a230 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -29,6 +29,7 @@ import shotgun_api3.lib.httplib2 as httplib2 import shotgun_api3 as api from shotgun_api3.shotgun import ServerCapabilities, SG_TIMEZONE +from . import base def b64encode(val): From f9f9a67f9a6036b49c9542e68300e6cf194fd74b Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Fri, 18 Jul 2025 12:00:07 +0200 Subject: [PATCH 5/6] Fixup/restore --- docs/advanced/iron_python.rst | 10 ++++++---- docs/reference.rst | 2 +- shotgun_api3/shotgun.py | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/advanced/iron_python.rst b/docs/advanced/iron_python.rst index a0176207..06786ab9 100644 --- a/docs/advanced/iron_python.rst +++ b/docs/advanced/iron_python.rst @@ -7,6 +7,12 @@ that we will be compatible with future releases of IronPython. While we don't of IronPython, we certainly will do our best to figure out any issues that come up while using it and how to avoid them. + +Legacy Information +------------------ + +This following information is provided for historical purposes only. + As of July 9, 2015 you can look at this fork of the repo to see what changes were needed as of that date to make things work. The original fork was as of v3.0.20 of the API. Big thanks to our awesome clients Pixomondo for making their work public and letting us refer to it: @@ -20,10 +26,6 @@ v3.0.20 can be used with IronPython with a little bit of added work: https://bitbucket.org/jdhardy/ironpythonzlib/src/. And the blog post about it here http://blog.jdhardy.ca/2008/12/solving-zlib-problem-ironpythonzlib.html -- If you encounter any SSL errors like - ``unknown field: SERIALNUMBER=0123456789`` or ``:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed``. - Inject you own CA cert <> - - If you encounter ``LookupError: unknown encoding: idna``, you can force utf-8 by changing iri2uri.py ~ln 71 from ``authority = authority.encode('idna')`` to ``authority = authority.encode('utf-8')`` diff --git a/docs/reference.rst b/docs/reference.rst index 96c91746..6b16a37b 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -15,7 +15,7 @@ The :mod:`~shotgun_api3.shotgun` module is a container for the :class:`~shotgun. class. There are a couple of useful attributes to note. .. automodule:: shotgun_api3.shotgun - :members: NO_SSL_VALIDATION, LOG + :members: LOG :private-members: :special-members: diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 8017b050..59613ffc 100644 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -3798,6 +3798,22 @@ def _make_call(self, verb, path, body, headers): attempt += 1 try: return self._http_request(verb, path, body, req_headers) + except ssl.SSLEOFError as e: + # SG-34910 - EOF occurred in violation of protocol (_ssl.c:2426) + # This issue seems to be related to proxy and keep alive. + # It looks like, sometimes, the proxy drops the connection on + # the TCP/TLS level despites the keep-alive. So we need to close + # the connection and make a new attempt. + LOG.debug("SSLEOFError: {}".format(e)) + self._close_connection() + if attempt == max_rpc_attempts: + LOG.debug("Request failed. Giving up after %d attempts." % attempt) + raise + except (ssl.SSLError, ssl.CertificateError) as e: + self._close_connection() + if attempt == max_rpc_attempts: + LOG.debug("Request failed. Giving up after %d attempts." % attempt) + raise except Exception: self._close_connection() if attempt == max_rpc_attempts: From 2a56e5e4524c4cd45ae1642e79029f8fb2e901e1 Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Fri, 18 Jul 2025 13:23:00 +0200 Subject: [PATCH 6/6] More cleanup --- shotgun_api3/shotgun.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 59613ffc..14da0069 100644 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -4541,22 +4541,6 @@ def connect(self): ) -class CACertsHTTPSHandler(urllib.request.HTTPHandler): - """ - Handler that ensures https connections are created with the custom CA certs. - """ - - def __init__(self, cacerts): - super().__init__(self) - self.__ca_certs = cacerts - - def https_open(self, req): - return self.do_open(self.create_https_connection, req) - - def create_https_connection(self, *args, **kwargs): - return CACertsHTTPSConnection(*args, ca_certs=self.__ca_certs, **kwargs) - - # Helpers from the previous API, left as is. # Based on http://code.activestate.com/recipes/146306/ class FormPostHandler(urllib.request.BaseHandler):