767
772
got_creds = yield self.wc.request_proxy_auth_credentials(self.domain,
769
774
self.assertFalse(got_creds, 'Return true when user cancels.')
777
class BaseSSLTestCase(SquidTestCase):
778
"""Base test that allows to use ssl connections."""
780
@defer.inlineCallbacks
782
"""Set the diff tests."""
783
yield super(BaseSSLTestCase, self).setUp()
784
self.cert_dir = os.path.join(self.tmpdir, 'cert')
785
self.cert_details = dict(organization='Canonical',
786
common_name=gethostname(),
787
locality_name='London',
790
state_name='London',)
791
self.ssl_settings = self._generate_self_signed_certificate(
794
self.addCleanup(self._clean_ssl_certificate_files)
796
self.ws = MockWebServer(self.ssl_settings)
797
self.addCleanup(self.ws.stop)
798
self.base_iri = self.ws.get_iri()
799
self.base_ssl_iri = self.ws.get_ssl_iri()
801
def _clean_ssl_certificate_files(self):
802
"""Remove the certificate files."""
803
if os.path.exists(self.cert_dir):
804
shutil.rmtree(self.cert_dir)
806
def _generate_self_signed_certificate(self, cert_dir, cert_details):
807
"""Generate the required SSL certificates."""
808
if not os.path.exists(cert_dir):
809
os.makedirs(cert_dir)
810
cert_path = os.path.join(cert_dir, 'cert.crt')
811
key_path = os.path.join(cert_dir, 'cert.key')
813
if os.path.exists(cert_path):
815
if os.path.exists(key_path):
820
key.generate_key(crypto.TYPE_RSA, 1024)
822
# create a self-signed cert
824
cert.get_subject().C = cert_details['country_name']
825
cert.get_subject().ST = cert_details['state_name']
826
cert.get_subject().L = cert_details['locality_name']
827
cert.get_subject().O = cert_details['organization']
828
cert.get_subject().OU = cert_details['unit']
829
cert.get_subject().CN = cert_details['common_name']
830
cert.set_serial_number(1000)
831
cert.gmtime_adj_notBefore(0)
832
cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
833
cert.set_issuer(cert.get_subject())
835
cert.sign(key, 'sha1')
837
with open(cert_path, 'wt') as fd:
838
fd.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
840
with open(key_path, 'wt') as fd:
841
fd.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
843
return dict(key=key_path, cert=cert_path)
846
class CorrectProxyTestCase(BaseSSLTestCase):
847
"""Test the interaction with a SSL enabled proxy."""
849
@defer.inlineCallbacks
852
yield super(CorrectProxyTestCase, self).setUp()
854
# fake the gsettings to have diff settings for https and http
855
http_settings = self.get_auth_proxy_settings()
857
#remember so that we can use them in the creds request
858
proxy_username = http_settings['username']
859
proxy_password = http_settings['password']
861
# delete the username and password so that we get a 407 for testing
862
del http_settings['username']
863
del http_settings['password']
865
https_settings = self.get_nonauth_proxy_settings()
867
proxy_settings = dict(http=http_settings, https=https_settings)
868
self.patch(gsettings, "get_proxy_settings", lambda: proxy_settings)
870
self.wc = webclient.webclient_factory()
871
self.addCleanup(self.wc.shutdown)
875
def fake_creds_request(domain, retry):
876
"""Fake user interaction."""
877
self.called.append('request_proxy_auth_credentials')
878
self.wc.proxy_username = proxy_username
879
self.wc.proxy_password = proxy_password
880
return defer.succeed(True)
882
self.patch(self.wc, 'request_proxy_auth_credentials',
885
def assert_header_contains(self, headers, expected):
886
"""One of the headers matching key must contain a given value."""
887
self.assertTrue(any(expected in value for value in headers))
889
def test_https_request(self):
890
"""Test using the correct proxy for the ssl request.
892
In order to assert that the correct proxy is used we expect not to call
893
the auth dialog since we set the https proxy not to use the auth proxy
894
and to fail because we are reaching a https page with bad self-signed
897
# we fail due to the fake ssl cert
898
self.failUnlessFailure(self.wc.request(
899
self.base_ssl_iri + SIMPLERESOURCE),
900
webclient.WebClientError)
901
# https requests do not use the auth proxy therefore called should be
902
# empty. This asserts that we are using the correct settings for the
904
self.assertEqual([], self.called)
906
@defer.inlineCallbacks
907
def test_http_request(self):
908
"""Test using the correct proxy for the plain request.
910
This tests does the opposite to the https tests. We did set the auth
911
proxy for the http request therefore we expect the proxy dialog to be
912
used and not to get an error since we are not visiting a https with bad
915
# we do not fail since we are not going to the https page
916
result = yield self.wc.request(self.base_iri + SIMPLERESOURCE)
917
self.assert_header_contains(result.headers["Via"], "squid")
918
# assert that we did go through the auth proxy
919
self.assertIn('request_proxy_auth_credentials', self.called)
921
if WEBCLIENT_MODULE_NAME.endswith(".txweb"):
922
reason = 'Multiple proxy settings is not supported.'
923
test_https_request.skip = reason
924
test_http_request.skip = reason
926
if WEBCLIENT_MODULE_NAME.endswith(".libsoup"):
927
reason = 'Hard to test since we need to fully mock gsettings.'
928
test_https_request.skip = reason
929
test_http_request.skip = reason
932
class SSLTestCase(BaseSSLTestCase):
933
"""Test error handling when dealing with ssl."""
935
@defer.inlineCallbacks
937
"""Set the diff tests."""
938
yield super(SSLTestCase, self).setUp()
940
self.wc = webclient.webclient_factory()
941
self.addCleanup(self.wc.shutdown)
943
self.return_code = USER_CANCELLATION
946
def fake_launch_ssl_dialog(client, domain, details):
947
"""Fake the ssl dialog."""
948
self.called.append(('_launch_ssl_dialog', domain, details))
949
return defer.succeed(self.return_code)
951
self.patch(BaseWebClient, '_launch_ssl_dialog', fake_launch_ssl_dialog)
953
@defer.inlineCallbacks
954
def _assert_ssl_fail_user_accepts(self, proxy_settings=None):
955
"""Assert the dialog is shown in an ssl fail."""
956
self.return_code = USER_SUCCESS
958
self.wc.force_use_proxy(proxy_settings)
959
yield self.wc.request(self.base_ssl_iri + SIMPLERESOURCE)
960
details = SSL_DETAILS_TEMPLATE % self.cert_details
961
self.assertIn(('_launch_ssl_dialog', gethostname(), details),
964
def test_ssl_fail_dialog_user_accepts(self):
965
"""Test showing the dialog and accepting."""
966
self._assert_ssl_fail_user_accepts()
968
def test_ssl_fail_dialog_user_accepts_via_proxy(self):
969
"""Test showing the dialog and accepting when using a proxy."""
970
self._assert_ssl_fail_user_accepts(self.get_nonauth_proxy_settings())
972
def test_ssl_fail_dialog_user_rejects(self):
973
"""Test showing the dialog and rejecting."""
974
self.failUnlessFailure(self.wc.request(self.base_iri + SIMPLERESOURCE),
975
webclient.WebClientError)
977
def test_format_ssl_details(self):
978
"""Assert that details are correctly formatted"""
979
details = SSL_DETAILS_TEMPLATE % self.cert_details
980
self.assertEqual(details,
981
self.wc.format_ssl_details(self.cert_details))
983
if (WEBCLIENT_MODULE_NAME.endswith(".txweb") or
984
WEBCLIENT_MODULE_NAME.endswith(".libsoup")):
985
reason = 'SSL support has not yet been implemented.'
986
test_ssl_fail_dialog_user_accepts.skip = reason
987
test_ssl_fail_dialog_user_accepts_via_proxy.skip = reason
988
test_ssl_fail_dialog_user_rejects.skip = reason