]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Close #19494: add urrlib.request.HTTPBasicPriorAuthHandler
authorNick Coghlan <ncoghlan@gmail.com>
Wed, 12 Nov 2014 13:33:50 +0000 (23:33 +1000)
committerNick Coghlan <ncoghlan@gmail.com>
Wed, 12 Nov 2014 13:33:50 +0000 (23:33 +1000)
This auth handler adds the Authorization header to the first
HTTP request rather than waiting for a HTTP 401 Unauthorized
response from the server as the default HTTPBasicAuthHandler
does.

This 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.

Patch by Matej Cepl.

Doc/library/urllib.request.rst
Doc/whatsnew/3.5.rst
Lib/test/test_urllib2.py
Lib/urllib/request.py
Misc/NEWS

index f42025961fc29b233f82d66d8736f5049e516cf0..dca56d6e044d62640d26a504f02717ac6a7fd518 100644 (file)
@@ -304,6 +304,17 @@ The following classes are provided:
    presented with a wrong Authentication scheme.
 
 
+.. class:: HTTPBasicPriorAuthHandler(password_mgr=None)
+
+   A variant of :class:`HTTPBasicAuthHandler` which automatically sends
+   authorization credentials with the first request, rather than waiting to
+   first receive a HTTP 401 "Unauthorised" error response. This allows
+   authentication to sites that don't provide a 401 response when receiving
+   a request without an Authorization header. Aside from this difference,
+   this behaves exactly as :class:`HTTPBasicAuthHandler`.
+
+   .. versionadded:: 3.5
+
 .. class:: ProxyBasicAuthHandler(password_mgr=None)
 
    Handle authentication with the proxy. *password_mgr*, if given, should be
index 502ac44030b23ecf9d5e1b9b51fc3ec3a26f49a3..e90f5fa5cbdf0d026d445176a3c4392624bdd15f 100644 (file)
@@ -297,6 +297,15 @@ time
 * The :func:`time.monotonic` function is now always available.  (Contributed by
   Victor Stinner in :issue:`22043`.)
 
+time
+----
+
+* A new :class:`urllib.request.HTTPBasicPriorAuthHandler` allows HTTP Basic
+  Authentication credentials to be sent unconditionally with the first HTTP
+  request, rather than waiting for a HTTP 401 Unauthorized response from the
+  server.
+  (Contributed by Matej Cepl in :issue:`19494`.)
+
 wsgiref
 -------
 
index 9ea39a49b2df19b298a8a43c55b34b846c14b189..823890e2ca0040e5a58aabb863d31e918fde6fe1 100644 (file)
@@ -1422,6 +1422,21 @@ class HandlerTests(unittest.TestCase):
             handler.do_open(conn, req)
         self.assertTrue(conn.fakesock.closed, "Connection not closed")
 
+    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.assertNotIn('Authorization', http_hand.httpconn.req_headers)
 
 class MiscTests(unittest.TestCase):
 
index e0c8116373299819dc4cc10e0f9bbd3a5c4ef35a..36ae1ef461fbf1013ef02883266add8fba89eed9 100644 (file)
@@ -916,6 +916,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
 
index 431015a26ac71d9f69c65b0adca9243eac5b1228..1c72d964796d09994abb56ee58cf4b6f82cd9fd5 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -183,6 +183,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #19494: Added urllib.request.HTTPBasicPriorAuthHandler. Patch by
+  Matej Cepl.
+
 - Issue #22578: Added attributes to the re.error class.
 
 - Issue #12728: Different Unicode characters having the same uppercase but