def forward(self, name, *args, **kw): """ Forward call to command named ``name`` over XML-RPC. This method will encode and forward an XML-RPC request, and will then decode and return the corresponding XML-RPC response. :param command: The name of the command being forwarded. :param args: Positional arguments to pass to remote command. :param kw: Keyword arguments to pass to remote command. """ server = getattr(context, 'request_url', None) self.log.info("Forwarding '%s' to %s server '%s'", name, self.protocol, server) command = getattr(self.conn, name) params = [args, kw] try: return self._call_command(command, params) except Fault as e: e = decode_fault(e) self.debug('Caught fault %d from server %s: %s', e.faultCode, server, e.faultString) if e.faultCode in errors_by_code: error = errors_by_code[e.faultCode] raise error(message=e.faultString) raise UnknownError( code=e.faultCode, error=e.faultString, server=server, ) except SSLError as e: raise NetworkError(uri=server, error=str(e)) except ProtocolError as e: # By catching a 401 here we can detect the case where we have # a single IPA server and the session is invalid. Otherwise # we always have to do a ping(). session_cookie = getattr(context, 'session_cookie', None) if session_cookie and e.errcode == 401: # Unauthorized. Remove the session and try again. delattr(context, 'session_cookie') try: principal = getattr(context, 'principal', None) delete_persistent_client_session_data(principal) except Exception as e: # This shouldn't happen if we have a session but it isn't fatal. pass # Create a new serverproxy with the non-session URI serverproxy = self.create_connection( os.environ.get('KRB5CCNAME'), self.env.verbose, self.env.fallback, self.env.delegate) setattr(context, self.id, Connection(serverproxy, self.disconnect)) return self.forward(name, *args, **kw) raise NetworkError(uri=server, error=e.errmsg) except socket.error as e: raise NetworkError(uri=server, error=str(e)) except (OverflowError, TypeError) as e: raise XMLRPCMarshallError(error=str(e))
def http_request(host, port, url, **kw): """ :param url: The URL to post to. :param kw: Keyword arguments to encode into POST body. :return: (http_status, http_reason_phrase, http_headers, http_body) as (integer, unicode, dict, str) Perform an HTTP request. """ if isinstance(host, unicode): host = host.encode('utf-8') uri = 'http://%s%s' % (ipautil.format_netloc(host, port), url) post = urlencode(kw) root_logger.info('request %r', uri) root_logger.debug('request post %r', post) conn = httplib.HTTPConnection(host, port) try: conn.request( 'POST', url, body=post, headers={'Content-type': 'application/x-www-form-urlencoded'}, ) res = conn.getresponse() http_status = res.status http_reason_phrase = unicode(res.reason, 'utf-8') http_headers = res.msg.dict http_body = res.read() conn.close() except NSPRError, e: raise NetworkError(uri=uri, error=str(e))
def _httplib_request( protocol, host, port, path, connection_factory, request_body): """ :param request_body: Request body :param connection_factory: Connection class to use. Will be called with the host and port arguments. Perform a HTTP(s) request. """ if isinstance(host, unicode): host = host.encode('utf-8') uri = '%s://%s%s' % (protocol, ipautil.format_netloc(host, port), path) root_logger.debug('request %r', uri) root_logger.debug('request body %r', request_body) try: conn = connection_factory(host, port) conn.request( 'POST', uri, body=request_body, headers={'Content-type': 'application/x-www-form-urlencoded'}, ) res = conn.getresponse() http_status = res.status http_reason_phrase = unicode(res.reason, 'utf-8') http_headers = res.msg.dict http_body = res.read() conn.close() except Exception, e: raise NetworkError(uri=uri, error=str(e))
def https_request(host, port, url, secdir, password, nickname, operation, args, **kw): """ :param url: The URL to post to. :param operation: GET, POST, (PUT and DELETE not yet implemented) :param args: arguments for GET command line, or for POST :param kw: Keyword arguments to encode into POST body. :return: (http_status, http_reason_phrase, http_headers, http_body) as (integer, unicode, dict, str) Perform a client authenticated HTTPS request """ if isinstance(host, six.text_type): host = host.encode('utf-8') uri = 'https://%s%s' % (ipautil.format_netloc(host, port), url) logging.info('sslget %r', uri) request_headers = { "Content-type": "application/xml", "Accept": "application/xml" } if operation == "POST": if args is not None: post = args elif kw is not None: post = urlencode(kw) request_headers = { "Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain" } conn = None try: conn = nsslib.NSSConnection(host, port, dbdir=secdir) conn.set_debuglevel(0) conn.connect() conn.sock.set_client_auth_data_callback( nsslib.client_auth_data_callback, nickname, password, nss.get_default_certdb()) if operation == "GET": url = url + "?" + args conn.request("GET", url) elif operation == "POST": conn.request("POST", url, post, request_headers) res = conn.getresponse() http_status = res.status http_reason_phrase = six.text_type(res.reason, 'utf-8') http_headers = res.msg.dict http_body = res.read() except Exception as e: raise NetworkError(uri=uri, error=str(e)) finally: if conn is not None: conn.close() return http_status, http_reason_phrase, http_headers, http_body
def _httplib_request(protocol, host, port, path, connection_factory, request_body, method='POST', headers=None, connection_options=None): """ :param request_body: Request body :param connection_factory: Connection class to use. Will be called with the host and port arguments. :param method: HTTP request method (default: 'POST') :param connection_options: a dictionary that will be passed to connection_factory as keyword arguments. Perform a HTTP(s) request. """ if connection_options is None: connection_options = {} uri = u'%s://%s%s' % (protocol, ipautil.format_netloc(host, port), path) logger.debug('request %s %s', method, uri) logger.debug('request body %r', request_body) headers = headers or {} if (method == 'POST' and 'content-type' not in (str(k).lower() for k in headers)): headers['content-type'] = 'application/x-www-form-urlencoded' try: conn = connection_factory(host, port, **connection_options) conn.request(method, path, body=request_body, headers=headers) res = conn.getresponse() http_status = res.status http_headers = res.msg http_body = res.read() conn.close() except Exception as e: logger.debug("httplib request failed:", exc_info=True) raise NetworkError(uri=uri, error=str(e)) encoding = res.getheader('Content-Encoding') if encoding == 'gzip': http_body = gzip_decompress(http_body) elif encoding == 'deflate': http_body = zlib.decompress(http_body) logger.debug('response status %d', http_status) logger.debug('response headers %s', http_headers) logger.debug('response body (decoded): %r', http_body) return http_status, http_headers, http_body
def http_request(host, port, url, operation, args): """ :param url: The URL to post to. :param operation: GET, POST, (PUT and DELETE not yet implemented) :param args: arguments for GET command line, or for POST :return: (http_status, http_reason_phrase, http_headers, http_body) as (integer, unicode, dict, str) Perform an HTTP request. """ if isinstance(host, six.text_type): host = host.encode('utf-8') uri = 'http://%s%s' % (ipautil.format_netloc(host, port), url) logging.info('request %r', uri) request_headers = { "Content-type": "application/xml", "Accept": "application/xml" } if operation == "POST": if args is not None: post = args else: post = "" conn = http_client.HTTPConnection(host, port) try: if operation == "GET": if args is not None: url = url + "?" + args conn.request("GET", url) elif operation == "POST": conn.request("POST", url, post, request_headers) res = conn.getresponse() http_status = res.status http_reason_phrase = six.text_type(res.reason, 'utf-8') http_headers = res.msg.dict http_body = res.read() except NSPRError as e: raise NetworkError(uri=uri, error=str(e)) finally: if conn is not None: conn.close() logging.debug('request status %d', http_status) logging.debug('request reason_phrase %r', http_reason_phrase) logging.debug('request headers %s', http_headers) logging.debug('request body %r', http_body) return http_status, http_reason_phrase, http_headers, http_body
def _httplib_request(protocol, host, port, path, connection_factory, request_body, method='POST', headers=None): """ :param request_body: Request body :param connection_factory: Connection class to use. Will be called with the host and port arguments. :param method: HTTP request method (default: 'POST') Perform a HTTP(s) request. """ if isinstance(host, unicode): host = host.encode('utf-8') uri = '%s://%s%s' % (protocol, ipautil.format_netloc(host, port), path) root_logger.debug('request %s %s', method, uri) root_logger.debug('request body %r', request_body) headers = headers or {} if (method == 'POST' and 'content-type' not in (str(k).lower() for k in headers)): headers['content-type'] = 'application/x-www-form-urlencoded' try: conn = connection_factory(host, port) conn.request(method, uri, body=request_body, headers=headers) res = conn.getresponse() http_status = res.status http_headers = res.msg.dict http_body = res.read() conn.close() except Exception as e: raise NetworkError(uri=uri, error=str(e)) root_logger.debug('response status %d', http_status) root_logger.debug('response headers %s', http_headers) root_logger.debug('response body %r', http_body) return http_status, http_headers, http_body
def https_request(host, port, url, secdir, password, nickname, **kw): """ :param url: The URL to post to. :param kw: Keyword arguments to encode into POST body. :return: (http_status, http_reason_phrase, http_headers, http_body) as (integer, unicode, dict, str) Perform a client authenticated HTTPS request """ if isinstance(host, unicode): host = host.encode('utf-8') uri = 'https://%s%s' % (ipautil.format_netloc(host, port), url) post = urlencode(kw) root_logger.debug('https_request %r', uri) root_logger.debug('https_request post %r', post) request_headers = { "Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain" } try: conn = nsslib.NSSConnection(host, port, dbdir=secdir, tls_version_min=api.env.tls_version_min, tls_version_max=api.env.tls_version_max) conn.set_debuglevel(0) conn.connect() conn.sock.set_client_auth_data_callback( nsslib.client_auth_data_callback, nickname, password, nss.get_default_certdb()) conn.request("POST", url, post, request_headers) res = conn.getresponse() http_status = res.status http_reason_phrase = unicode(res.reason, 'utf-8') http_headers = res.msg.dict http_body = res.read() conn.close() except Exception, e: raise NetworkError(uri=uri, error=str(e))
def create_connection(self, ccache=None, verbose=None, fallback=None, delegate=None, ca_certfile=None): if verbose is None: verbose = self.api.env.verbose if fallback is None: fallback = self.api.env.fallback if delegate is None: delegate = self.api.env.delegate if ca_certfile is None: ca_certfile = self.api.env.tls_ca_cert context.ca_certfile = ca_certfile rpc_uri = self.env[self.env_rpc_uri_key] try: principal = get_principal(ccache_name=ccache) stored_principal = getattr(context, 'principal', None) if principal != stored_principal: try: delattr(context, 'session_cookie') except AttributeError: pass setattr(context, 'principal', principal) # We have a session cookie, try using the session URI to see if it # is still valid if not delegate: rpc_uri = self.apply_session_cookie(rpc_uri) except (errors.CCacheError, ValueError): # No session key, do full Kerberos auth pass urls = self.get_url_list(rpc_uri) proxy_kw = { 'allow_none': True, 'encoding': 'UTF-8', 'verbose': verbose } for url in urls: # should we get ProtocolError (=> error in HTTP response) and # 401 (=> Unauthorized), we'll be re-trying with new session # cookies several times for _try_num in range(0, 5): if url.startswith('https://'): if delegate: transport_class = DelegatedKerbTransport else: transport_class = KerbTransport else: transport_class = LanguageAwareTransport proxy_kw['transport'] = transport_class(protocol=self.protocol, service='HTTP', ccache=ccache) logger.info('trying %s', url) setattr(context, 'request_url', url) serverproxy = self.server_proxy_class(url, **proxy_kw) if len(urls) == 1: # if we have only 1 server and then let the # main requester handle any errors. This also means it # must handle a 401 but we save a ping. return serverproxy try: command = getattr(serverproxy, 'ping') try: command([], {}) except Fault as e: e = decode_fault(e) if e.faultCode in errors_by_code: error = errors_by_code[e.faultCode] raise error(message=e.faultString) else: raise UnknownError( code=e.faultCode, error=e.faultString, server=url, ) # We don't care about the response, just that we got one return serverproxy except errors.KerberosError: # kerberos error on one server is likely on all raise except ProtocolError as e: if hasattr(context, 'session_cookie') and e.errcode == 401: # Unauthorized. Remove the session and try again. delattr(context, 'session_cookie') try: delete_persistent_client_session_data(principal) except Exception: # This shouldn't happen if we have a session but # it isn't fatal. pass # try the same url once more with a new session cookie continue if not fallback: raise else: logger.info('Connection to %s failed with %s', url, e) # try the next url break except Exception as e: if not fallback: raise else: logger.info('Connection to %s failed with %s', url, e) # try the next url break # finished all tries but no serverproxy was found raise NetworkError(uri=_('any of the configured servers'), error=', '.join(urls))
def create_connection(self, ccache=None, verbose=None, fallback=None, delegate=None, ca_certfile=None): if verbose is None: verbose = self.api.env.verbose if fallback is None: fallback = self.api.env.fallback if delegate is None: delegate = self.api.env.delegate if ca_certfile is None: ca_certfile = self.api.env.tls_ca_cert try: rpc_uri = self.env[self.env_rpc_uri_key] principal = get_principal(ccache_name=ccache) stored_principal = getattr(context, 'principal', None) if principal != stored_principal: try: delattr(context, 'session_cookie') except AttributeError: pass setattr(context, 'principal', principal) # We have a session cookie, try using the session URI to see if it # is still valid if not delegate: rpc_uri = self.apply_session_cookie(rpc_uri) except (errors.CCacheError, ValueError): # No session key, do full Kerberos auth pass context.ca_certfile = ca_certfile urls = self.get_url_list(rpc_uri) serverproxy = None for url in urls: kw = dict(allow_none=True, encoding='UTF-8') kw['verbose'] = verbose if url.startswith('https://'): if delegate: transport_class = DelegatedKerbTransport else: transport_class = KerbTransport else: transport_class = LanguageAwareTransport kw['transport'] = transport_class(protocol=self.protocol, service='HTTP', ccache=ccache) self.log.info('trying %s' % url) setattr(context, 'request_url', url) serverproxy = self.server_proxy_class(url, **kw) if len(urls) == 1: # if we have only 1 server and then let the # main requester handle any errors. This also means it # must handle a 401 but we save a ping. return serverproxy try: command = getattr(serverproxy, 'ping') try: command([], {}) except Fault as e: e = decode_fault(e) if e.faultCode in errors_by_code: error = errors_by_code[e.faultCode] raise error(message=e.faultString) else: raise UnknownError( code=e.faultCode, error=e.faultString, server=url, ) # We don't care about the response, just that we got one break except KerberosError as krberr: # kerberos error on one server is likely on all raise errors.KerberosError(message=unicode(krberr)) except ProtocolError as e: if hasattr(context, 'session_cookie') and e.errcode == 401: # Unauthorized. Remove the session and try again. delattr(context, 'session_cookie') try: delete_persistent_client_session_data(principal) except Exception as e: # This shouldn't happen if we have a session but it isn't fatal. pass return self.create_connection(ccache, verbose, fallback, delegate) if not fallback: raise serverproxy = None except Exception as e: if not fallback: raise else: self.log.info('Connection to %s failed with %s', url, e) serverproxy = None if serverproxy is None: raise NetworkError(uri=_('any of the configured servers'), error=', '.join(urls)) return serverproxy
# This shouldn't happen if we have a session but it isn't fatal. pass return self.create_connection(ccache, verbose, fallback, delegate) if not fallback: raise serverproxy = None except Exception, e: if not fallback: raise else: self.log.info('Connection to %s failed with %s', url, e) serverproxy = None if serverproxy is None: raise NetworkError(uri=_('any of the configured servers'), error=', '.join(urls)) return serverproxy def destroy_connection(self): if sys.version_info >= (2, 7): conn = getattr(context, self.id, None) if conn is not None: conn = conn.conn._ServerProxy__transport conn.close() def _call_command(self, command, params): """Call the command with given params""" # For XML, this method will wrap/unwrap binary values # For JSON we do that in the proxy return command(*params)