def _build_opener(apiurl): from osc.core import __version__ global config if 'last_opener' not in _build_opener.__dict__: _build_opener.last_opener = (None, None) if apiurl == _build_opener.last_opener[0]: return _build_opener.last_opener[1] # respect no_proxy env variable if proxy_bypass(apiurl): # initialize with empty dict proxyhandler = ProxyHandler({}) else: # read proxies from env proxyhandler = ProxyHandler() # 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) class OscHTTPBasicAuthHandler(HTTPBasicAuthHandler): def http_error_401(self, *args): response = HTTPBasicAuthHandler.http_error_401(self, *args) self.retried = 0 return response def http_error_404(self, *args): self.retried = 0 return None 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: return None return HTTPBasicAuthHandler.retry_http_basic_auth( self, host, req, realm) authhandler_class = OscHTTPBasicAuthHandler options = config['api_host_options'][apiurl] # with None as first argument, it will always use this username/password # combination for urls for which arg2 (apisrv) is a super-url authhandler = authhandler_class( \ HTTPPasswordMgrWithDefaultRealm()) authhandler.add_password(None, apiurl, options['user'], options['pass']) if options['sslcertck']: try: from . import oscssl from M2Crypto import m2urllib2 except ImportError as e: print(e) raise NoSecureSSLError( 'M2Crypto is needed to access %s in a secure way.\nPlease install python-m2crypto.' % apiurl) cafile = options.get('cafile', None) capath = options.get('capath', None) if not cafile and not capath: for i in ['/etc/pki/tls/cert.pem', '/etc/ssl/certs']: if os.path.isfile(i): cafile = i break elif os.path.isdir(i): capath = i break if not cafile and not capath: raise oscerr.OscIOError(None, 'No CA certificates found') ctx = oscssl.mySSLContext() if ctx.load_verify_locations(capath=capath, cafile=cafile) != 1: raise oscerr.OscIOError(None, 'No CA certificates found') opener = m2urllib2.build_opener( ctx, oscssl.myHTTPSHandler(ssl_context=ctx, appname='osc'), HTTPCookieProcessor(cookiejar), authhandler, proxyhandler) else: handlers = [HTTPCookieProcessor(cookiejar), authhandler, proxyhandler] try: # disable ssl cert check in python >= 2.7.9 ctx = ssl._create_unverified_context() handlers.append(HTTPSHandler(context=ctx)) except AttributeError: pass print( "WARNING: SSL certificate checks disabled. Connection is insecure!\n", file=sys.stderr) opener = build_opener(*handlers) opener.addheaders = [('User-agent', 'osc/%s' % __version__)] _build_opener.last_opener = (apiurl, opener) return opener
def _build_opener(apiurl): from osc.core import __version__ global config class OscHTTPBasicAuthHandler(HTTPBasicAuthHandler, object): # python2: inherit from object in order to make it a new-style class # (HTTPBasicAuthHandler is not a new-style class) def _rewind_request(self, req): if hasattr(req.data, 'seek'): # if the request is issued again (this time with an # Authorization header), the file's offset has to be # repositioned to the beginning of the file (otherwise, # a 0-length body is sent which most likely does not match # the Content-Length header (if present)) req.data.seek(0) def retry_http_basic_auth(self, host, req, realm): self._rewind_request(req) return super(self.__class__, self).retry_http_basic_auth(host, req, realm) if 'last_opener' not in _build_opener.__dict__: _build_opener.last_opener = (None, None) if apiurl == _build_opener.last_opener[0]: return _build_opener.last_opener[1] # respect no_proxy env variable if proxy_bypass(apiurl): # initialize with empty dict proxyhandler = ProxyHandler({}) else: # read proxies from env proxyhandler = ProxyHandler() authhandler_class = OscHTTPBasicAuthHandler # workaround for http://bugs.python.org/issue9639 if sys.version_info >= (2, 6, 6) and sys.version_info < (2, 7, 9): class OscHTTPBasicAuthHandlerCompat(OscHTTPBasicAuthHandler): # 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) 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) def retry_http_basic_auth(self, host, req, realm): self._rewind_request(req) 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 authhandler_class = OscHTTPBasicAuthHandlerCompat options = config['api_host_options'][apiurl] # with None as first argument, it will always use this username/password # combination for urls for which arg2 (apisrv) is a super-url authhandler = authhandler_class( \ HTTPPasswordMgrWithDefaultRealm()) authhandler.add_password(None, apiurl, options['user'], options['pass']) if options['sslcertck']: try: from . import oscssl from M2Crypto import m2urllib2 except ImportError as e: print(e) raise NoSecureSSLError( 'M2Crypto is needed to access %s in a secure way.\nPlease install python-m2crypto.' % apiurl) cafile = options.get('cafile', None) capath = options.get('capath', None) if not cafile and not capath: for i in ['/etc/pki/tls/cert.pem', '/etc/ssl/certs']: if os.path.isfile(i): cafile = i break elif os.path.isdir(i): capath = i break if not cafile and not capath: raise oscerr.OscIOError( None, 'No CA certificates found. (You may want to install ca-certificates-mozilla package)' ) ctx = oscssl.mySSLContext() if ctx.load_verify_locations(capath=capath, cafile=cafile) != 1: raise oscerr.OscIOError( None, 'No CA certificates found. (You may want to install ca-certificates-mozilla package)' ) opener = m2urllib2.build_opener( ctx, oscssl.myHTTPSHandler(ssl_context=ctx, appname='osc'), HTTPCookieProcessor(cookiejar), authhandler, proxyhandler) else: handlers = [HTTPCookieProcessor(cookiejar), authhandler, proxyhandler] try: # disable ssl cert check in python >= 2.7.9 ctx = ssl._create_unverified_context() handlers.append(HTTPSHandler(context=ctx)) except AttributeError: pass print( "WARNING: SSL certificate checks disabled. Connection is insecure!\n", file=sys.stderr) opener = build_opener(*handlers) opener.addheaders = [('User-agent', 'osc/%s' % __version__)] _build_opener.last_opener = (apiurl, opener) return opener