Rev 4056: Tweak HTTP negotiate authentication scheme support in http://bazaar.launchpad.net/%7Evila/bzr/bzr.integration

Vincent Ladeuil v.ladeuil+lp at free.fr
Thu Feb 26 07:21:48 GMT 2009


At http://bazaar.launchpad.net/%7Evila/bzr/bzr.integration

------------------------------------------------------------
revno: 4056
revision-id: v.ladeuil+lp at free.fr-20090226072059-4jzmpddp5xldwo0m
parent: pqm at pqm.ubuntu.com-20090226052717-5tbzaulyew9auo7t
parent: v.ladeuil+lp at free.fr-20090225193419-ynaapyvufvg3nh8k
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: bzr.integration
timestamp: Thu 2009-02-26 08:20:59 +0100
message:
  Tweak HTTP negotiate authentication scheme support
modified:
  bzrlib/tests/test_http.py      testhttp.py-20051018020158-b2eef6e867c514d9
  bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
    ------------------------------------------------------------
    revno: 4050.2.3
    revision-id: v.ladeuil+lp at free.fr-20090225193419-ynaapyvufvg3nh8k
    parent: v.ladeuil+lp at free.fr-20090225192444-az73q3t73x8ti6bi
    committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
    branch nick: 256612-http-auth
    timestamp: Wed 2009-02-25 20:34:19 +0100
    message:
      Slight cosmetic tweaks.
    modified:
      bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
    ------------------------------------------------------------
    revno: 4050.2.2
    revision-id: v.ladeuil+lp at free.fr-20090225192444-az73q3t73x8ti6bi
    parent: v.ladeuil+lp at free.fr-20090225185843-mns85a9v5zqr60ef
    committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
    branch nick: 256612-http-auth
    timestamp: Wed 2009-02-25 20:24:44 +0100
    message:
      Ensures all auth handlers correctly parse all auth headers.
      
      * bzrlib/tests/test_http.py:
      (TestAuthHeader): Test for all known auth schemes.
      
      * bzrlib/transport/http/_urllib2_wrappers.py:
      (AbstractAuthHandler._parse_auth_header): All handlers should be
      able to parse any header, they may as well share the
      implementation.
      (NegotiateAuthHandler.auth_match, BasicAuthHandler.auth_match,
      DigestAuthHandler.auth_match): JFDI.
    modified:
      bzrlib/tests/test_http.py      testhttp.py-20051018020158-b2eef6e867c514d9
      bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
    ------------------------------------------------------------
    revno: 4050.2.1
    revision-id: v.ladeuil+lp at free.fr-20090225185843-mns85a9v5zqr60ef
    parent: pqm at pqm.ubuntu.com-20090225171156-l63eiz2bz51ialsg
    parent: jelmer at samba.org-20090218165011-dp0uuk76echk0dta
    parent: jelmer at samba.org-20090218162331-hjjc7us2hd6hbfl0
    committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
    branch nick: 256612-http-auth
    timestamp: Wed 2009-02-25 19:58:43 +0100
    message:
      merge jelmer patches
    modified:
      bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
        ------------------------------------------------------------
        revno: 4017.6.1
        revision-id: jelmer at samba.org-20090218162331-hjjc7us2hd6hbfl0
        parent: pqm at pqm.ubuntu.com-20090218132708-okubrahz9exvae9r
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: bzr.dev
        timestamp: Wed 2009-02-18 17:23:31 +0100
        message:
          Allow HTTP authentication handlers (such as the NegotiateAuthHandler) to 
          do authentication without a username.
        modified:
          bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
    ------------------------------------------------------------
    revno: 4017.5.1
    revision-id: jelmer at samba.org-20090218165011-dp0uuk76echk0dta
    parent: pqm at pqm.ubuntu.com-20090218132708-okubrahz9exvae9r
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzr.dev
    timestamp: Wed 2009-02-18 17:50:11 +0100
    message:
      Cope with the WWW-Authenticate header containing only a single word in 
      Digest/Basic authentication.
    modified:
      bzrlib/transport/http/_urllib2_wrappers.py _urllib2_wrappers.py-20060913231729-ha9ugi48ktx481ao-1
