Commits

Markus Zapke-Gründemann  committed 968fb49

Better detection of bad authentication.

  • Participants
  • Parent commits b066b1e

Comments (0)

Files changed (3)

File mercurial_keyring.py

         # request, then the previously returned auth must have been
         # wrong. So we note this to force password prompt (and avoid
         # reusing bad password indifinitely).
+        try:
+            headers = req.header_items()
+        except AttributeError:
+            headers = None
         after_bad_auth = (self.last_reply \
                           and (self.last_reply['realm'] == realm) \
                           and (self.last_reply['authuri'] == authuri) \
-                          and (self.last_reply['req'] == req))
+                          and (self.last_reply['headers'] == headers))
         if after_bad_auth:
             _debug(ui, _("Working after bad authentication, cached passwords not used %s") % str(self.last_reply))
 
             _debug_reply(ui, _("Auth data found in repository URL"),
                          base_url, user, pwd)
             self.last_reply = dict(realm=realm, authuri=authuri, user=user,
-                req=req)
+                headers=headers)
             return user, pwd
 
         # Loading .hg/hgrc [auth] section contents. If prefix is given,
                 _debug_reply(ui, _("Cached auth data found"),
                              base_url, user, pwd)
                 self.last_reply = dict(realm=realm, authuri=authuri, user=user,
-                    req=req)
+                    headers=headers)
                 return user, pwd
 
         if auth_user:
                 _debug_reply(ui, _("Auth data set in .hg/hgrc"),
                              base_url, user, pwd)
                 self.last_reply = dict(realm=realm, authuri=authuri, user=user,
-                    req=req)
+                    headers=headers)
                 return user, pwd
             else:
                 _debug(ui, _("Username found in .hg/hgrc: %s") % user)
                 _debug_reply(ui, _("Keyring password found"),
                              base_url, user, pwd)
                 self.last_reply = dict(realm=realm, authuri=authuri, user=user,
-                    req=req)
+                    headers=headers)
                 return user, pwd
             else:
                 _debug(ui, _("Password not present in the keyring"))
         _debug_reply(ui, _("Manually entered password"),
                      base_url, user, pwd)
         self.last_reply = dict(realm=realm, authuri=authuri, user=user,
-            req=req)
+            headers=headers)
         return user, pwd
 
     def load_hgrc_auth(self, ui, base_url, user):

File tests/test-find_auth.py

 import os
+from urllib2 import Request
 import sys
 
 from mercurial import ui, util
-from mercurial.keepalive import HTTPResponse
 from mercurial.url import passwordmgr
 
 from mercurial_keyring import HTTPPasswordHandler, PasswordStore
         return self._password
 
 
-class MockSock(object):
-    fileno = 1
-
-    def makefile(self, mode, bufsize):
-        return ''
-
-
 class TestAuth(object):
     def __init__(self, user, password=None, interactive=True):
         self.user = user
             self.password = password
         self.ui = MockUi(self.user, self.password, interactive=interactive)
         self.pwmgr = passwordmgr(self.ui)
-        self.response = HTTPResponse(MockSock())
         self.pwhandler = HTTPPasswordHandler()
 
     @staticmethod
     def debug(msg):
         sys.stdout.write('[test] %s' % msg)
 
-    def test_uri(self, uri, realm=None, msg=None, debug=True):
+    def test_uri(self, uri, headers=None, realm=None, msg=None, debug=True, noreq=False):
+        if headers is None and not noreq:
+            headers = {}
         if msg is not None:
             self.debug(msg + '\n')
         if debug:
             debug_orig = self.ui.config('ui', 'debug')
             self.ui.setconfig('ui', 'debug', 'True')
         self.debug('testing user %s at %s\n' % (self.user, uri))
+        if noreq:
+            request = None
+        else:
+            request = Request(uri, headers=headers)
+            if headers:
+                self.debug('request headers:\n')
+                for key, value in headers.items():
+                    self.debug('\t%s: %s\n' % (key, value))
         try:
             user, pwd = self.pwhandler.find_auth(self.pwmgr, realm, uri,
-                self.response)
+                request)
             assert user == self.user, 'user did not match'
             assert pwd == self.password, 'password did not match'
         except util.Abort, err:
 
 
 t = TestAuth('alice')
-t.test_uri('https://hg.example.com/repo', msg='ask for the password')
+t.test_uri('https://hg.example.com/repo', msg='ask for password',
+    headers={'User-agent': 'mercurial/proto-1.0'})
 t.test_uri('https://hg.example.com/repo',
-    msg='ignore password cache because of identical request', debug=False)
+    msg='ignore password cache because of identical request',
+    headers={'User-agent': 'mercurial/proto-1.0'})
 t.test_uri('https://alice@hg.example.com/repo',
     msg='read username from URI')
 t.test_uri('https://alice:secret@hg.example.com/repo',
     msg='read username and password from URI')
 t.test_uri('https://hg.example.com/repo', msg='use cached auth data')
+t.test_uri('https://hg.example.com/repo', noreq=True,
+    msg='use no request object, uses cached auth data')
+t.test_uri('https://hg.example.com/repo', noreq=True,
+    msg='second request with no request object fails')
 
 # add auth section to .hgrc
 hgrc = open(os.environ["HGRCPATH"], 'w')
 t = TestAuth('bob', interactive=False)
 t.test_uri('https://hg.example.com/repo',
     msg='will abort because terminal is not-interactive', debug=False)
