예제 #1
0
    def _serverCommo(self):
        """ Handler part 2

            Server (or next proxy) communication.
        """

        log_debug(1)

        # Copy the method from the original request, and use the
        # handler for this server
        # We add path_info to the put (GET, CONNECT, HEAD, PUT, POST) request.
        log_debug(2, self.req.method, self.uri)
        self.responseContext.getConnection().putrequest(
            self.req.method, self.uri)

        # Send the headers, the body and expect a response
        try:
            status, headers, bodyFd = self._proxy2server()
            self.responseContext.setHeaders(headers)
            self.responseContext.setBodyFd(bodyFd)
        except IOError:
            # Raised by HTTP*Connection.getresponse
            # Server closed connection on us, no need to mail out
            # XXX: why are we not mailing this out???
            Traceback("SharedHandler._serverCommo", self.req, mail=0)
            raise_with_tb(
                rhnFault(
                    1000,
                    _("SUSE Manager Proxy error: connection with the SUSE Manager server failed"
                      )),
                sys.exc_info()[2])
        except socket.error:
            # maybe self.req.read() failed?
            Traceback("SharedHandler._serverCommo", self.req)
            raise_with_tb(
                rhnFault(
                    1000,
                    _("SUSE Manager Proxy error: connection with the SUSE Manager server failed"
                      )),
                sys.exc_info()[2])

        log_debug(2, "HTTP status code (200 means all is well): %s" % status)

        # Now we need to decide how to deal with the server's response.  We'll
        # defer to subclass-specific implementation here.  The handler will
        # return apache.OK if the request was a success.

        return self._handleServerResponse(status)
예제 #2
0
 def _wrapper(self, req, function_name):
     #log_debug(1, "_wrapper", req, function_name)
     if not hasattr(self.server, function_name):
         log_error("%s doesn't have a %s function" %
                   (self.server, function_name))
         return apache.HTTP_NOT_FOUND
     function = getattr(self.server, function_name)
     try:
         log_debug(5, "Calling", function)
         ret = function(req)
     except rhnFault:
         e = sys.exc_info()[1]
         log_debug(4, "rhnFault caught: %s" % (e, ))
         error_string = self._exception_to_text(e)
         error_code = e.code
         self._error_to_headers(req.err_headers_out, error_code,
                                error_string)
         ret = rhnFlags.get("apache-return-code")
         if not ret:
             ret = apache.HTTP_INTERNAL_SERVER_ERROR
         req.status = ret
         log_debug(
             4, "_wrapper %s exited with apache code %s" %
             (function_name, ret))
     except rhnSession.ExpiredSessionError:
         e = sys.exc_info()[1]
         # if session expires we catch here and return a forbidden
         # abd make it re-authenticate
         log_debug(4, "Expire Session Error Caught: %s" % (e, ))
         return 403
     except:
         Traceback("server.apacheUploadServer._wrapper", req=req)
         log_error("Unhandled exception")
         return apache.HTTP_INTERNAL_SERVER_ERROR
     return ret
예제 #3
0
    def _connectToParent(self):
        """ Handler part 1
            Should not return an error code -- simply connects.
        """

        scheme, host, port, self.uri = self._parse_url(self.rhnParent)
        self.responseContext.setConnection(self._create_connection())

        if not self.uri:
            self.uri = '/'

        log_debug(3, 'Scheme:', scheme)
        log_debug(3, 'Host:', host)
        log_debug(3, 'Port:', port)
        log_debug(3, 'URI:', self.uri)
        log_debug(3, 'HTTP proxy:', self.httpProxy)
        log_debug(3, 'HTTP proxy username:'******'HTTP proxy password:'******'CA cert:', self.caChain)

        try:
            self.responseContext.getConnection().connect()
        except socket.error, e:
            log_error("Error opening connection", self.rhnParent, e)
            Traceback(mail=0)
            raise rhnFault(1000,
                           _("Spacewalk Proxy could not successfully connect its RHN parent. "
                             "Please contact your system administrator.")), None, sys.exc_info()[2]
예제 #4
0
    def __getV2(self, action, dry_run=0):
        """ Fetches queued actions for the clients version 2+. """
        log_debug(3, self.server_id)
        # Get the root dir of this install
        try:
            method = getMethod.getMethod(action['method'],
                                         'server.action')
        except getMethod.GetMethodException:
            Traceback("queue.get V2")
            raise_with_tb(EmptyAction("Could not get a valid method for %s" % (
                action['method'],)), sys.exc_info()[2])
        # Call the method
        result = method(self.server_id, action['id'], dry_run)
        if result is None:
            # None are mapped to the empty list
            result = ()
        elif not isinstance(result, TupleType):
            # Everything other than a tuple is wrapped in a tuple
            result = (result, )

        xmlblob = xmlrpclib.dumps(result, methodname=action['method'])
        log_debug(5, "returning xmlblob for action", xmlblob)
        return {
            'id': action['id'],
            'action': xmlblob,
            'version': action['version'],
        }
예제 #5
0
    def response(self, response):
        # Send the xml-rpc response back
        log_debug(3, type(response))
        needs_xmlrpc_encoding = not rhnFlags.test("XMLRPC-Encoded-Response")
        compress_response = rhnFlags.test("compress_response")
        # Init an output object; we'll use it for sending data in various
        # formats
        if isinstance(response, transports.File):
            if not hasattr(response.file_obj, 'fileno') and compress_response:
                # This is a StringIO that has to be compressed, so read it in
                # memory; mark that we don't have to do any xmlrpc encoding
                response = response.file_obj.read()
                needs_xmlrpc_encoding = 0
            else:
                # Just treat is as a file
                return self.response_file(response)

        output = transports.Output()

        # First, use the same encoding/transfer that the client used
        output.set_transport_flags(
            transfer=transports.lookupTransfer(self.input.transfer),
            encoding=transports.lookupEncoding(self.input.encoding))

        if isinstance(response, xmlrpclib.Fault):
            log_debug(4, "Return FAULT", response.faultCode,
                      response.faultString)
            # No compression for faults because we'd like them to pop
            # up in clear text on the other side just in case
            output.set_transport_flags(output.TRANSFER_NONE,
                                       output.ENCODE_NONE)
        elif compress_response:
            # check if we have to compress this result
            log_debug(4, "Compression on for client version", self.client)
            if self.client > 0:
                output.set_transport_flags(output.TRANSFER_BINARY,
                                           output.ENCODE_ZLIB)
            else:  # original clients had the binary transport support broken
                output.set_transport_flags(output.TRANSFER_BASE64,
                                           output.ENCODE_ZLIB)

        # We simply add the transport options to the output headers
        output.headers.update(rhnFlags.get('outputTransportOptions').dict())

        if needs_xmlrpc_encoding:
            # Normalize the response
            response = self.normalize(response)
            try:
                response = xmlrpclib.dumps(response, methodresponse=1)
            except TypeError, e:
                log_debug(
                    4, "Error \"%s\" encoding response = %s" % (e, response))
                Traceback("apacheHandler.response",
                          self.req,
                          extra="Error \"%s\" encoding response = %s" %
                          (e, response),
                          severity="notification")
                return apache.HTTP_INTERNAL_SERVER_ERROR
            except:
