From fbcbb0d90e5ead7f344dca81262aefd6a7114651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Mon, 11 Aug 2014 12:07:56 +0200 Subject: [PATCH] Alternative handler adding Authorization header even in the first request. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new HTTPBasicPriorAuthHandler which adds the Authorization header with the authentication code even to the first HTTP request not only as the reaction to the failed attempt to connect as the default HTTPBasicAuthHandler does. Allows working with websites like https://api.github.com which do not follow the strict interpretation of RFC, but more the dicta in the end of section 2 of RFC 2617: > A client MAY preemptively send the corresponding Authorization > header with requests for resources in that space without receipt > of another challenge from the server. Similarly, when a client > sends a request to a proxy, it may reuse a userid and password in > the Proxy-Authorization header field without receiving another > challenge from the proxy server. See section 4 for security > considerations associated with Basic authentication. Fixes issue 19494. Signed-off-by: Matěj Cepl --- Lib/test/test_urllib2.py | 15 +++++++++++++++ Lib/urllib/request.py | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 7620876..b5e3da7 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1393,6 +1393,21 @@ class HandlerTests(unittest.TestCase): self.assertEqual(len(http_handler.requests), 1) self.assertFalse(http_handler.requests[0].has_header(auth_header)) + def test_auth_prior_handler(self): + pwd_manager = MockPasswordManager() + pwd_manager.add_password(None, 'https://example.com', + 'somebody', 'verysecret') + auth_prior_handler = urllib.request.HTTPBasicPriorAuthHandler( + pwd_manager) + http_hand = MockHTTPSHandler() + + opener = OpenerDirector() + opener.add_handler(http_hand) + opener.add_handler(auth_prior_handler) + + req = Request("https://example.com") + opener.open(req) + self.assertFalse('Authorization' in http_hand.httpconn.req_headers) class MiscTests(unittest.TestCase): diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 0389f5e..94ec2dc 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -924,6 +924,21 @@ class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler): return response +class HTTPBasicPriorAuthHandler(HTTPBasicAuthHandler): + handler_order = 400 + + def http_request(self, req): + if not req.has_header('Authorization'): + user, passwd = self.passwd.find_user_password(None, req.host) + credentials = '{0}:{1}'.format(user, passwd).encode() + auth_str = base64.standard_b64encode(credentials).decode() + req.add_unredirected_header('Authorization', + 'Basic {}'.format(auth_str.strip())) + return req + + https_request = http_request + + # Return n random bytes. _randombytes = os.urandom -- 1.8.3.1