Exemplo n.º 1
0
    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))
Exemplo n.º 2
0
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))
Exemplo n.º 3
0
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))
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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))
Exemplo n.º 9
0
    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))
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
                        # 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)