예제 #6
0
    def add_hardware(self, hardware):
        """ add new hardware """
        log_debug(4, hardware)
        if not hardware:
            return -1
        if type(hardware) == type({}):
            hardware = UserDictCase(hardware)
        if not isinstance(hardware, UserDictCase):
            log_error("argument type is not  hash: %s" % hardware)
            raise TypeError, "This function requires a hash as an argument"
        # validation is important
        hw_class = hardware.get("class")
        if hw_class is None:
            return -1
        hw_class = string.lower(hw_class)

        class_type = None

        if hw_class in [
                "video", "audio", "audio_hd", "usb", "other", "hd", "floppy",
                "mouse", "modem", "network", "cdrom", "scsi", "unspec",
                "scanner", "tape", "capture", "raid", "socket", "keyboard",
                "printer", "firewire", "ide"
        ]:
            class_type = HardwareDevice
        elif hw_class == "cpu":
            class_type = CPUDevice
        elif hw_class == "netinfo":
            class_type = NetworkInformation
        elif hw_class == "memory":
            class_type = MemoryInformation
        elif hw_class == "dmi":
            class_type = DMIInformation
        elif hw_class == "installinfo":
            class_type = InstallInformation
        elif hw_class == "netinterfaces":
            class_type = NetIfaceInformation
        else:
            log_error("UNKNOWN CLASS TYPE `%s'" % hw_class)
            # Same trick: try-except and raise the exception so that Traceback
            # can send the e-mail
            try:
                raise KeyError, "Unknwon class type `%s' for hardware '%s'" % (
                    hw_class, hardware)
            except:
                Traceback(mail=1)
                return

        # create the new device
        new_dev = class_type(hardware)

        if self.__hardware.has_key(class_type):
            _l = self.__hardware[class_type]
        else:
            _l = self.__hardware[class_type] = []
        _l.append(new_dev)
        self.__changed = 1
        return 0
예제 #7
0
    def _connectToParent(self):
        """ Handler part 1
            Should not return an error code -- simply connects.
        """

        scheme, host, port, self.uri, query = self._parse_url(self.rhnParent)
        self.responseContext.setConnection(self._create_connection())

        if not self.uri:
            self.uri = '/'

        # if this request is for an upstream server, use the original query string.
        # Otherwise, if it is for the local Squid instance, strip it so that
        # Squid will not keep multiple cached copies of the same resource
        if self.httpProxy not in ['127.0.0.1:8080', 'localhost:8080']:
            if 'X-Suse-Auth-Token' in self.req.headers_in:
                self.uri += '?%s' % self.req.headers_in['X-Suse-Auth-Token']
            elif query:
                self.uri += '?%s' % query

        log_debug(3, 'Scheme:', scheme)
        log_debug(3, 'Host:', host)
        log_debug(3, 'Port:', port)
        log_debug(3, 'URI:', self.uri)
        log_debug(3, 'HTTP proxy:', self.httpProxy)
        log_debug(3, 'HTTP proxy username:'******'HTTP proxy password:'******'CA cert:', self.caChain)

        try:
            self.responseContext.getConnection().connect()
        except socket.error as e:
            log_error("Error opening connection", self.rhnParent, e)
            Traceback(mail=0)
            raise_with_tb(
                rhnFault(
                    1000,
                    _("SUSE Manager Proxy could not successfully connect its SUSE Manager parent. "
                      "Please contact your system administrator.")),
                sys.exc_info()[2])

        # At this point the server should be okay
        log_debug(3, "Connected to parent: %s " % self.rhnParent)
        if self.httpProxy:
            if self.httpProxyUsername:
                log_debug(
                    3, "HTTP proxy info: %s %s/<password>" %
                    (self.httpProxy, self.httpProxyUsername))
            else:
                log_debug(3, "HTTP proxy info: %s" % self.httpProxy)
        else:
            log_debug(3, "HTTP proxy info: not using an HTTP proxy")
        peer = self.responseContext.getConnection().sock.getpeername()
        log_debug(
            4, "Other connection info: %s:%s%s" % (peer[0], peer[1], self.uri))
예제 #8
0
 def __init__(self, fields, dict=None, mapping=None):
     GenericDevice.__init__(self)
     x = {}
     for k in fields:
         x[k] = None
     self.data = UserDictCase(x)
     if not dict:
         return
     # make sure we get a UserDictCase to work with
     if type(dict) == type({}):
         dict = UserDictCase(dict)
     if mapping is None or type(mapping) == type({}):
         mapping = UserDictCase(mapping)
     if not isinstance(dict, UserDictCase) or \
        not isinstance(mapping, UserDictCase):
         log_error("Argument passed is not a dictionary", dict, mapping)
         raise TypeError("Argument passed is not a dictionary", dict,
                         mapping)
     # make sure we have a platform
     for k in list(dict.keys()):
         if dict[k] == '':
             dict[k] = None
         if self.data.has_key(k):
             self.data[k] = dict[k]
             continue
         if mapping.has_key(k):
             # the mapping dict might tell us to lose some fields
             if mapping[k] is not None:
                 self.data[mapping[k]] = dict[k]
         else:
             log_error("Unknown HW key =`%s'" % k, dict.dict(),
                       mapping.dict())
             # The try-except is added just so that we can send e-mails
             try:
                 raise KeyError("Don't know how to parse key `%s''" % k,
                                dict.dict())
             except:
                 Traceback(mail=1)
                 # Ignore this key
                 continue
     # clean up this data
     try:
         for k in list(self.data.keys()):
             if type(self.data[k]) == type("") and len(self.data[k]):
                 self.data[k] = self.data[k].strip()
                 if not len(self.data[k]):
                     continue
                 if self.data[k][0] == '"' and self.data[k][-1] == '"':
                     self.data[k] = self.data[k][1:-1]
     except IndexError:
         raise_with_tb(
             IndexError("Can not process data = %s, key = %s" %
                        (repr(self.data), k)),
             sys.exc_info()[2])
예제 #9
0
 def process(self, stream=None):
     log_debug(6)
     if stream is not None:
         self.setStream(stream)
     try:
         self.__parser.parse(self.__stream)
     except (KeyboardInterrupt, SystemExit):
         raise
     except Exception:
         Traceback(ostream=sys.stderr, with_locals=1)
         if stream is not None:
             stream.close()
         sys.exit(1)
예제 #10
0
    def call_function(self, method, params):
        # short-circuit everything if sending a system-wide message.
        if CFG.SEND_MESSAGE_TO_ALL:
            # Make sure the applet doesn't see the message
            if method == 'applet.poll_status':
                return self.response({
                    'checkin_interval': 3600,
                    'server_status': 'normal'
                })
            if method == 'applet.poll_packages':
                return self.response({'use_cached_copy': 1})

            # Fetch global message being sent to clients if applicable.
            msg = open(CFG.MESSAGE_TO_ALL).read()
            log_debug(3, "Sending message to all clients: %s" % msg)
            # Send the message as a fault.
            response = xmlrpclib.Fault(
                -1,
                _("IMPORTANT MESSAGE FOLLOWS:\n%s") % msg)
            # and now send everything back
            ret = self.response(response)
            log_debug(4, "Leave with return value", ret)
            return ret

        # req: where the response is sent to
        log_debug(2, method)

        # Now we have the reference, call away
        force_rollback = 1
        try:
            rhnSQL.clear_log_id()
            # now get the function reference and call it
            func = self.method_ref(method)
            response = func(*params)
        except (TypeError, ValueError, KeyError, IndexError, UnknownXML):
            # report exception back to server
            fault = 1
            if sys.exc_type == UnknownXML:
                fault = -1
            e_type, e_value = sys.exc_info()[:2]
            response = xmlrpclib.Fault(
                fault,
                _("While running '%s': caught\n%s : %s\n") %
                (method, e_type, e_value))
            Traceback(method,
                      self.req,
                      extra="Response sent back to the caller:\n%s\n" %
                      (response.faultString, ),
                      severity="notification")
        except rhnNotFound, e:
            return apache.HTTP_NOT_FOUND