+
+# < 1.9
+t = TestAuth('bob')
+t.test_uri('https://hg.example.com/repo/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between')
+t.test_uri('https://hg.example.com/repo/?cmd=heads')
+t.test_uri('https://hg.example.com/repo/?cmd=changegroup&roots=0000000000000000000000000000000000000000')
+t.test_uri('https://hg.example.com/repo/?cmd=capabilities')
+t.test_uri('https://hg.example.com/repo/?cmd=listkeys&namespace=bookmarks')
+
+# >= 2.2
+t = TestAuth('bob')
+t.test_uri('https://hg.example.com/repo/?cmd=capabilities')
+t.test_uri('https://hg.example.com/repo/?cmd=batch',
+    headers={'x-hgarg-1': 'cmds=heads+%3Bknown+nodes%3D'})
+t.test_uri('https://hg.example.com/repo/?cmd=getbundle',
+    headers={'x-hgarg-1': 'common=0000000000000000000000000000000000000000&heads=b066b1ec09fb7b604b548375170e78bc008bb6b2'})
+t.test_uri('https://hg.example.com/repo/?cmd=listkeys',
+    headers={'x-hgarg-1': 'namespace=phases'})
+t.test_uri('https://hg.example.com/repo/?cmd=listkeys',
+    headers={'x-hgarg-1': 'namespace=bookmarks'})

File tests/test-find_auth.py.out

-[test] ask for the password
+[test] ask for password
 [test] testing user alice at https://hg.example.com/repo
+[test] request headers:
+[test] 	User-agent: mercurial/proto-1.0
 [HgKeyring] Keyring URL: https://hg.example.com/repo
 Username not specified in .hg/hgrc. Keyring will not be used.
 http authorization required
 
 [test] ignore password cache because of identical request
 [test] testing user alice at https://hg.example.com/repo
+[test] request headers:
+[test] 	User-agent: mercurial/proto-1.0
+[HgKeyring] Working after bad authentication, cached passwords not used {'headers': [('User-agent', 'mercurial/proto-1.0')], 'realm': None, 'user': 'alice', 'authuri': 'https://hg.example.com/repo'}
+[HgKeyring] Keyring URL: https://hg.example.com/repo
 Username not specified in .hg/hgrc. Keyring will not be used.
 http authorization required
 realm: None
+[HgKeyring] Manually entered password. Url: https://hg.example.com/repo, user: alice, passwd: ******
 
 [test] read username from URI
 [test] testing user alice at https://alice@hg.example.com/repo
 [HgKeyring] Keyring URL: https://hg.example.com/repo
 [HgKeyring] Cached auth data found. Url: https://hg.example.com/repo, user: alice, passwd: ******
 
+[test] use no request object, uses cached auth data
+[test] testing user alice at https://hg.example.com/repo
+[HgKeyring] Keyring URL: https://hg.example.com/repo
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo, user: alice, passwd: ******
+
+[test] second request with no request object fails
+[test] testing user alice at https://hg.example.com/repo
+[HgKeyring] Working after bad authentication, cached passwords not used {'headers': None, 'realm': None, 'user': 'alice', 'authuri': 'https://hg.example.com/repo'}
+[HgKeyring] Keyring URL: https://hg.example.com/repo
+Username not specified in .hg/hgrc. Keyring will not be used.
+http authorization required
+realm: None
+[HgKeyring] Manually entered password. Url: https://hg.example.com/repo, user: alice, passwd: ******
+
 [test] read password from keyring
 [test] testing user bob at https://hg.example.com/repo
 [HgKeyring] Keyring URL: https://hg.example.com
 [test] testing user bob at https://hg.example.com/repo
 [test] abort: mercurial_keyring: http authorization required but program used in non-interactive mode
 
+[test] testing user bob at https://hg.example.com/repo/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+Username not specified in .hg/hgrc. Keyring will not be used.
+http authorization required
+realm: None
+[HgKeyring] Manually entered password. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=heads
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=changegroup&roots=0000000000000000000000000000000000000000
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=capabilities
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=listkeys&namespace=bookmarks
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=capabilities
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+Username not specified in .hg/hgrc. Keyring will not be used.
+http authorization required
+realm: None
+[HgKeyring] Manually entered password. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=batch
+[test] request headers:
+[test] 	x-hgarg-1: cmds=heads+%3Bknown+nodes%3D
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=getbundle
+[test] request headers:
+[test] 	x-hgarg-1: common=0000000000000000000000000000000000000000&heads=b066b1ec09fb7b604b548375170e78bc008bb6b2
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=listkeys
+[test] request headers:
+[test] 	x-hgarg-1: namespace=phases
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+
+[test] testing user bob at https://hg.example.com/repo/?cmd=listkeys
+[test] request headers:
+[test] 	x-hgarg-1: namespace=bookmarks
+[HgKeyring] Keyring URL: https://hg.example.com/repo/
+[HgKeyring] Cached auth data found. Url: https://hg.example.com/repo/, user: bob, passwd: ******
+