diff --git a/browserdebuggertools/chrome/interface.py b/browserdebuggertools/chrome/interface.py index 5a56f68..2f571c9 100644 --- a/browserdebuggertools/chrome/interface.py +++ b/browserdebuggertools/chrome/interface.py @@ -1,7 +1,7 @@ import contextlib import time import logging -from base64 import b64decode +from base64 import b64decode, b64encode from browserdebuggertools.sockethandler import SocketHandler from browserdebuggertools.exceptions import ( @@ -215,3 +215,18 @@ def emulate_network_conditions(self, latency, download, upload, offline=False): } return self.execute("Network", "emulateNetworkConditions", network_conditions) + + def set_basic_auth(self, username, password): + """ + Creates a basic type Authorization header from the username and password strings + and applies it to all requests + """ + auth = "Basic " + b64encode("%s:%s" % (username, password)) + self.set_request_headers({"Authorization": auth}) + + def set_request_headers(self, headers): + """ + The specified headers are applied to all requests + :param headers: A dictionary of the form {"headerKey": "headerValue"} + """ + self.execute("Network", "setExtraHTTPHeaders", {"headers": headers}) diff --git a/setup.py b/setup.py index 18613b6..e84f2dd 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name="browserdebuggertools", - version="3.1.0", + version="3.2.0", packages=PACKAGES, install_requires=requires, license="GNU General Public License v3", diff --git a/tests/e2etests/chrome/test_interface.py b/tests/e2etests/chrome/test_interface.py index f0da0f2..39fb12b 100644 --- a/tests/e2etests/chrome/test_interface.py +++ b/tests/e2etests/chrome/test_interface.py @@ -223,7 +223,7 @@ def test_took_expected_time(self): start = time.time() self.assertTrue(self.waitForEventWithMethod("Network.loadingFinished")) time_taken = time.time() - start - self.assertEqual(10, int(round(time_taken))) + self.assertIn(int(round(time_taken)), [10, 11]) # Headed browser is a bit slower class Test_ChromeInterface_emulate_network_conditions_headed( @@ -236,3 +236,34 @@ class Test_ChromeInterface_emulate_network_conditions_headless( HeadlessChromeInterfaceTest, ChromeInterface_emulate_network_conditions, TestCase ): pass + + +class ChromeInterface_set_basic_auth(object): + + def test_standard_auth_page(self): + + self.devtools_client.enable_domain("Network") + url = "http://username:password@localhost:%s/auth_challenge" % self.testSite.port + self.devtools_client.navigate(url=url) + self._assert_dom_complete() + + responses_received = [] + for event in self.devtools_client.get_events("Network"): + if event.get("method") == "Network.responseReceived": + responses_received.append(event["params"]["response"]["status"]) + + self.assertTrue(len(responses_received) >= 2) # Headed browser creates extra requests + self.assertIn(200, responses_received) + self.assertNotIn(401, responses_received) # Devtools genuinely doesn't report these + + +class Test_ChromeInterface_set_baic_auth_headed( + HeadedChromeInterfaceTest, ChromeInterface_set_basic_auth, TestCase +): + pass + + +class Test_ChromeInterface_set_baic_auth_headless( + HeadlessChromeInterfaceTest, ChromeInterface_set_basic_auth, TestCase +): + pass diff --git a/tests/e2etests/testsite/start.py b/tests/e2etests/testsite/start.py index 05c1d3a..df5e2f3 100644 --- a/tests/e2etests/testsite/start.py +++ b/tests/e2etests/testsite/start.py @@ -1,5 +1,6 @@ import multiprocessing import time +from base64 import b64decode import cherrypy @@ -35,6 +36,40 @@ def javascript_file(self, response_time=None): def big_body(self, size=1000000): return "T" * size + @cherrypy.expose + def auth_challenge(self, authorized_username="username", authorized_password="password", + response_body=None): + + if self.is_authenticated(authorized_username, authorized_password): + + if response_body: + return response_body + + return """ + +
+ + Authorized + + + """ + cherrypy.response.headers["WWW-Authenticate"] = "basic" + cherrypy.response.status = 401 + return "Need to authorize" + + @staticmethod + def is_authenticated(authorized_username, authorized_password): + + if "Authorization" in cherrypy.request.headers: + + auth_string = str(cherrypy.request.headers["Authorization"]) + secret = auth_string.split("Basic ")[1] + credentials = b64decode(secret).decode() + this_username, this_password = tuple(credentials.split(":")) + if (this_username == authorized_username) and (this_password == authorized_password): + return True + return False + class Server(object):