예제 #11
0
 def _wrapper(self, req, function):
     try:
         ret = function(req)
     except rhnFault:
         e = sys.exc_info()[1]
         return self._send_xmlrpc(req, e)
     except ClosedConnectionError:
         # The error code most likely doesn't matter, the client won't see
         # it anyway
         return apache.HTTP_NOT_ACCEPTABLE
     except Exception:  # pylint: disable=E0012, W0703
         Traceback("satexport._wrapper", req=req)
         return apache.HTTP_INTERNAL_SERVER_ERROR
     return ret
예제 #12
0
class GetHandler(apacheRequest):
    # we require our own init since we depend on a channel

    def __init__(self, client_version, req):
        apacheRequest.__init__(self, client_version, req)
        self.channel = None

    def _setup_servers(self):
        # Nothing to do here
        pass

    # get a function reference for the GET request
    def method_ref(self, method):
        log_debug(3, self.server, method)

        # Init the repository
        server_id = rhnFlags.get("AUTH_SESSION_TOKEN")['X-RHN-Server-Id']
        username = rhnFlags.get("AUTH_SESSION_TOKEN")['X-RHN-Auth-User-Id']
        repository = rhnRepository.Repository(self.channel, server_id,
                                              username)
        repository.set_qos()

        f = repository.get_function(method)
        if f is None:
            raise UnknownXML("function '%s' invalid; path_info is %s" %
                             (method, self.req.path_info))
        return f

    # handle the GET requests
    def process(self):
        log_debug(3)
        # Query repository; only after a clients signature has been
        # authenticated.

        try:
            method, params = self._get_method_params()
        except rhnFault, f:
            log_debug(2, "Fault caught")
            response = f.getxml()
            self.response(response)
            return apache.HTTP_NOT_FOUND
        except Exception, e:
            rhnSQL.rollback()
            # otherwise we do a full stop
            Traceback(method, self.req, severity="unhandled")
            return apache.HTTP_INTERNAL_SERVER_ERROR
예제 #13
0
    def _clientCommo(self, status=apache.OK):
        """ Handler part 3
            Forward server's response to the client.
        """
        log_debug(1)

        try:
            self._forwardServer2Client()
        except IOError:
            # Raised by HTTP*connection.getresponse
            # Client closed connection on us, no need to mail out a traceback
            Traceback("SharedHandler._clientCommo", self.req, mail=0)
            return apache.HTTP_SERVICE_UNAVAILABLE

        # Close all open response contexts.
        self.responseContext.clear()

        return status
예제 #14
0
    def process_extra_data(self, server_id, action_id, data={},
                           action_type=None):
        log_debug(4, server_id, action_id, action_type)

        if not action_type:
            # Shouldn't happen
            return

        try:
            method = getMethod.getMethod(action_type,
                                         'server.action_extra_data')
        except getMethod.GetMethodException:
            Traceback("queue.get V2")
            raise_with_tb(EmptyAction("Could not get a valid method for %s" %
                              action_type), sys.exc_info()[2])
        # Call the method
        result = method(self.server_id, action_id, data=data)
        return result
예제 #15
0
    def __request(self, methodname, params):
        # pylint: disable=R0915
        log_debug(6, methodname, params)
        # Init the socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        try:
            sock.connect(self.serverAddr)
        except socket.error, e:
            sock.close()
            methodname = None
            log_error("Error connecting to the auth cache: %s" % str(e))
            Traceback("Shelf.__request", extra="""
              Error connecting to the the authentication cache daemon.
              Make sure it is started on %s""" % str(self.serverAddr))
            # FIXME: PROBLEM: this rhnFault will never reach the client
            raise rhnFault(1000,
                           _("Spacewalk Proxy error (issues connecting to auth cache). "
                             "Please contact your system administrator")), None, sys.exc_info()[2]
예제 #16
0
    def _handler(self, req):
        log_debug(3, "Method", req.method)

        # Read all the request
        data = req.read()
        log_debug(7, "Received", data)

        # Decode the data
        try:
            params, methodname = xmlrpclib.loads(data)
        except:
            raise

        log_debug(5, params, methodname)

        try:
            f = self.get_function(methodname, req)
        except FunctionRetrievalError, e:
            Traceback(methodname, req)
            return self._send_xmlrpc(req, rhnFault(3008, str(e), explain=0))
예제 #17
0
    def set_cached_token(self, token):
        """ Caches current token in the auth cache.
        """
        log_debug(3)
        # Try to connect to the token-cache.
        shelf = get_auth_shelf()
        # Cache the token.
        try:
            shelf[self.__cache_proxy_key()] = token
        except:
            text = _("""\
Caching of authentication token for proxy id %s failed!
Either the authentication caching daemon is experiencing
problems, isn't running, or the token is somehow corrupt.
""") % self.__serverid
            Traceback("ProxyAuth.set_cached_token", extra=text)
            raise rhnFault(1000,
                           _("Spacewalk Proxy error (auth caching issue). "
                             "Please contact your system administrator.")), None, sys.exc_info()[2]
        log_debug(4, "successfully returning")
        return token
예제 #18
0
    def process(self):
        log_debug(3)
        # Query repository; only after a clients signature has been
        # authenticated.

        try:
            method, params = self._get_method_params()
        except rhnFault:
            f = sys.exc_info()[1]
            log_debug(2, "Fault caught")
            response = f.getxml()
            self.response(response)
            return apache.HTTP_NOT_FOUND
        except Exception:
            e = sys.exc_info()[1]
            rhnSQL.rollback()
            # otherwise we do a full stop
            Traceback(method, self.req, severity="unhandled")
            return apache.HTTP_INTERNAL_SERVER_ERROR
        # make the actual function call and return the result
        return self.call_function(method, params)
예제 #19
0
    def __call__(self, req):
        # NOTE: all imports done here due to required initialization of
        #       of the configuration module before all others.
        #       Initialization is dependent on RHNComponentType in the
        #       req object.

        if self.__init:
            from apacheHandler import getComponentType
            # We cannot trust the config files to tell us if we are in the
            # broker or in the redirect because we try to always pass
            # upstream all requests
            componentType = getComponentType(req)
            initCFG(componentType)
            initLOG(CFG.LOG_FILE, CFG.DEBUG)
            log_debug(1, 'New request, component %s' % (componentType, ))

        # Instantiate the handlers
        if HandlerWrap.svrHandlers is None:
            HandlerWrap.svrHandlers = self.get_handler_factory(req)()

        if self.__init:
            # Set the component type
            HandlerWrap.svrHandlers.set_component(componentType)

        try:
            log_setreq(req)
            if hasattr(HandlerWrap.svrHandlers, self.__name):
                f = getattr(HandlerWrap.svrHandlers, self.__name)
                ret = f(req)
            else:
                raise Exception("Class has no attribute %s" % self.__name)
        # pylint: disable=W0702
        except:
            Traceback(self.__name,
                      req,
                      extra="Unhandled exception type",
                      severity="unhandled")
            return apache.HTTP_INTERNAL_SERVER_ERROR
        else:
            return ret
예제 #20
0
    def _handler(self, req):
        log_debug(3, "Method", req.method)

        # Read all the request
        data = req.read()
        log_debug(7, "Received", data)

        # Decode the data
        try:
            params, methodname = xmlrpclib.loads(data)
        except:
            raise

        log_debug(5, params, methodname)

        try:
            f = self.get_function(methodname, req)
        except FunctionRetrievalError:
            e = sys.exc_info()[1]
            Traceback(methodname, req)
            return self._send_xmlrpc(req, rhnFault(3008, str(e), explain=0))

        if len(params) < 2:
            params = []
        else:
            params = params[1:]

        result = f(*params)

        if result:
            # Error of some sort
            return self._send_xmlrpc(req, rhnFault(3009))

        # Presumably the function did all the sending
        log_debug(4, "Exiting OK")
        return apache.OK