-------------- next part --------------
=== modified file 'bzrlib/tests/test_http.py'
--- a/bzrlib/tests/test_http.py	2009-02-24 08:11:42 +0000
+++ b/bzrlib/tests/test_http.py	2009-02-25 19:24:44 +0000
@@ -216,6 +216,35 @@
         self.port = None
 
 
+class TestAuthHeader(tests.TestCase):
+
+    def parse_header(self, header):
+        ah =  _urllib2_wrappers.AbstractAuthHandler()
+        return ah._parse_auth_header(header)
+
+    def test_empty_header(self):
+        scheme, remainder = self.parse_header('')
+        self.assertEquals('', scheme)
+        self.assertIs(None, remainder)
+
+    def test_negotiate_header(self):
+        scheme, remainder = self.parse_header('Negotiate')
+        self.assertEquals('negotiate', scheme)
+        self.assertIs(None, remainder)
+
+    def test_basic_header(self):
+        scheme, remainder = self.parse_header(
+            'Basic realm="Thou should not pass"')
+        self.assertEquals('basic', scheme)
+        self.assertEquals('realm="Thou should not pass"', remainder)
+
+    def test_digest_header(self):
+        scheme, remainder = self.parse_header(
+            'Digest realm="Thou should not pass"')
+        self.assertEquals('digest', scheme)
+        self.assertEquals('realm="Thou should not pass"', remainder)
+
+
 class TestHTTPServer(tests.TestCase):
     """Test the HTTP servers implementations."""
 

=== modified file 'bzrlib/transport/http/_urllib2_wrappers.py'
--- a/bzrlib/transport/http/_urllib2_wrappers.py	2009-02-23 16:15:00 +0000
+++ b/bzrlib/transport/http/_urllib2_wrappers.py	2009-02-25 19:34:19 +0000
@@ -983,6 +983,9 @@
     _max_retry = 3
     """We don't want to retry authenticating endlessly"""
 
+    requires_username = True
+    """Whether the auth mechanism requires a username."""
+
     # The following attributes should be defined by daughter
     # classes:
     # - auth_required_header:  the header received from the server
@@ -994,6 +997,22 @@
         # in such a cycle by default.
         self._retry_count = None
 
+    def _parse_auth_header(self, server_header):
+        """Parse the authentication header.
+
+        :param server_header: The value of the header sent by the server
+            describing the authenticaion request.
+
+        :return: A tuple (scheme, remainder) scheme being the first word in the
+            given header (lower cased), remainder may be None.
+        """
+        try:
+            scheme, remainder = server_header.split(None, 1)
+        except ValueError:
+            scheme = server_header
+            remainder = None
+        return (scheme.lower(), remainder)
+
     def update_auth(self, auth, key, value):
         """Update a value in auth marking the auth as modified if needed"""
         old_value = auth.get(key, None)
@@ -1034,7 +1053,7 @@
                 # We already tried that, give up
                 return None
 
-            if auth.get('user', None) is None:
+            if self.requires_username and auth.get('user', None) is None:
                 # Without a known user, we can't authenticate
                 return None
 
@@ -1158,8 +1177,10 @@
 
     handler_order = 480
 
+    requires_username = False
+
     def auth_match(self, header, auth):
-        scheme = header.lower()
+        scheme, raw_auth = self._parse_auth_header(header)
         if scheme != 'negotiate':
             return False
         self.update_auth(auth, 'scheme', scheme)
@@ -1209,8 +1230,7 @@
         return auth_header
 
     def auth_match(self, header, auth):
-        scheme, raw_auth = header.split(None, 1)
-        scheme = scheme.lower()
+        scheme, raw_auth = self._parse_auth_header(header)
         if scheme != 'basic':
             return False
 
@@ -1257,7 +1277,7 @@
 class DigestAuthHandler(AbstractAuthHandler):
     """A custom digest authentication handler."""
 
-    # Before basic as digest is a bit more secure
+    # Before basic as digest is a bit more secure and should be preferred
     handler_order = 490
 
     def auth_params_reusable(self, auth):
@@ -1267,8 +1287,7 @@
         return auth.get('scheme', None) == 'digest'
 
     def auth_match(self, header, auth):
-        scheme, raw_auth = header.split(None, 1)
-        scheme = scheme.lower()
+        scheme, raw_auth = self._parse_auth_header(header)
         if scheme != 'digest':
             return False
 



More information about the bazaar-commits mailing list