def __init__(self, suite): # Suite only needed for back-compat with old clients (see below): self.suite = suite self.engine = None self.port = None # Figure out the ports we are allowed to use. base_port = glbl_cfg().get(['communication', 'base port']) max_ports = glbl_cfg().get( ['communication', 'maximum number of ports']) self.ok_ports = range(int(base_port), int(base_port) + int(max_ports)) random.shuffle(self.ok_ports) comms_options = glbl_cfg().get(['communication', 'options']) # HTTP Digest Auth uses MD5 - pretty secure in this use case. # Extending it with extra algorithms is allowed, but won't be # supported by most browsers. requests and urllib2 are OK though. self.hash_algorithm = "MD5" if "SHA1" in comms_options: # Note 'SHA' rather than 'SHA1'. self.hash_algorithm = "SHA" self.srv_files_mgr = SuiteSrvFilesManager() self.comms_method = glbl_cfg().get(['communication', 'method']) self.get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain( { 'cylc': self.srv_files_mgr.get_auth_item( self.srv_files_mgr.FILE_BASE_PASSPHRASE, suite, content=True), 'anon': NO_PASSPHRASE }, algorithm=self.hash_algorithm) if self.comms_method == 'http': self.cert = None self.pkey = None else: # if self.comms_method in [None, 'https']: try: self.cert = self.srv_files_mgr.get_auth_item( self.srv_files_mgr.FILE_BASE_SSL_CERT, suite) self.pkey = self.srv_files_mgr.get_auth_item( self.srv_files_mgr.FILE_BASE_SSL_PEM, suite) except SuiteServiceFileError: ERR.error("no HTTPS/OpenSSL support. Aborting...") raise CylcError("No HTTPS support. " "Configure user's global.rc to use HTTP.") self.start()
def _call_server_impl_urllib2(self, url, method, payload): """Call server with "urllib2" library.""" import json import urllib2 import ssl unverified_context = getattr(ssl, '_create_unverified_context', None) if unverified_context is not None: ssl._create_default_https_context = unverified_context scheme = url.split(':', 1)[0] # Can use urlparse? username, password = self._get_auth(scheme)[0:2] auth_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() auth_manager.add_password(None, url, username, password) auth = urllib2.HTTPDigestAuthHandler(auth_manager) opener = urllib2.build_opener(auth, urllib2.HTTPSHandler()) headers_list = self._get_headers().items() if payload: payload = json.dumps(payload) headers_list.append(('Accept', 'application/json')) json_headers = {'Content-Type': 'application/json', 'Content-Length': len(payload)} else: payload = None json_headers = {'Content-Length': 0} opener.addheaders = headers_list req = urllib2.Request(url, payload, json_headers) # This is an unpleasant monkey patch, but there isn't an # alternative. urllib2 uses POST if there is a data payload # but that is not the correct criterion. # The difference is basically that POST changes # server state and GET doesn't. req.get_method = lambda: method try: response = opener.open(req, timeout=self.timeout) except urllib2.URLError as exc: if "unknown protocol" in str(exc) and url.startswith("https:"): # Server is using http rather than https, for some reason. sys.stderr.write(self.ERROR_NO_HTTPS_SUPPORT.format(exc)) raise CylcError( "Cannot issue commands over unsecured http.") if cylc.flags.debug: traceback.print_exc() if "timed out" in str(exc): raise ClientTimeout(url, exc) else: raise ClientConnectError(url, exc) except Exception as exc: if cylc.flags.debug: traceback.print_exc() raise ClientError(url, exc) if response.getcode() == 401: access_desc = 'private' if self.auth == self.ANON_AUTH: access_desc = 'public' raise ClientDeniedError(url, self.prog_name, access_desc) response_text = response.read() if response.getcode() >= 400: exception_text = get_exception_from_html(response_text) if exception_text: sys.stderr.write(exception_text) else: sys.stderr.write(response_text) raise ClientConnectedError( url, "%s HTTP return code" % response.getcode()) if self.auth and self.auth[1] != NO_PASSPHRASE: self.srv_files_mgr.cache_passphrase( self.suite, self.owner, self.host, self.auth[1]) try: return json.loads(response_text) except ValueError: return response_text
def _call_server_impl_requests(self, url, method, payload): """Call server with "requests" library.""" import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning warnings.simplefilter("ignore", InsecureRequestWarning) if self.session is None: self.session = requests.Session() if method == self.METHOD_POST: session_method = self.session.post else: session_method = self.session.get scheme = url.split(':', 1)[0] # Can use urlparse? username, password, verify = self._get_auth(scheme) try: ret = session_method( url, json=payload, verify=verify, proxies={}, headers=self._get_headers(), auth=requests.auth.HTTPDigestAuth(username, password), timeout=self.timeout ) except requests.exceptions.SSLError as exc: if "unknown protocol" in str(exc) and url.startswith("https:"): # Server is using http rather than https, for some reason. sys.stderr.write(self.ERROR_NO_HTTPS_SUPPORT.format(exc)) raise CylcError( "Cannot issue commands over unsecured http.") if cylc.flags.debug: traceback.print_exc() raise ClientConnectError(url, exc) except requests.exceptions.Timeout as exc: if cylc.flags.debug: traceback.print_exc() raise ClientTimeout(url, exc) except requests.exceptions.RequestException as exc: if cylc.flags.debug: traceback.print_exc() raise ClientConnectError(url, exc) if ret.status_code == 401: access_desc = 'private' if self.auth == self.ANON_AUTH: access_desc = 'public' raise ClientDeniedError(url, self.prog_name, access_desc) if ret.status_code >= 400: exception_text = get_exception_from_html(ret.text) if exception_text: sys.stderr.write(exception_text) else: sys.stderr.write(ret.text) try: ret.raise_for_status() except requests.exceptions.HTTPError as exc: if cylc.flags.debug: traceback.print_exc() raise ClientConnectedError(url, exc) if self.auth and self.auth[1] != NO_PASSPHRASE: self.srv_files_mgr.cache_passphrase( self.suite, self.owner, self.host, self.auth[1]) try: return ret.json() except ValueError: return ret.text
def test_cylc_error(self): error = CylcError("abcd") self.assertEqual("abcd", error.msg) self.assertEqual("'abcd'", str(error))
def _call_server_impl_requests(self, url, method, payload): """Call server with "requests" library.""" import requests # Filter InsecureRequestWarning from urlib3. We use verify=False # deliberately (and safely) for anonymous access. from requests.packages.urllib3.exceptions import InsecureRequestWarning warnings.simplefilter("ignore", InsecureRequestWarning) # Filter security warnings from urllib3 on python <2.7.9. Obviously, we # want to upgrade, but some sites have to run cylc on platforms with # python <2.7.9. On those platforms, these warnings serve no purpose # except to annoy or confuse users. if sys.version_info < (2, 7, 9): try: from requests.packages.urllib3.exceptions import ( InsecurePlatformWarning) except ImportError: pass else: warnings.simplefilter("ignore", InsecurePlatformWarning) try: from requests.packages.urllib3.exceptions import ( SNIMissingWarning) except ImportError: pass else: warnings.simplefilter("ignore", SNIMissingWarning) if self.session is None: self.session = requests.Session() if method == self.METHOD_POST: session_method = self.session.post else: session_method = self.session.get scheme = url.split(':', 1)[0] # Can use urlparse? username, password, verify = self._get_auth(scheme) try: ret = session_method(url, json=payload, verify=verify, proxies={}, headers=self._get_headers(), auth=requests.auth.HTTPDigestAuth( username, password), timeout=self.timeout) except requests.exceptions.SSLError as exc: if "unknown protocol" in str(exc) and url.startswith("https:"): # Server is using http rather than https, for some reason. sys.stderr.write(self.ERROR_NO_HTTPS_SUPPORT.format(exc)) raise CylcError("Cannot issue commands over unsecured http.") if cylc.flags.debug: traceback.print_exc() raise ClientConnectError(url, exc) except requests.exceptions.Timeout as exc: if cylc.flags.debug: traceback.print_exc() raise ClientTimeout(url, exc) except requests.exceptions.RequestException as exc: if cylc.flags.debug: traceback.print_exc() raise ClientConnectError(url, exc) if ret.status_code == 401: access_desc = 'private' if self.auth == self.ANON_AUTH: access_desc = 'public' raise ClientDeniedError(url, self.prog_name, access_desc) if ret.status_code >= 400: exception_text = get_exception_from_html(ret.text) if exception_text: sys.stderr.write(exception_text) else: sys.stderr.write(ret.text) try: ret.raise_for_status() except requests.exceptions.HTTPError as exc: if cylc.flags.debug: traceback.print_exc() raise ClientConnectedError(url, exc) if self.auth and self.auth[1] != NO_PASSPHRASE: self.srv_files_mgr.cache_passphrase(self.suite, self.owner, self.host, self.auth[1]) try: return ret.json() except ValueError: return ret.text