예제 #21
0
    def response(self, req, response):
        """ send the response (common code) """

        # Send the xml-rpc response back
        log_debug(5, "Response type", type(response))

        needs_xmlrpc_encoding = rhnFlags.test("NeedEncoding")
        compress_response = rhnFlags.test("compress_response")
        # Init an output object; we'll use it for sending data in various
        # formats
        if isinstance(response, rpclib.transports.File):
            if not hasattr(response.file_obj, 'fileno') and compress_response:
                # This is a StringIO that has to be compressed, so read it in
                # memory; mark that we don't have to do any xmlrpc encoding
                response = response.file_obj.read()
                needs_xmlrpc_encoding = 0
            else:
                # Just treat is as a file
                return self.response_file(req, response)

        is_fault = 0
        if isinstance(response, rhnFault):
            if req.method == 'GET':
                return self._response_fault_get(req, response.getxml())
            # Need to encode the response as xmlrpc
            response = response.getxml()
            is_fault = 1
            # No compression
            compress_response = 0
            # This is an xmlrpc Fault, so we have to encode it
            needs_xmlrpc_encoding = 1

        output = rpclib.transports.Output()

        if not is_fault:
            # First, use the same encoding/transfer that the client used
            output.set_transport_flags(
                transfer=rpclib.transports.lookupTransfer(self.input.transfer),
                encoding=rpclib.transports.lookupEncoding(self.input.encoding))

        if compress_response:
            # check if we have to compress this result
            log_debug(4, "Compression on for client version",
                      self.clientVersion)
            if self.clientVersion > 0:
                output.set_transport_flags(output.TRANSFER_BINARY,
                                           output.ENCODE_ZLIB)
            else:  # original clients had the binary transport support broken
                output.set_transport_flags(output.TRANSFER_BASE64,
                                           output.ENCODE_ZLIB)

        # We simply add the transport options to the output headers
        output.headers.update(rhnFlags.get('outputTransportOptions').dict())

        if needs_xmlrpc_encoding:
            # Normalize the response
            response = self.normalize(response)
            try:
                response = rpclib.xmlrpclib.dumps(response, methodresponse=1)
            except TypeError, e:
                log_debug(
                    -1, "Error \"%s\" encoding response = %s" % (e, response))
                Traceback("apacheHandler.response",
                          req,
                          extra="Error \"%s\" encoding response = %s" %
                          (e, response),
                          severity="notification")
                return apache.HTTP_INTERNAL_SERVER_ERROR
            except Exception:  # pylint: disable=E0012, W0703
                # Uncaught exception; signal the error
                Traceback("apacheHandler.response", req, severity="unhandled")
                return apache.HTTP_INTERNAL_SERVER_ERROR
예제 #22
0
class Shelf:
    """ Client authenication temp. db.

        Main class that the client side (client to the caching daemon) has to
        instantiate to expose the proper API. Basically, the API is a dictionary.
    """

    # pylint: disable=R0903

    def __init__(self, server_addr):
        log_debug(6, server_addr)
        self.serverAddr = server_addr

    def __request(self, methodname, params):
        # pylint: disable=R0915
        log_debug(6, methodname, params)
        # Init the socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        try:
            sock.connect(self.serverAddr)
        except socket.error, e:
            sock.close()
            methodname = None
            log_error("Error connecting to the auth cache: %s" % str(e))
            Traceback("Shelf.__request",
                      extra="""
              Error connecting to the the authentication cache daemon.
              Make sure it is started on %s""" % str(self.serverAddr))
            # FIXME: PROBLEM: this rhnFault will never reach the client
            raise rhnFault(
                1000,
                _("Spacewalk Proxy error (issues connecting to auth cache). "
                  "Please contact your system administrator")
            ), None, sys.exc_info()[2]

        wfile = sock.makefile("w")

        try:
            send(wfile, methodname, None, *params)
        except CommunicationError:
            wfile.close()
            sock.close()
            Traceback("Shelf.__request",
                      extra="Encountered a CommunicationError")
            raise
        except socket.error:
            wfile.close()
            sock.close()
            log_error("Error communicating to the auth cache: %s" % str(e))
            Traceback("Shelf.__request",
                      extra="""\
                     Error sending to the authentication cache daemon.
                     Make sure the authentication cache daemon is started""")
            # FIXME: PROBLEM: this rhnFault will never reach the client
            raise rhnFault(
                1000,
                _("Spacewalk Proxy error (issues connecting to auth cache). "
                  "Please contact your system administrator")
            ), None, sys.exc_info()[2]

        wfile.close()

        rfile = sock.makefile("r")
        try:
            params, methodname = recv(rfile)
        except CommunicationError, e:
            log_error(e.faultString)
            rfile.close()
            sock.close()
            log_error("Error communicating to the auth cache: %s" % str(e))
            Traceback("Shelf.__request",
                      extra="""\
                      Error receiving from the authentication cache daemon.
                      Make sure the authentication cache daemon is started""")
            # FIXME: PROBLEM: this rhnFault will never reach the client
            raise rhnFault(
                1000,
                _("Spacewalk Proxy error (issues communicating to auth cache). "
                  "Please contact your system administrator")
            ), None, sys.exc_info()[2]
