From 326abe0c8bf3fd8ba1a0684a84048d8764319352 Mon Sep 17 00:00:00 2001 From: Hemmo Nieminen Date: Wed, 15 Jun 2016 03:14:41 -0700 Subject: [PATCH] Backport a fix from Python 2.7 upstream for urllib2. This change makes basic authentication fail after first unsuccessful attempt (instead of automatically retrying) and thus prevents unnecessary failed login attempts. This change also allows printing the contents of an HTTP 401 message to the user. --- osc/conf.py | 52 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/osc/conf.py b/osc/conf.py index 7fc3b7db..e49014db 100644 --- a/osc/conf.py +++ b/osc/conf.py @@ -42,6 +42,7 @@ import os import re import sys import ssl +import warnings try: from http.cookiejar import LWPCookieJar, CookieJar @@ -57,7 +58,7 @@ except ImportError: from httplib import HTTPConnection, HTTPResponse from StringIO import StringIO from urlparse import urlsplit - from urllib2 import URLError, HTTPBasicAuthHandler, HTTPCookieProcessor, HTTPPasswordMgrWithDefaultRealm, ProxyHandler + from urllib2 import URLError, HTTPBasicAuthHandler, HTTPCookieProcessor, HTTPPasswordMgrWithDefaultRealm, ProxyHandler, AbstractBasicAuthHandler from urllib2 import AbstractHTTPHandler, build_opener, proxy_bypass, HTTPSHandler from . import OscConfigParser @@ -479,38 +480,33 @@ def _build_opener(apiurl): # workaround for http://bugs.python.org/issue9639 authhandler_class = HTTPBasicAuthHandler - if sys.version_info >= (2, 6, 6) and sys.version_info < (2, 7, 1) \ - and not 'reset_retry_count' in dir(HTTPBasicAuthHandler): - print('warning: your urllib2 version seems to be broken. ' \ - 'Using a workaround for http://bugs.python.org/issue9639', file=sys.stderr) - + if sys.version_info >= (2, 6, 6) and sys.version_info < (2, 7, 9): class OscHTTPBasicAuthHandler(HTTPBasicAuthHandler): - def http_error_401(self, *args): - response = HTTPBasicAuthHandler.http_error_401(self, *args) - self.retried = 0 - return response + # The following two functions were backported from upstream 2.7. + def http_error_auth_reqed(self, authreq, host, req, headers): + authreq = headers.get(authreq, None) - def http_error_404(self, *args): - self.retried = 0 - return None + if authreq: + mo = AbstractBasicAuthHandler.rx.search(authreq) + if mo: + scheme, quote, realm = mo.groups() + if quote not in ['"', "'"]: + warnings.warn("Basic Auth Realm was unquoted", + UserWarning, 2) + if scheme.lower() == 'basic': + return self.retry_http_basic_auth(host, req, realm) - authhandler_class = OscHTTPBasicAuthHandler - elif sys.version_info >= (2, 6, 6) and sys.version_info < (2, 7, 1): - class OscHTTPBasicAuthHandler(HTTPBasicAuthHandler): - def http_error_404(self, *args): - self.reset_retry_count() - return None - - authhandler_class = OscHTTPBasicAuthHandler - elif sys.version_info >= (2, 6, 5) and sys.version_info < (2, 6, 6): - # workaround for broken urllib2 in python 2.6.5: wrong credentials - # lead to an infinite recursion - class OscHTTPBasicAuthHandler(HTTPBasicAuthHandler): def retry_http_basic_auth(self, host, req, realm): - # don't retry if auth failed - if req.get_header(self.auth_header, None) is not None: + user, pw = self.passwd.find_user_password(realm, host) + if pw is not None: + raw = "%s:%s" % (user, pw) + auth = 'Basic %s' % base64.b64encode(raw).strip() + if req.get_header(self.auth_header, None) == auth: + return None + req.add_unredirected_header(self.auth_header, auth) + return self.parent.open(req, timeout=req.timeout) + else: return None - return HTTPBasicAuthHandler.retry_http_basic_auth(self, host, req, realm) authhandler_class = OscHTTPBasicAuthHandler