[PATCH] url: don't reuse bad credentials for basic/digest auth (issue3210)
matt.zuba at gmail.com
matt.zuba at gmail.com
Fri Jan 4 10:25:21 CST 2013
# HG changeset patch
# User Matt Zuba <matt.zuba at gmail.com>
# Date 1357316654 25200
# Node ID 93e59ec3adde4189a2257006796a6ea089c1c682
# Parent 2c1276825e938872ebc099c191eb202f0dbadfcc
url: don't reuse bad credentials for basic/digest auth (issue3210)
The urllib2 library will reuse existing credentials (whether they are good or bad)
on 401 HTTP responses for up to 5 times. This can cause servers to lock out
users if they have a threshold for failed logins less than 5. Instead of retrying
bad credentials, remove them from the password store and allow the user to enter
new ones.
diff --git a/mercurial/url.py b/mercurial/url.py
--- a/mercurial/url.py
+++ b/mercurial/url.py
@@ -57,6 +57,18 @@
return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
self, None, authuri)
+ def remove_password(self, realm, uri):
+ # uri could be a single URI or a sequence
+ if isinstance(uri, basestring):
+ uri = [uri]
+ if not realm in self.passwd:
+ return
+ for default_port in True, False:
+ reduced_uri = tuple(
+ [self.reduce_uri(u, default_port) for u in uri])
+ if reduced_uri in self.passwd[realm]:
+ del self.passwd[realm][reduced_uri]
+
class proxyhandler(urllib2.ProxyHandler):
def __init__(self, ui):
proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
@@ -411,6 +423,18 @@
return
raise
+ def retry_http_digest_auth(self, req, auth):
+ # Remove any username for this realm that might exist
+ # We use 1 here because urllib appears to do one request
+ # to see if Auth is even needed, then does a second request
+ # to actually do the authentication
+ if self.retried > 1:
+ token, challenge = auth.split(' ', 1)
+ chal = urllib2.parse_keqv_list(urllib2.parse_http_list(challenge))
+ self.passwd.remove_password(chal['realm'], req.get_full_url())
+ return urllib2.HTTPDigestAuthHandler.retry_http_digest_auth(
+ self, req, auth)
+
class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
def __init__(self, *args, **kwargs):
urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
@@ -430,6 +454,16 @@
return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
self, auth_header, host, req, headers)
+ def retry_http_basic_auth(self, host, req, realm):
+ # Remove any username for this realm that might exist
+ # We use 1 here because urllib appears to do one request
+ # to see if Auth is even needed, then does a second request
+ # to actually do the authentication
+ if self.retried > 1:
+ self.passwd.remove_password(realm, host)
+ return urllib2.HTTPBasicAuthHandler.retry_http_basic_auth(
+ self, host, req, realm)
+
handlerfuncs = []
def opener(ui, authinfo=None):
More information about the Mercurial-devel
mailing list