예제 #23
0
    def __redirectToNextLocationNoRetry(self, loopProtection=False):
        """ This function will perform a redirection to the next location, as
            specified in the last response's "Location" header. This function will
            return an actual HTTP response status code.  If successful, it will
            return apache.HTTP_OK, not apache.OK.  If unsuccessful, this function
            will simply return; no retries will be performed.  The following error
            codes can be returned:

            HTTP_OK,HTTP_PARTIAL_CONTENT - Redirect successful.
            HTTP_MOVED_TEMPORARILY     - Redirect was redirected again by 3rd party.
            HTTP_MOVED_PERMANENTLY     - Redirect was redirected again by 3rd party.
            HTTP_INTERNAL_SERVER_ERROR - Error extracting redirect information
            HTTP_SERVICE_UNAVAILABLE   - Could not connect to 3rd party server,
                                         connection was reset, or a read error
                                         occurred during communication.
            HTTP_*                     - Any other HTTP status code may also be
                                         returned.

            Upon successful completion of this function, a new responseContext
            will be created and pushed onto the stack.
        """

        # Obtain the redirect location first before we replace the current
        # response context.  It's contained in the Location header of the
        # previous response.

        redirectLocation = self._get_header(rhnConstants.HEADER_LOCATION)

        # We are about to redirect to a new location so now we'll push a new
        # response context before we return any errors.
        self.responseContext.add()

        # There should always be a redirect URL passed back to us.  If not,
        # there's an error.

        if not redirectLocation:
            log_error("  No redirect location specified!")
            Traceback(mail=0)
            return apache.HTTP_INTERNAL_SERVER_ERROR

        # The _get_header function returns the value as a list.  There should
        # always be exactly one location specified.

        redirectLocation = redirectLocation[0]
        log_debug(1, "  Redirecting to: ", redirectLocation)

        # Tear apart the redirect URL.  We need the scheme, the host, the
        # port (if not the default), and the URI.

        _scheme, host, port, uri, query = self._parse_url(redirectLocation)

        # Add back the query string
        if query:
            uri += '?' + query

        # Now create a new connection.  We'll use SSL if configured to do
        # so.

        params = {
            'host': host,
            'port': port,
        }
        if CFG.has_key('timeout'):
            params['timeout'] = CFG.TIMEOUT
        if CFG.USE_SSL:
            log_debug(1, "  Redirecting with SSL.  Cert= ", self.caChain)
            params['trusted_certs'] = [self.caChain]
            connection = connections.HTTPSConnection(**params)
        else:
            log_debug(1, "  Redirecting withOUT SSL.")
            connection = connections.HTTPConnection(**params)

        # Put the connection into the current response context.
        self.responseContext.setConnection(connection)

        # Now open the connection to the 3rd party server.

        log_debug(4, "Attempting to connect to 3rd party server...")
        try:
            connection.connect()
        except socket.error as e:
            log_error("Error opening redirect connection", redirectLocation, e)
            Traceback(mail=0)
            return apache.HTTP_SERVICE_UNAVAILABLE
        log_debug(4, "Connected to 3rd party server:",
                  connection.sock.getpeername())

        # Put the request out on the wire.

        response = None
        try:
            # We'll redirect to the URI made in the original request, but with
            # the new server instead.

            log_debug(4, "Making request: ", self.req.method, uri)
            connection.putrequest(self.req.method, uri)

            # Add some custom headers.

            if loopProtection:
                connection.putheader(rhnConstants.HEADER_RHN_REDIRECT, '0')

            log_debug(4, "  Adding original URL header: ", self.rhnParent)
            connection.putheader(rhnConstants.HEADER_RHN_ORIG_LOC,
                                 self.rhnParent)

            # Add all the other headers in the original request in case we
            # need to re-authenticate with Hosted.

            for hdr in list(self.req.headers_in.keys()):
                if hdr.lower().startswith("x-rhn"):
                    connection.putheader(hdr, self.req.headers_in[hdr])
                    log_debug(4, "Passing request header: ", hdr,
                              self.req.headers_in[hdr])

            connection.endheaders()

            response = connection.getresponse()
        except IOError as ioe:
            # Raised by getresponse() if server closes connection on us.
            log_error("Redirect connection reset by peer.", redirectLocation,
                      ioe)
            Traceback(mail=0)

            # The connection is saved in the current response context, and
            # will be closed when the caller pops the context.
            return apache.HTTP_SERVICE_UNAVAILABLE

        except socket.error as se:
            # Some socket error occurred.  Possibly a read error.
            log_error("Redirect request failed.", redirectLocation, se)
            Traceback(mail=0)

            # The connection is saved in the current response context, and
            # will be closed when the caller pops the context.
            return apache.HTTP_SERVICE_UNAVAILABLE

        # Save the response headers and body FD in the current communication
        # context.

        self.responseContext.setBodyFd(response)
        self.responseContext.setHeaders(response.msg)

        log_debug(4, "Response headers: ",
                  list(self.responseContext.getHeaders().items()))
        log_debug(4, "Got redirect response.  Status=", response.status)

        # Return the HTTP status to the caller.

        return response.status
예제 #24
0
    def response(self, response):
        # Send the xml-rpc response back
        log_debug(3, type(response))
        needs_xmlrpc_encoding = not rhnFlags.test("XMLRPC-Encoded-Response")
        compress_response = rhnFlags.test("compress_response")
        # Init an output object; we'll use it for sending data in various
        # formats
        if isinstance(response, transports.File):
            if not hasattr(response.file_obj, 'fileno') and compress_response:
                # This is a StringIO that has to be compressed, so read it in
                # memory; mark that we don't have to do any xmlrpc encoding
                response = response.file_obj.read()
                needs_xmlrpc_encoding = 0
            else:
                # Just treat is as a file
                return self.response_file(response)

        output = transports.Output()

        # First, use the same encoding/transfer that the client used
        output.set_transport_flags(
            transfer=transports.lookupTransfer(self.input.transfer),
            encoding=transports.lookupEncoding(self.input.encoding))

        if isinstance(response, xmlrpclib.Fault):
            log_debug(4, "Return FAULT",
                      response.faultCode, response.faultString)
            # No compression for faults because we'd like them to pop
            # up in clear text on the other side just in case
            output.set_transport_flags(output.TRANSFER_NONE, output.ENCODE_NONE)
        elif compress_response:
            # check if we have to compress this result
            log_debug(4, "Compression on for client version", self.client)
            if self.client > 0:
                output.set_transport_flags(output.TRANSFER_BINARY,
                                           output.ENCODE_ZLIB)
            else:  # original clients had the binary transport support broken
                output.set_transport_flags(output.TRANSFER_BASE64,
                                           output.ENCODE_ZLIB)

        # We simply add the transport options to the output headers
        output.headers.update(rhnFlags.get('outputTransportOptions').dict())

        if needs_xmlrpc_encoding:
            # Normalize the response
            response = self.normalize(response)
            try:
                response = xmlrpclib.dumps(response, methodresponse=1)
            except TypeError:
                e = sys.exc_info()[1]
                log_debug(4, "Error \"%s\" encoding response = %s" % (e, response))
                Traceback("apacheHandler.response", self.req,
                          extra="Error \"%s\" encoding response = %s" % (e, response),
                          severity="notification")
                return apache.HTTP_INTERNAL_SERVER_ERROR
            except:
                # Uncaught exception; signal the error
                Traceback("apacheHandler.response", self.req,
                          severity="unhandled")
                return apache.HTTP_INTERNAL_SERVER_ERROR

        # we're about done here, patch up the headers
        output.process(response)
        # Copy the rest of the fields
        for k, v in output.headers.items():
            if string.lower(k) == 'content-type':
                # Content-type
                self.req.content_type = v
            else:
                setHeaderValue(self.req.headers_out, k, v)

        if 5 <= CFG.DEBUG < 10:
            log_debug(5, "The response: %s[...SNIP (for sanity) SNIP...]%s" % (response[:100], response[-100:]))
        elif CFG.DEBUG >= 10:
            # if you absolutely must have that whole response in the log file
            log_debug(10, "The response: %s" % response)

        # send the headers
        self.req.send_http_header()
        try:
            # XXX: in case data is really large maybe we should split
            # it in smaller chunks instead of blasting everything at
            # once. Not yet a problem...
            self.req.write(output.data)
        except IOError:
            # send_http_header is already sent, so it doesn't make a lot of
            # sense to return a non-200 error; but there is no better solution
            return apache.HTTP_BAD_REQUEST
        del output
        return apache.OK
예제 #25
0
    def call_function(self, method, params):
        # short-circuit everything if sending a system-wide message.
        if CFG.SEND_MESSAGE_TO_ALL:
            # Make sure the applet doesn't see the message
            if method == 'applet.poll_status':
                return self.response({
                    'checkin_interval': 3600,
                    'server_status': 'normal'
                })
            if method == 'applet.poll_packages':
                return self.response({'use_cached_copy': 1})

            # Fetch global message being sent to clients if applicable.
            msg = open(CFG.MESSAGE_TO_ALL).read()
            log_debug(3, "Sending message to all clients: %s" % msg)
            # Send the message as a fault.
            response = xmlrpclib.Fault(
                -1, _("IMPORTANT MESSAGE FOLLOWS:\n%s") % msg)
            # and now send everything back
            ret = self.response(response)
            log_debug(4, "Leave with return value", ret)
            return ret

        # req: where the response is sent to
        log_debug(2, method)

        # Now we have the reference, call away
        force_rollback = 1
        try:
            rhnSQL.clear_log_id()
            # now get the function reference and call it
            func = self.method_ref(method)
            response = func(*params)
        except (TypeError, ValueError, KeyError, IndexError, UnknownXML):
            # report exception back to server
            fault = 1

            if sys.version_info[0] == 3:
                exctype = sys.exc_info()[0]
            else:
                exctype = sys.exc_type

            if exctype == UnknownXML:
                fault = -1
            e_type, e_value = sys.exc_info()[:2]
            response = xmlrpclib.Fault(fault, _(
                "While running '%s': caught\n%s : %s\n") % (
                method, e_type, e_value))
            Traceback(method, self.req,
                      extra="Response sent back to the caller:\n%s\n" % (
                          response.faultString,),
                      severity="notification")
        except rhnNotFound:
            e = sys.exc_info()[1]
            return apache.HTTP_NOT_FOUND
        # pkilambi:catch exception if redirect
        except redirectException:
            re = sys.exc_info()[1]
            log_debug(3, "redirect exception caught", re.path)
            response = re.path

        except rhnFault:
            f = sys.exc_info()[1]
            response = f.getxml()
        except rhnSQL.SQLSchemaError:
            e = sys.exc_info()[1]
            f = None
            if e.errno == 20200:
                log_debug(2, "User Group Membership EXCEEDED")
                f = rhnFault(43, e.errmsg)
            if not f:
                log_error("rhnSQL.SQLSchemaError caught", e)
                rhnSQL.rollback()
                # generate the traceback report
                Traceback(method, self.req,
                          extra="SQL Error generated: %s" % e,
                          severity="schema")
                return apache.HTTP_INTERNAL_SERVER_ERROR
            response = f.getxml()
        except rhnSQL.SQLError:
            e = sys.exc_info()[1]
            log_error("rhnSQL.SQLError caught", e)
            rhnSQL.rollback()
            Traceback(method, self.req,
                      extra="SQL Error generated: %s" % e,
                      severity="schema")
            return apache.HTTP_INTERNAL_SERVER_ERROR
        except Exception:
            e = sys.exc_info()[1]
            log_error("Unhandled exception", e)
            rhnSQL.rollback()
            # otherwise we do a full stop
            Traceback(method, self.req, severity="unhandled")
            return apache.HTTP_INTERNAL_SERVER_ERROR
        else:
            # if no exception, we don't need to rollback
            force_rollback = 0
        if force_rollback:
            rhnSQL.rollback()
        rhnSQL.clear_log_id()
        # and now send everything back
        ret = self.response(response)
        log_debug(4, "Leave with return value", ret)
        return ret
예제 #26
0
            response = connection.getresponse()
        except IOError, ioe:
            # Raised by getresponse() if server closes connection on us.
            log_error("Redirect connection reset by peer.", redirectLocation,
                      ioe)
            Traceback(mail=0)

            # The connection is saved in the current response context, and
            # will be closed when the caller pops the context.
            return apache.HTTP_SERVICE_UNAVAILABLE

        except socket.error, se:
            # Some socket error occurred.  Possibly a read error.
            log_error("Redirect request failed.", redirectLocation, se)
            Traceback(mail=0)

            # The connection is saved in the current response context, and
            # will be closed when the caller pops the context.
            return apache.HTTP_SERVICE_UNAVAILABLE

        # Save the response headers and body FD in the current communication
        # context.

        self.responseContext.setBodyFd(response)
        self.responseContext.setHeaders(response.msg)

        log_debug(4, "Response headers: ",
                  self.responseContext.getHeaders().items())
        log_debug(4, "Got redirect response.  Status=", response.status)
예제 #27
0
    def __redirectToNextLocationNoRetry(self, loopProtection=False):
        """ This function will perform a redirection to the next location, as
            specified in the last response's "Location" header. This function will
            return an actual HTTP response status code.  If successful, it will
            return apache.HTTP_OK, not apache.OK.  If unsuccessful, this function
            will simply return; no retries will be performed.  The following error
            codes can be returned:

            HTTP_OK,HTTP_PARTIAL_CONTENT - Redirect successful.
            HTTP_MOVED_TEMPORARILY     - Redirect was redirected again by 3rd party.
            HTTP_MOVED_PERMANENTLY     - Redirect was redirected again by 3rd party.
            HTTP_INTERNAL_SERVER_ERROR - Error extracting redirect information
            HTTP_SERVICE_UNAVAILABLE   - Could not connect to 3rd party server,
                                         connection was reset, or a read error
                                         occurred during communication.
            HTTP_*                     - Any other HTTP status code may also be
                                         returned.

            Upon successful completion of this function, a new responseContext
            will be created and pushed onto the stack.
        """

        # Obtain the redirect location first before we replace the current
        # response context.  It's contained in the Location header of the
        # previous response.

        redirectLocation = self._get_header(rhnConstants.HEADER_LOCATION)

        # We are about to redirect to a new location so now we'll push a new
        # response context before we return any errors.
        self.responseContext.add()

        # There should always be a redirect URL passed back to us.  If not,
        # there's an error.

        if not redirectLocation or len(redirectLocation) == 0:
            log_error("  No redirect location specified!")
            Traceback(mail=0)
            return apache.HTTP_INTERNAL_SERVER_ERROR

        # The _get_header function returns the value as a list.  There should
        # always be exactly one location specified.

        redirectLocation = redirectLocation[0]
        log_debug(1, "  Redirecting to: ", redirectLocation)

        # Tear apart the redirect URL.  We need the scheme, the host, the
        # port (if not the default), and the URI.

        _scheme, host, port, uri = self._parse_url(redirectLocation)

        # Add any params onto the URI since _parse_url doesn't include them.
        if redirectLocation.find('?') > -1:
            uri += redirectLocation[redirectLocation.index('?'):]

        # Now create a new connection.  We'll use SSL if configured to do
        # so.

        params = {
            'host': host,
            'port': port,
        }
        if CFG.has_key('timeout'):
            params['timeout'] = CFG.TIMEOUT
        if CFG.USE_SSL:
            log_debug(1, "  Redirecting with SSL.  Cert= ", self.caChain)
            params['trusted_certs'] = [self.caChain]
            connection = connections.HTTPSConnection(**params)
        else:
            log_debug(1, "  Redirecting withOUT SSL.")
            connection = connections.HTTPConnection(**params)

        # Put the connection into the current response context.
        self.responseContext.setConnection(connection)

        # Now open the connection to the 3rd party server.

        log_debug(4, "Attempting to connect to 3rd party server...")
        try:
            connection.connect()
        except socket.error, e:
            log_error("Error opening redirect connection", redirectLocation, e)
            Traceback(mail=0)
            return apache.HTTP_SERVICE_UNAVAILABLE
예제 #28
0
             # rather big problem: http proxy not running.
             log_error("*** ERROR ***: %s" % error[1])
             Traceback(mail=0)
         except socket.sslerror, e:
             error = ['socket.sslerror',
                      '(%s) %s' % (CFG.HTTP_PROXY, e)]
             # rather big problem: http proxy not running.
             log_error("*** ERROR ***: %s" % error[1])
             Traceback(mail=0)
         else:
             error = ['socket', str(e)]
             log_error(error)
             Traceback(mail=0)
     else:
         log_error("Socket error", e)
         Traceback(mail=0)
     Traceback(mail=1)
     token = None
     time.sleep(.25)
     continue
 except SSL.SSL.Error, e:
     token = None
     error = ['rhn.SSL.SSL.Error', repr(e), str(e)]
     log_error(error)
     Traceback(mail=0)
     time.sleep(.25)
     continue
 except xmlrpclib.ProtocolError, e:
     token = None
     log_error('xmlrpclib.ProtocolError', e)
     time.sleep(.25)
예제 #29
0
class RedirectHandler(SharedHandler):
    """ Spacewalk Proxy SSL Redirect specific handler code called by rhnApache.

        Workflow is:
        Client -> Apache:Broker -> Squid -> Apache:Redirect -> Satellite

        Redirect handler get all request for localhost:80 and they come
        from Broker handler through Squid, which hadle caching.
        Redirect module transform destination url to parent or http proxy.
        Depend on what we have in CFG.
    """
    def __init__(self, req):
        SharedHandler.__init__(self, req)
        self.componentType = 'proxy.redirect'
        self._initConnectionVariables(req)
        self.rhnParentXMLRPC = None

    def _initConnectionVariables(self, _req):
        """ set connection variables
            NOTE: self.{caChain,rhnParent,httpProxy*} are initialized
                  in SharedHandler
        """

        effectiveURI = self._getEffectiveURI()
        effectiveURI_parts = urlparse(effectiveURI)
        scheme = 'http'
        if CFG.USE_SSL:
            scheme = 'https'
        else:
            self.caChain = ''
        self.rhnParentXMLRPC = urlunparse(
            (scheme, self.rhnParent, '/XMLRPC', '', '', ''))
        self.rhnParent = urlunparse((scheme, self.rhnParent) +
                                    effectiveURI_parts[2:])

        log_debug(3, 'remapped self.rhnParent:       %s' % self.rhnParent)
        log_debug(3,
                  'remapped self.rhnParentXMLRPC: %s' % self.rhnParentXMLRPC)

    def handler(self):
        """ Main handler for all requests pumped through this server. """

        log_debug(4, 'In redirect handler')
        self._prepHandler()

        # Rebuild the X-Forwarded-For header so that it reflects the actual
        # path of the request.  We must do this because squid is unable to
        # determine the "real" client, and will make each entry in the chain
        # 127.0.0.1.
        _oto = rhnFlags.get('outputTransportOptions')
        _oto['X-Forwarded-For'] = _oto['X-RHN-IP-Path']

        self.rhnParent = self.rhnParent or ''  # paranoid

        log_debug(4, 'Connecting to parent...')
        self._connectToParent()  # part 1

        log_debug(4, 'Initiating communication with server...')
        status = self._serverCommo()  # part 2
        if (status != apache.OK) and (status != apache.HTTP_PARTIAL_CONTENT):
            log_debug(3, "Leaving handler with status code %s" % status)
            return status

        log_debug(4, 'Initiating communication with client...')
        # If we got this far, it has to be a good response
        return self._clientCommo(status)

    def _handleServerResponse(self, status):
        """ Here, we'll override the default behavior for handling server responses
            so that we can adequately handle 302's.

            We will follow redirects unless it is redirect to (re)login page. In which
            case we change protocol to https and return redirect to user.
        """

        # In case of a 302, redirect the original request to the location
        # specified in the response.

        if status == apache.HTTP_MOVED_TEMPORARILY or \
           status == apache.HTTP_MOVED_PERMANENTLY:

            log_debug(1, "Received redirect response: ", status)

            # if we redirected to ssl version of login page, send redirect directly to user
            headers = self.responseContext.getHeaders()
            if headers is not None:
                for headerKey in headers.keys():
                    if headerKey == 'location':
                        location = self._get_header(headerKey)
                        relogin = re.compile(
                            r'https?://.*(/rhn/(Re)?Login.do\?.*)')
                        m = relogin.match(location[0])
                        if m:
                            # pull server name out of "t:o:k:e:n:hostname1,t:o:k:e:n:hostname2,..."
                            proxy_auth = self.req.headers_in[
                                'X-RHN-Proxy-Auth']
                            last_auth = proxy_auth.split(',')[-1]
                            server_name = last_auth.split(':')[-1]
                            log_debug(
                                1, "Redirecting to SSL version of login page")
                            rhnLib.setHeaderValue(
                                self.req.headers_out, 'Location',
                                "https://%s%s" % (server_name, m.group(1)))
                            return apache.HTTP_MOVED_PERMANENTLY

            redirectStatus = self.__redirectToNextLocation()

            # At this point, we've either:
            #
            #     (a) successfully redirected to the 3rd party
            #     (b) been told to redirect somewhere else from the 3rd party
            #     (c) run out of retry attempts
            #
            # We'll keep redirecting until we've received HTTP_OK or an error.

            while redirectStatus == apache.HTTP_MOVED_PERMANENTLY or \
                  redirectStatus == apache.HTTP_MOVED_TEMPORARILY:

                # We've been told to redirect again.  We'll pass a special
                # argument to ensure that if we end up back at the server, we
                # won't be redirected again.

                log_debug(1, "Redirected again!  Code=", redirectStatus)
                redirectStatus = self.__redirectToNextLocation(True)

            if (redirectStatus != apache.HTTP_OK) and (
                    redirectStatus != apache.HTTP_PARTIAL_CONTENT):

                # We must have run out of retry attempts.  Fail over to Hosted
                # to perform the request.

                log_debug(1, "Redirection failed; retries exhausted.  " \
                             "Failing over.  Code=",                    \
                             redirectStatus)
                redirectStatus = self.__redirectFailover()

            return SharedHandler._handleServerResponse(self, redirectStatus)

        else:
            # Otherwise, revert to default behavior.
            return SharedHandler._handleServerResponse(self, status)

    def __redirectToNextLocation(self, loopProtection=False):
        """ This function will perform a redirection to the next location, as
            specified in the last response's "Location" header. This function will
            return an actual HTTP response status code.  If successful, it will
            return apache.HTTP_OK, not apache.OK.  If unsuccessful, this function
            will retry a configurable number of times, as defined in
            CFG.NETWORK_RETRIES.  The following codes define "success".

              HTTP_OK
              HTTP_PARTIAL_CONTENT
              HTTP_MOVED_TEMPORARILY
              HTTP_MOVED_PERMANENTLY

            Upon successful completion of this function, the responseContext
            should be populated with the response.

            Arguments:

            loopProtection - If True, this function will insert a special
                           header into the new request that tells the RHN
                           server not to issue another redirect to us, in case
                           that's where we end up being redirected.

            Return:

            This function may return any valid HTTP_* response code.  See
            __redirectToNextLocationNoRetry for more info.
        """
        retriesLeft = CFG.NETWORK_RETRIES

        # We'll now try to redirect to the 3rd party.  We will keep
        # retrying until we exhaust the number of allowed attempts.
        # Valid response codes are:
        #     HTTP_OK
        #     HTTP_PARTIAL_CONTENT
        #     HTTP_MOVED_PERMANENTLY
        #     HTTP_MOVED_TEMPORARILY

        redirectStatus = self.__redirectToNextLocationNoRetry(loopProtection)
        while redirectStatus != apache.HTTP_OK                and \
              redirectStatus != apache.HTTP_PARTIAL_CONTENT   and \
              redirectStatus != apache.HTTP_MOVED_PERMANENTLY and \
              redirectStatus != apache.HTTP_MOVED_TEMPORARILY and \
              retriesLeft > 0:

            retriesLeft = retriesLeft - 1
            log_debug(1, "Redirection failed; trying again.  " \
                         "Retries left=",                      \
                         retriesLeft,                          \
                         "Code=",                              \
                         redirectStatus)

            # Pop the current response context and restore the state to
            # the last successful response.  The acts of remove the current
            # context will cause all of its open connections to be closed.
            self.responseContext.remove()

            # XXX: Possibly sleep here for a second?
            redirectStatus = \
                self.__redirectToNextLocationNoRetry(loopProtection)

        return redirectStatus

    def __redirectToNextLocationNoRetry(self, loopProtection=False):
        """ This function will perform a redirection to the next location, as
            specified in the last response's "Location" header. This function will
            return an actual HTTP response status code.  If successful, it will
            return apache.HTTP_OK, not apache.OK.  If unsuccessful, this function
            will simply return; no retries will be performed.  The following error
            codes can be returned:

            HTTP_OK,HTTP_PARTIAL_CONTENT - Redirect successful.
            HTTP_MOVED_TEMPORARILY     - Redirect was redirected again by 3rd party.
            HTTP_MOVED_PERMANENTLY     - Redirect was redirected again by 3rd party.
            HTTP_INTERNAL_SERVER_ERROR - Error extracting redirect information
            HTTP_SERVICE_UNAVAILABLE   - Could not connect to 3rd party server,
                                         connection was reset, or a read error
                                         occurred during communication.
            HTTP_*                     - Any other HTTP status code may also be
                                         returned.

            Upon successful completion of this function, a new responseContext
            will be created and pushed onto the stack.
        """

        # Obtain the redirect location first before we replace the current
        # response context.  It's contained in the Location header of the
        # previous response.

        redirectLocation = self._get_header(rhnConstants.HEADER_LOCATION)

        # We are about to redirect to a new location so now we'll push a new
        # response context before we return any errors.
        self.responseContext.add()

        # There should always be a redirect URL passed back to us.  If not,
        # there's an error.

        if not redirectLocation or len(redirectLocation) == 0:
            log_error("  No redirect location specified!")
            Traceback(mail=0)
            return apache.HTTP_INTERNAL_SERVER_ERROR

        # The _get_header function returns the value as a list.  There should
        # always be exactly one location specified.

        redirectLocation = redirectLocation[0]
        log_debug(1, "  Redirecting to: ", redirectLocation)

        # Tear apart the redirect URL.  We need the scheme, the host, the
        # port (if not the default), and the URI.

        _scheme, host, port, uri = self._parse_url(redirectLocation)

        # Add any params onto the URI since _parse_url doesn't include them.
        if redirectLocation.find('?') > -1:
            uri += redirectLocation[redirectLocation.index('?'):]

        # Now create a new connection.  We'll use SSL if configured to do
        # so.

        params = {
            'host': host,
            'port': port,
        }
        if CFG.has_key('timeout'):
            params['timeout'] = CFG.TIMEOUT
        if CFG.USE_SSL:
            log_debug(1, "  Redirecting with SSL.  Cert= ", self.caChain)
            params['trusted_certs'] = [self.caChain]
            connection = connections.HTTPSConnection(**params)
        else:
            log_debug(1, "  Redirecting withOUT SSL.")
            connection = connections.HTTPConnection(**params)

        # Put the connection into the current response context.
        self.responseContext.setConnection(connection)

        # Now open the connection to the 3rd party server.

        log_debug(4, "Attempting to connect to 3rd party server...")
        try:
            connection.connect()
        except socket.error, e:
            log_error("Error opening redirect connection", redirectLocation, e)
            Traceback(mail=0)
            return apache.HTTP_SERVICE_UNAVAILABLE
        log_debug(4, "Connected to 3rd party server:",
                  connection.sock.getpeername())

        # Put the request out on the wire.

        response = None
        try:
            # We'll redirect to the URI made in the original request, but with
            # the new server instead.

            log_debug(4, "Making request: ", self.req.method, uri)
            connection.putrequest(self.req.method, uri)

            # Add some custom headers.

            if loopProtection:
                connection.putheader(rhnConstants.HEADER_RHN_REDIRECT, '0')

            log_debug(4, "  Adding original URL header: ", self.rhnParent)
            connection.putheader(rhnConstants.HEADER_RHN_ORIG_LOC,
                                 self.rhnParent)

            # Add all the other headers in the original request in case we
            # need to re-authenticate with Hosted.

            for hdr in self.req.headers_in.keys():
                if hdr.lower().startswith("x-rhn"):
                    connection.putheader(hdr, self.req.headers_in[hdr])
                    log_debug(4, "Passing request header: ", hdr,
                              self.req.headers_in[hdr])

            connection.endheaders()

            response = connection.getresponse()
        except IOError, ioe:
            # Raised by getresponse() if server closes connection on us.
            log_error("Redirect connection reset by peer.", redirectLocation,
                      ioe)
            Traceback(mail=0)

            # The connection is saved in the current response context, and
            # will be closed when the caller pops the context.
            return apache.HTTP_SERVICE_UNAVAILABLE
예제 #30
0
    def login(self):
        """ Login and fetch new token (proxy token).

            How it works in a nutshell.
            Only the broker component uses this. We perform a xmlrpc request
            to rhn_parent. This occurs outside of the http process we are
            currently working on. So, we do this all on our own; do all of
            our own SSL decisionmaking etc. We use CFG.RHN_PARENT as we always
            bypass the SSL redirect.

            DESIGN NOTES:  what is the proxy auth token?
            -------------------------------------------
            An Spacewalk Proxy auth token is a token fetched upon login from
            Red Hat Satellite or hosted.

            It has this format:
               'S:U:ST:EO:SIG'
            Where:
               S   = server ID
               U   = username
               ST  = server time
               EO  = expiration offset
               SIG = signature
               H   = hostname (important later)

            Within this function within the Spacewalk Proxy Broker we also tag on
            the hostname to the end of the token. The token as described above
            is enough for authentication purposes, but we need a to identify
            the exact hostname (as the Spacewalk Proxy sees it). So now the token
            becomes (token:hostname):
               'S:U:ST:EO:SIG:H'

            DESIGN NOTES:  what is X-RHN-Proxy-Auth?
            -------------------------------------------
            This is where we use the auth token beyond Spacewalk Proxy login
            purposes. This a header used to track request routes through
            a hierarchy of RHN Proxies.

            X-RHN-Proxy-Auth is a header that passes proxy authentication
            information around in the form of an ordered list of tokens. This
            list is used to gain information as to how a client request is
            routed throughout an RHN topology.

            Format: 'S1:U1:ST1:EO1:SIG1:H1,S2:U2:ST2:EO2:SIG2:H2,...'
                     |_________1_________| |_________2_________| |__...
                             token                 token
                     where token is really: token:hostname

            leftmost token was the first token hit by a client request.
            rightmost token was the last token hit by a client request.

        """
        # pylint: disable=R0915

        log_debug(3)
        server = self.__getXmlrpcServer()
        error = None
        token = None
        # update the systemid/serverid if need be.
        self.__processSystemid()
        # Makes three attempts to login
        for _i in range(self.__nRetries):
            try:
                token = server.proxy.login(self.__systemid)
            except (socket.error, socket.sslerror), e:
                if CFG.HTTP_PROXY:
                    # socket error, check to see if your HTTP proxy is running...
                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    httpProxy, httpProxyPort = CFG.HTTP_PROXY.split(':')
                    try:
                        s.connect((httpProxy, int(httpProxyPort)))
                    except socket.error, e:
                        error = ['socket.error', 'HTTP Proxy not running? '
                                 '(%s) %s' % (CFG.HTTP_PROXY, e)]
                        # rather big problem: http proxy not running.
                        log_error("*** ERROR ***: %s" % error[1])
                        Traceback(mail=0)
                    except socket.sslerror, e:
                        error = ['socket.sslerror',
                                 '(%s) %s' % (CFG.HTTP_PROXY, e)]
                        # rather big problem: http proxy not running.
                        log_error("*** ERROR ***: %s" % error[1])
                        Traceback(mail=0)
                    else:
                        error = ['socket', str(e)]
                        log_error(error)
                        Traceback(mail=0)