Beispiel #1
0
    def __call__(self, *args, **kwargs):
        if len(kwargs) > 0 and len(args) > 0:
            raise JSONRPCException("JSON-RPC does not support positional and keyword arguments at the same time")

        if not self.__cookieProcessor.has_token():
            # get the cookie
            self.__opener.open(self.__serviceURL).read()

        if len(kwargs):
            postdata = dumps({"method": self.__serviceName, 'params': kwargs, 'id': 'jsonrpc'})
        else:
            postdata = dumps({"method": self.__serviceName, 'params': args, 'id': 'jsonrpc'})

        try:
            if self.__mode == 'POST':
                respdata = self.__opener.open(self.__serviceURL, postdata.encode('utf8')).read()
            else:
                respdata = self.__opener.open(self.__serviceURL + "?" + quote(postdata.encode('utf8'))).read()
        except HTTPError as e:
            error = loads(e.fp.readline())
            raise JSONRPCException(error['error']['message'])

        resp = loads(respdata)
        if resp['error'] != None:
            raise JSONRPCException(resp['error'])

        return resp['result']
Beispiel #2
0
    def __call__(self, *args, **kwargs):
        if len(kwargs) > 0 and len(args) > 0:
            raise JSONRPCException("JSON-RPC does not support positional and keyword arguments at the same time")

        # Default to 'core' queue
        call_id = uuid.uuid4()
        topic = "%s/%s" % (self.__serviceAddress, call_id)

        if isinstance(self.__methods, Future):
            self.__methods = yield self.__methods

        if self.__methods and self.__serviceName not in self.__methods:
            raise NameError("name '%s' not defined" % self.__serviceName)

        # Send
        if len(kwargs):
            postdata = dumps({"method": self.__serviceName, 'params': kwargs, 'id': 'jsonrpc'})
        else:
            postdata = dumps({"method": self.__serviceName, 'params': args, 'id': 'jsonrpc'})

        response = yield self.__handler.send_sync_message(postdata, topic)
        resp = loads(response)

        if 'error' in resp and resp['error'] is not None:
            raise JSONRPCException(resp['error'])

        raise gen.Return(response)
Beispiel #3
0
    def handle_request(self, topic, message):
        if topic == self.subtopic:
            # event from proxy received
            try:
                data = etree.fromstring(message, PluginRegistry.getEventParser())
                event_type = stripNs(data.xpath('/g:Event/*', namespaces={'g': "http://www.gonicus.de/Events"})[0].tag)
                if event_type == "ClientLeave":
                    proxy_id = str(data.ClientLeave.Id)
                    registry = PluginRegistry.getInstance("BackendRegistry")
                    registry.unregisterBackend(proxy_id)

            except etree.XMLSyntaxError as e:
                self.log.error("Event parsing error: %s" % e)

        elif topic.startswith(self.subtopic):
            response_topic = "%s/response" % "/".join(topic.split("/")[0:4])

            try:
                id_, res = self.process(topic, message)
                if is_future(res):
                    res = yield res
                response = dumps({"result": res, "id": id_})
                self.log.debug("MQTT-RPC response: %s on topic %s" % (response, topic))

            except Exception as e:
                err = str(e)
                self.log.error("MQTT RPC call error: %s" % err)
                response = dumps({'id': topic.split("/")[-2], 'error': err})

            # Get rid of it...
            self.mqtt.send_message(response, topic=response_topic, qos=2)

        else:
            self.log.warning("unhandled topic request received: %s" % topic)
Beispiel #4
0
    def __call__(self, *args, **kwargs):
        if len(kwargs) > 0 and len(args) > 0:
            raise JSONRPCException("JSON-RPC does not support positional and keyword arguments at the same time")

        if not self.__cookieProcessor.has_token():
            # get the cookie
            self.__opener.open(self.__serviceURL).read()

        if len(kwargs):
            postdata = dumps({"method": self.__serviceName, 'params': kwargs, 'id': 'jsonrpc'})
        else:
            postdata = dumps({"method": self.__serviceName, 'params': args, 'id': 'jsonrpc'})

        try:
            if self.__mode == 'POST':
                respdata = self.__opener.open(self.__serviceURL, postdata.encode('utf8')).read()
            else:
                respdata = self.__opener.open(self.__serviceURL + "?" + quote(postdata.encode('utf8'))).read()
        except HTTPError as e:
            raise JSONRPCException(e.reason if len(e.reason) else str(e))

        resp = loads(respdata)
        if resp['error'] is not None:
            raise JSONRPCException(resp['error'])

        return resp['result']
Beispiel #5
0
    def post(self):
        try:
            resp = self.process(self.request.body)
        except ValueError as e:
            self.clear()
            self.set_status(400)
            self.finish(str(e))
        except tornado.web.HTTPError as e:
            self.clear()
            self.set_status(e.status_code) 
            self.finish(e.log_message)
            raise e
        except FilterException as e:
            self.clear()
            self.set_status(500)
            error = dict(
                name='JSONRPCError',
                code=100,
                message=str(e),
                error=str(e)
            )
            self.finish(dumps(dict(result=None, error=error, id=None)))
            raise e
        else:
            if isinstance(resp['result'], Future):
                resp['result'] = yield resp['result']

            self.write(dumps(resp))
            self.set_header("Content-Type", "application/json")
Beispiel #6
0
    def __update_population(self):
        # collect current attribute values
        data = {}
        for prop in self._get_attributes():
            data[prop] = getattr(self, prop)

        changes = {}

        for key in self.__attribute_map:
            if self.__attribute_map[key][
                    'values_populate'] and self.__attribute_map[key][
                        're_populate_on_update'] is True:
                cr = PluginRegistry.getInstance('CommandRegistry')
                values = cr.call(self.__attribute_map[key]['values_populate'],
                                 data)
                if self.__attribute_map[key]['values'] != values:
                    changes[key] = values
                self.__attribute_map[key]['values'] = values

        if len(changes.keys()) and self.__user is not None:
            e = EventMaker()
            changed = list()
            for key, values in changes.items():
                change = e.Change(e.PropertyName(key),
                                  e.NewValues(dumps(values)))
                changed.append(change)

            ev = e.Event(
                e.ObjectPropertyValuesChanged(e.UUID(self.uuid), *changed))
            event_object = objectify.fromstring(
                etree.tostring(ev).decode('utf-8'))
            SseHandler.notify(event_object, channel="user.%s" % self.__user)
Beispiel #7
0
 def test_getSessionUser(self):
     self.login()
     data = dumps({"id": 1, "method": "getSessionUser", "params": []})
     response = self.fetch('/rpc', method='POST', body=data)
     assert response.code == 200
     json = loads(response.body)
     assert json['result'] == "admin"
Beispiel #8
0
    def setPasswordRecoveryAnswers(self, user, object_dn, data):
        """
        Set the password recovery answers for a user
        """
        data = loads(data)

        # Do we have read permissions for the requested attribute
        env = Environment.getInstance()
        topic = "%s.objects.%s.attributes.%s" % (env.domain, "User", "passwordRecoveryHash")
        aclresolver = PluginRegistry.getInstance("ACLResolver")
        if not aclresolver.check(user, topic, "w", base=object_dn):

            self.__log.debug("user '%s' has insufficient permissions to write %s on %s, required is %s:%s" % (
                user, "isLocked", object_dn, topic, "w"))
            raise ACLException(C.make_error('PERMISSION_ACCESS', topic, target=object_dn))

        user = ObjectProxy(object_dn)
        method = user.passwordMethod

        # Try to detect the responsible password method-class
        pwd_o = self.get_method_by_method_type(method)
        if not pwd_o:
            raise PasswordException(C.make_error("PASSWORD_UNKNOWN_HASH", type=method))

        # hash the new answers
        for idx, answer in data.items():
            data[idx] = pwd_o.generate_password_hash(self.clean_string(answer), method)
            print("%s encrypted with %s as index %s => %s" % (self.clean_string(answer), method, idx, data[idx]))

        # Set the password and commit the changes
        user.passwordRecoveryHash = dumps(data)
        user.commit()
Beispiel #9
0
 def will_set(self, topic, message, qos=0, retain=False):
     """
     Set a Will to be sent to the broker. If the client disconnects without calling disconnect(),
     the broker will publish the message on its behalf.
     """
     payload = {"content": message, "sender_id": self.__sender_id}
     self.client.will_set(topic, dumps(payload), qos, retain)
Beispiel #10
0
    def commandReceived(self, topic, message):
        """
        Process incoming commands, coming in with session and message
        information.

        ================= ==========================
        Parameter         Description
        ================= ==========================
        message           Received MQTT message
        ================= ==========================

        Incoming messages are coming from an
        :class:`gosa.common.components.mqtt_proxy.MQTTServiceProxy`. The command
        result is written to the '<domain>.client.<client-uuid>' queue.
        """
        err = None
        res = None
        name = None
        args = None
        kwargs = None
        id_ = ''

        response_topic = "%s/response" % "/".join(topic.split("/")[0:4])

        try:
            req = loads(message)
        except Exception as e:
            err = str(e)
            self.log.error("ServiceRequestNotTranslatable: %s" % err)
            req = {'id': topic.split("/")[-2]}

        if err is None:
            try:
                id_ = req['id']
                name = req['method']
                args = req['params']
                kwargs = req['kwparams']

            except KeyError as e:
                self.log.error("KeyError: %s" % e)
                err = str(BadServiceRequest(message))
        self.log.debug("received call [%s] for %s: %s(%s,%s)" % (id_, topic, name, args, kwargs))

        # Try to execute
        if err is None:
            try:
                res = self.__cr.dispatch(name, *args, **kwargs)
            except Exception as e:
                err = str(e)

                # Write exception to log
                exc_type, exc_value, exc_traceback = sys.exc_info()
                self.log.error(traceback.format_exception(exc_type, exc_value, exc_traceback))

        self.log.debug("returning call [%s]: %s / %s" % (id_, res, err))

        response = dumps({"result": res, "id": id_})

        # Get rid of it...
        self.client.send_message(response, topic=response_topic)
Beispiel #11
0
    def test_handle_backend_message(self):
        e = EventMaker()

        # send client poll
        with mock.patch.object(self.service.proxy_mqtt, "send_message") as mps:

            # send ACLChanged
            m_resolver = PluginRegistry.getInstance("ACLResolver")
            self.service._handle_backend_message("%s/proxy" % self.env.domain,
                                                 etree.tostring(e.Event(e.Trigger(e.Type("ACLChanged"))),
                                                                pretty_print=True).decode())
            assert m_resolver.load_acls.called
            assert not mps.called

            m_index = PluginRegistry.getInstance("ObjectIndex")
            self.service._handle_backend_message("%s/client/broadcast" % self.env.domain,
                                                 etree.tostring(e.Event(e.ClientPoll()),
                                                                pretty_print=True).decode())
            assert m_index.registerProxy.called
            assert mps.called
            mps.reset_mock()

            # send client RPC
            payload = dumps({"id": "mqttrpc", "method": "test", "params": []})
            topic = "%s/client/client_id/request_id/request" % self.env.domain
            self.service._handle_backend_message(topic, payload)
            mps.assert_called_with(payload, topic, qos=1, proxied=True)
Beispiel #12
0
    def test_handle_backend_message(self):
        e = EventMaker()

        # send client poll
        with mock.patch.object(self.service.proxy_mqtt, "send_message") as mps:

            # send ACLChanged
            m_resolver = PluginRegistry.getInstance("ACLResolver")
            self.service._handle_backend_message(
                "%s/proxy" % self.env.domain,
                etree.tostring(e.Event(e.Trigger(e.Type("ACLChanged"))),
                               pretty_print=True).decode())
            assert m_resolver.load_acls.called
            assert not mps.called

            m_index = PluginRegistry.getInstance("ObjectIndex")
            self.service._handle_backend_message(
                "%s/client/broadcast" % self.env.domain,
                etree.tostring(e.Event(e.ClientPoll()),
                               pretty_print=True).decode())
            assert m_index.registerProxy.called
            assert mps.called
            mps.reset_mock()

            # send client RPC
            payload = dumps({"id": "jsonrpc", "method": "test", "params": []})
            topic = "%s/client/client_id/request_id/request" % self.env.domain
            self.service._handle_backend_message(topic, payload)
            mps.assert_called_with(payload, topic, qos=1)
Beispiel #13
0
    def test_handle_proxy_message(self):

        # send client poll
        with mock.patch.object(self.service.backend_mqtt, "send_message") as mbs:
            payload = dumps({"id": "mqttrpc", "result": "test"})
            topic = "%s/client/client_id/request_id/response" % self.env.domain
            self.service._handle_proxy_message(topic, payload)
            mbs.assert_called_with(payload, topic, qos=1, proxied=True)
Beispiel #14
0
    def test_unknown(self):
        self.login()
        data = dumps({"id": 1, "method": "unknownmethod", "params": []})
        response = self.fetch('/rpc', method='POST', body=data)

        assert response.code == 500
        json = loads(response.body)
        assert json['error']['code'] == 100
        assert json['error']['name'] == "JSONRPCError"
Beispiel #15
0
 def test_xsrf(self):
     data = dumps({
         "id": 3,
         "method": "login",
         "params": ["admin", "tester"]
     })
     response = self.fetch('/rpc', method='POST', body=data)
     # without requesting the xsrf cookie we get the 403 code
     assert response.code == 403
Beispiel #16
0
    def test_handle_proxy_message(self):

        # send client poll
        with mock.patch.object(self.service.backend_mqtt,
                               "send_message") as mbs:
            payload = dumps({"id": "jsonrpc", "result": "test"})
            topic = "%s/client/client_id/request_id/response" % self.env.domain
            self.service._handle_proxy_message(topic, payload)
            mbs.assert_called_with(payload, topic, qos=1)
Beispiel #17
0
 def login(self):
     # fetch the xsrf cookie
     self.fetch('/rpc', method='GET')
     data = dumps({
         "id": 0,
         "method": "login",
         "params": ["admin", "tester"]
     })
     # login
     return self.fetch('/rpc', method='POST', body=data)
Beispiel #18
0
 def will_set(self, topic, message, qos=0, retain=False):
     """
     Set a Will to be sent to the broker. If the client disconnects without calling disconnect(),
     the broker will publish the message on its behalf.
     """
     payload = {
         "content": message,
         "sender_id": self.__sender_id
     }
     self.client.will_set(topic, dumps(payload), qos, retain)
Beispiel #19
0
    def stop(self):
        settings_file = self.env.config.get("webhooks.registry-store", "/var/lib/gosa/webhooks")
        to_save = self.__hooks.copy()
        for mime_type, sender_name in self.__temporary:
            if mime_type in to_save and sender_name in to_save[mime_type]:
                del to_save[mime_type][sender_name]
                if len(to_save[mime_type].keys()) == 0:
                    del to_save[mime_type]

        with open(settings_file, 'w') as f:
            f.write(dumps(to_save))
Beispiel #20
0
    def publish(self, topic, message, qos=0, retain=False):
        """ Publish a message on the MQTT bus"""
        message = {
            "sender_id": self.__sender_id,
            "content": message
        }
        res, mid = self.client.publish(topic, payload=dumps(message), qos=qos, retain=retain)

        self.__published_messages[mid] = res
        if res == mqtt.MQTT_ERR_NO_CONN:
            self.log.error("mqtt server not reachable, message could not be send to '%s'" % topic)
Beispiel #21
0
    def test_logout(self):
        # fetch the xsrf cookie
        self.fetch('/rpc', method='GET')
        data = dumps({"id": 3, "method": "logout", "params": []})
        response = self.fetch('/rpc', method='POST', body=data)
        # logging out before beeing logged in is not allowed
        assert response.code == 401

        self.login()
        response = self.fetch('/rpc', method='POST', body=data)

        assert response.code == 200
        json = loads(response.body)
        assert json['result'] is True
        assert json['error'] is None
        assert json['id'] == 3

        # check if we are logged out
        data = dumps({"id": 3, "method": "getSessionUser", "params": []})
        response = self.fetch('/rpc', method='POST', body=data)
        assert response.code == 401
Beispiel #22
0
 def test_xsrf(self):
     data = dumps({
         "id": 3,
         "method": "login",
         "params": ["admin", "tester"]
     })
     response = self.fetch('/rpc',
                       method='POST',
                       body=data
                       )
     # without requesting the xsrf cookie we get the 403 code
     assert response.code == 403
Beispiel #23
0
 def test_missingparameter(self):
     # fetch the xsrf cookie
     self.fetch('/rpc', method='GET')
     data = dumps({
         "id": 1,
         "params": []
     })
     response = self.fetch('/rpc',
                           method='POST',
                           body=data
                           )
     assert response.code == 400
Beispiel #24
0
    def publish(self, topic, message, qos=0, retain=False):
        """ Publish a message on the MQTT bus"""
        message = {"sender_id": self.__sender_id, "content": message}
        res, mid = self.client.publish(topic,
                                       payload=dumps(message),
                                       qos=qos,
                                       retain=retain)

        self.__published_messages[mid] = res
        if res == mqtt.MQTT_ERR_NO_CONN:
            self.log.error(
                "mqtt server not reachable, message could not be send to '%s'"
                % topic)
Beispiel #25
0
 def test_wrong_parameter_format(self):
     # fetch the xsrf cookie
     self.fetch('/rpc', method='GET')
     data = dumps({
         "id": 1,
         "method": "login",
         "params": 'no list or dict'
     })
     response = self.fetch('/rpc',
                           method='POST',
                           body=data
                           )
     assert response.code == 400
Beispiel #26
0
 def test_bad_method_name(self):
     # fetch the xsrf cookie
     self.fetch('/rpc', method='GET')
     data = dumps({
         "id": 1,
         "method": "_somemethod",
         "params": []
     })
     response = self.fetch('/rpc',
                           method='POST',
                           body=data
                           )
     assert response.code == 403
Beispiel #27
0
 def login(self):
     # fetch the xsrf cookie
     self.fetch('/rpc', method='GET')
     data = dumps({
         "id": 0,
         "method": "login",
         "params": ["admin", "tester"]
     })
     # login
     return self.fetch('/rpc',
                       method='POST',
                       body=data
                       )
Beispiel #28
0
    def handle_request(self, request_handler):
        foreman = PluginRegistry.getInstance("Foreman")
        self.log.debug(request_handler.request.body)
        data = loads(request_handler.request.body)

        if data["action"] in ForemanRealmReceiver.skip_next_event:
            del ForemanRealmReceiver.skip_next_event[data["action"]]
            return

        # TODO disable hook logging to file
        with open("foreman-log.json", "a") as f:
            f.write("%s,\n" % dumps(data, indent=4, sort_keys=True))

        ForemanBackend.modifier = "foreman"
        if data['action'] == "create":
            # new client -> join it
            try:
                key = yield foreman.add_host(data['hostname'])

                # send key as otp to foremans realm proxy
                request_handler.finish(dumps({
                    "randompassword": key
                }))
            except Exception as e:
                request_handler.finish(dumps({
                    "error": "%s" % e
                }))
                raise e

        elif data['action'] == "delete":
            try:
                foreman.remove_type("ForemanHost", data['hostname'])
            except Exception as e:
                request_handler.finish(dumps({
                    "error": "%s" % e
                }))
                raise e

        ForemanBackend.modifier = None
Beispiel #29
0
    def test_logout(self):
        # fetch the xsrf cookie
        self.fetch('/rpc', method='GET')
        data = dumps({
            "id": 3,
            "method": "logout",
            "params": []
        })
        response = self.fetch('/rpc',
                          method='POST',
                          body=data
                          )
        # logging out before beeing logged in is not allowed
        assert response.code == 401

        self.login()
        response = self.fetch('/rpc',
                              method='POST',
                              body=data
                              )

        assert response.code == 200
        json = loads(response.body)
        assert json['result'] is True
        assert json['error'] is None
        assert json['id'] == 3

        # check if we are logged out
        data = dumps({
            "id": 3,
            "method": "getSessionUser",
            "params": []
        })
        response = self.fetch('/rpc',
                              method='POST',
                              body=data
                              )
        assert response.code == 401
Beispiel #30
0
    def save(self):
        settings_file = self.env.config.get("webhooks.registry-store", "/var/lib/gosa/webhooks")
        to_save = copy.deepcopy(self.__hooks)
        for mime_type, sender_name in self.__temporary:
            if mime_type in to_save and sender_name in to_save[mime_type]:
                del to_save[mime_type][sender_name]
                if len(to_save[mime_type].keys()) == 0:
                    del to_save[mime_type]

        # backup old file
        if os.path.exists(settings_file):
            shutil.copyfile(settings_file, "%s.backup" % settings_file)
        with open(settings_file, 'w') as f:
            f.write(dumps(to_save))
Beispiel #31
0
 def test_getSessionUser(self):
     self.login()
     data = dumps({
         "id": 1,
         "method": "getSessionUser",
         "params": []
     })
     response = self.fetch('/rpc',
                           method='POST',
                           body=data
                           )
     assert response.code == 200
     json = loads(response.body)
     assert json['result'] == "admin"
Beispiel #32
0
 def test_exception(self):
     self.login()
     data = dumps({
         "id": 1,
         "method": "getSessionUser",
         "params": {
             'test': 'test'
         }
     })
     response = self.fetch('/rpc', method='POST', body=data)
     assert response.code == 500
     json = loads(response.body)
     assert json['error']['code'] == 100
     assert json['error']['name'] == "JSONRPCError"
Beispiel #33
0
    def configureUsers(self, client_id, users):
        users_config = self.__collect_user_configuration(client_id, users)
        for uid, config in users_config.items():
            if "menu" in config:
                # send to client
                self.log.debug("sending generated menu for user %s" % uid)
                self.queuedClientDispatch(client_id, "dbus_configureUserMenu", uid, dumps(config["menu"]))

            if "printer-setup" in config:
                self.configureHostPrinters(client_id, config["printer-setup"])

            if "resolution" in config and config["resolution"] is not None and len(config["resolution"]):
                self.log.debug("sending screen resolution: %sx%s for user %s to client %s" % (config["resolution"][0], config["resolution"][1], uid, client_id))
                self.queuedClientDispatch(client_id, "dbus_configureUserScreen", uid, config["resolution"][0], config["resolution"][1])
Beispiel #34
0
    def __call__(self, *args, **kwargs):
        if len(kwargs) > 0 and len(args) > 0:
            raise JSONRPCException(
                "JSON-RPC does not support positional and keyword arguments at the same time"
            )

        # Default to 'core' queue
        call_id = uuid.uuid4()
        topic = "%s/%s" % (self.__serviceAddress, call_id)

        if isinstance(self.__methods, Future):
            self.__methods = yield self.__methods

        if self.__methods and self.__serviceName not in self.__methods:
            raise NameError("name '%s' not defined" % self.__serviceName)

        # Send
        if len(kwargs):
            postdata = dumps({
                "method": self.__serviceName,
                'params': kwargs,
                'id': 'jsonrpc'
            })
        else:
            postdata = dumps({
                "method": self.__serviceName,
                'params': args,
                'id': 'jsonrpc'
            })

        response = yield self.__handler.send_sync_message(postdata, topic)
        resp = loads(response)

        if 'error' in resp and resp['error'] is not None:
            raise JSONRPCException(resp['error'])

        raise gen.Return(response)
Beispiel #35
0
    def send_message(cls,
                     msg,
                     topic=None,
                     channel='broadcast',
                     session_id=None):
        """ Sends a message to all live connections """
        id = str(uuid.uuid4())

        if isinstance(msg, dict):
            msg = dumps(msg)

        dataString = format(
            "%s\n" % "\n".join([("data: %s" % x)
                                for x in msg.splitlines() if not x == '']))

        if topic is not None:
            message = format('id: %s\nevent: %s\n%s\n' %
                             (id, topic, dataString))
        else:
            message = format('id: %s\n%s\n' % (id, dataString))

        cls._cache.append({
            'id': id,
            'channel': channel,
            'body': message,
        })
        if len(cls._cache) > cls._cache_size:
            cls._cache = cls._cache[-cls._cache_size:]

        if channel == 'broadcast':
            clients = []
            for chan in cls._channels:
                for client in cls._channels[chan]:
                    clients.append(client)
        elif session_id is not None:
            clients = []
            channel_clients = cls._channels.get(channel, [])
            for connection_id in channel_clients:
                if channel_clients[connection_id] == session_id:
                    clients.append(connection_id)
        else:
            clients = cls._channels.get(channel, [])

        cls.log.info(
            'Sending %s "%s" in channel %s (session: %s) to %s clients' %
            (topic, msg, channel, session_id, len(clients)))
        for client_id in clients:
            client = cls._connections[client_id]
            client.on_message(message)
Beispiel #36
0
 def test_exception(self):
     self.login()
     data = dumps({
         "id": 1,
         "method": "getSessionUser",
         "params": {'test': 'test'}
     })
     response = self.fetch('/rpc',
                           method='POST',
                           body=data
                           )
     assert response.code == 500
     json = loads(response.body)
     assert json['error']['code'] == 500
     assert json['error']['name'] == "JSONRPCError"
Beispiel #37
0
    def handle_request(self, topic, message):
        if topic == self.subtopic:
            # event from proxy received
            try:
                data = etree.fromstring(message,
                                        PluginRegistry.getEventParser())
                event_type = stripNs(
                    data.xpath(
                        '/g:Event/*',
                        namespaces={'g':
                                    "http://www.gonicus.de/Events"})[0].tag)
                if event_type == "ClientLeave":
                    proxy_id = str(data.ClientLeave.Id)
                    registry = PluginRegistry.getInstance("BackendRegistry")
                    registry.unregisterBackend(proxy_id)

            except etree.XMLSyntaxError as e:
                self.log.error("Event parsing error: %s" % e)

        elif topic.startswith(self.subtopic):
            response_topic = "%s/response" % "/".join(topic.split("/")[0:4])

            try:
                id_, res = self.process(topic, message)
                response = dumps({"result": res, "id": id_})

            except Exception as e:
                err = str(e)
                self.log.error("MQTT RPC call error: %s" % err)
                response = dumps({'id': topic.split("/")[-2], 'error': err})

            # Get rid of it...
            self.mqtt.send_message(response, topic=response_topic)

        else:
            self.log.warning("unhandled topic request received: %s" % topic)
Beispiel #38
0
    def __request(self, method_name, object_type, object_id=None, data=None):
        if self.foreman_host is None:
            return {}

        cached = ForemanClientCache.get_cache(method_name, object_type, object_id=object_id)
        if cached is not None:
            self.log.debug("using cached response for %s request of %s" % (method_name, object_type))
            return cached

        url = "%s/%s" % (self.foreman_host, object_type)
        if object_id is not None:
            url += "/%s" % object_id

        method = getattr(requests, method_name)
        data = dumps(data) if data is not None else None
        self.log.debug("sending %s request with %s to %s" % (method_name, data, url))
        kwargs = {
            "headers": self.headers,
            "verify": self.env.config.get("foreman.verify", "true") == "true",
            "data": data,
            "cookies": self.__cookies,
            "timeout": 30
        }
        try:
            if self.__cookies is None:
                response = self.__authenticate(method, url, kwargs)
            else:
                response = method(url, **kwargs)
        except Exception as e:
            self.log.error("Error during foreman API request: %s" % str(e))
            raise e

        if response.status_code == 401 and self.__cookies is not None:
            # try to re-authenticate session might be timed out
            response = self.__authenticate(method, url, kwargs)

        if response.ok:
            data = response.json()
            self.log.debug("response %s" % data)
            # check for error
            if "error" in data:
                raise ForemanBackendException(response, method=method_name)
            else:
                ForemanClientCache.add_to_cache(method_name, object_type, data, object_id=object_id)
            return data
        else:
            self.log.error("%s request with %s to %s failed: %s" % (method_name, data, url, str(response.content)))
            raise ForemanBackendException(response, method=method_name)
Beispiel #39
0
    def test_unknown(self):
        self.login()
        data = dumps({
            "id": 1,
            "method": "unknownmethod",
            "params": []
        })
        response = self.fetch('/rpc',
                              method='POST',
                              body=data
                              )

        assert response.code == 500
        json = loads(response.body)
        assert json['error']['code'] == 100
        assert json['error']['name'] == "JSONRPCError"
Beispiel #40
0
    def publish(self, topic, content, qos=0, retain=False, retried=0, proxied=False):
        """ Publish a message on the MQTT bus"""
        message = {
            "sender_id": self.__sender_id,
            "content": content
        }
        if proxied is True:
            message['proxied_by'] = self.__sender_id

        res, mid = self.client.publish(topic, payload=dumps(message), qos=qos, retain=retain)

        self.__published_messages[mid] = res
        if res == mqtt.MQTT_ERR_NO_CONN:
            self.log.error("%s: mqtt server not reachable, message could not be send to '%s'" % (self.get_identifier(), topic))

            if qos > 0 and retried < 3:
                # try again
                yield gen.sleep(0.1)
                self.publish(topic, content, qos=qos, retain=retain, retried=retried+1)
Beispiel #41
0
    def __call__(self, *args, **kwargs):
        data = {}
        if '__user__' in kwargs:
            data['user'] = kwargs['__user__']
            del kwargs['__user__']
        if '__session_id__' in kwargs:
            data['session_id'] = kwargs['__session_id__']
            del kwargs['__session_id__']

        if len(kwargs) > 0 and len(args) > 0:
            raise JSONRPCException(
                "JSON-RPC does not support positional and keyword arguments at the same time"
            )

        # Default to 'core' queue
        call_id = uuid.uuid4()
        topic = "%s/%s" % (self.__serviceAddress, call_id)

        if isinstance(self.__methods, Future):
            self.__methods = yield self.__methods

        if self.__methods and self.__serviceName not in self.__methods:
            raise NameError("name '%s' not defined" % self.__serviceName)

        # Send
        data.update({
            "method": self.__serviceName,
            "id": "jsonrpc",
            "sender": self.env.uuid
        })
        if len(kwargs):
            data["params"] = kwargs
        else:
            data["params"] = args
        postdata = dumps(data)

        response = yield self.__handler.send_sync_message(postdata, topic)
        resp = loads(response)

        if 'error' in resp and resp['error'] is not None:
            raise JSONRPCException(resp['error'])

        return resp['result']
Beispiel #42
0
    def test_MqttEventConsumer(self):
        schema = '<?xml version="1.0"?>' \
                 '<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:e="http://www.gonicus.de/Events" ' \
                 'targetNamespace="http://www.gonicus.de/Events" elementFormDefault="qualified">'\
                 '<include schemaLocation="%s"/>'\
                 '<complexType name="Event">'\
                 '<choice maxOccurs="1" minOccurs="1">'\
                 '<group ref="e:Events"/>'\
                 '</choice>'\
                 '</complexType>'\
                 '<group name="Events">'\
                 '<choice>'\
                 '<element name="BackendChange" type="e:BackendChange"/>'\
                 '</choice>'\
                 '</group>'\
                 '<element name="Event" type="e:Event"/>'\
                 '</schema>' % resource_filename('gosa.backend', 'data/events/BackendChange.xsd')

        with unittest.mock.patch("gosa.common.events.PluginRegistry.getEventSchema", return_value=schema):
            e = EventMaker()
            callback = unittest.mock.Mock()
            event = e.Event(
                e.BackendChange(
                    e.DN("dn"),
                    e.ModificationTime("mod_time"),
                    e.ChangeType("type")
                )
            )
            mec = MqttEventConsumer(callback=callback, event_type="BackendChange")
            payload = dumps({
                "sender_id": None,
                "content": etree.tostring(event, pretty_print=True).decode('utf-8')
            })
            message = unittest.mock.MagicMock()
            message.payload = payload
            message.topic = "%s/events" % Environment.getInstance().domain
            mec.mqtt.get_client().client.on_message(None, None, message)

            args, kwargs = callback.call_args
            assert etree.tostring(args[0], pretty_print=True).decode('utf-8') == etree.tostring(event, pretty_print=True).decode('utf-8')
            PluginRegistry._event_parser = None
Beispiel #43
0
    def send_message(cls, msg, topic=None, channel='broadcast', session_id=None):
        """ Sends a message to all live connections """
        id = str(uuid.uuid4())

        if isinstance(msg, dict):
            msg = dumps(msg)

        dataString = format("%s\n" % "\n".join([("data: %s" % x) for x in msg.splitlines() if not x == '']))

        if topic is not None:
            message = format('id: %s\nevent: %s\n%s\n' % (id, topic, dataString))
        else:
            message = format('id: %s\n%s\n' % (id, dataString))

        cls._cache.append({
            'id': id,
            'channel': channel,
            'body': message,
        })
        if len(cls._cache) > cls._cache_size:
            cls._cache = cls._cache[-cls._cache_size:]

        if channel == 'broadcast':
            clients = []
            for chan in cls._channels:
                for client in cls._channels[chan]:
                    clients.append(client)
        elif session_id is not None:
            clients = []
            channel_clients = cls._channels.get(channel, [])
            for connection_id in channel_clients:
                if channel_clients[connection_id] == session_id:
                    clients.append(connection_id)
        else:
            clients = cls._channels.get(channel, [])

        logging.info('Sending %s "%s" in channel %s (session: %s) to %s clients' % (topic, msg, channel, session_id, len(clients)))
        for client_id in clients:
            client = cls._connections[client_id]
            client.on_message(message)
Beispiel #44
0
    def configureUsers(self, client_id, users):
        users_config = self.__collect_user_configuration(client_id, users)
        for uid, config in users_config.items():
            if "menu" in config:
                # send to client
                self.log.debug("sending generated menu for user %s" % uid)
                self.queuedClientDispatch(client_id, "dbus_configureUserMenu",
                                          uid, dumps(config["menu"]))

            if "printer-setup" in config:
                self.configureHostPrinters(client_id, config["printer-setup"])

            if "resolution" in config and config[
                    "resolution"] is not None and len(config["resolution"]):
                self.log.debug(
                    "sending screen resolution: %sx%s for user %s to client %s"
                    % (config["resolution"][0], config["resolution"][1], uid,
                       client_id))
                self.queuedClientDispatch(client_id,
                                          "dbus_configureUserScreen", uid,
                                          config["resolution"][0],
                                          config["resolution"][1])
Beispiel #45
0
    def __request(self, method_name, type, id=None, data=None):
        if self.foreman_host is None:
            return {}

        url = "%s/%s" % (self.foreman_host, type)
        if id is not None:
            url += "/%s" % id

        method = getattr(requests, method_name)
        data = dumps(data) if data is not None else None
        self.log.debug("sending %s request with %s to %s" %
                       (method_name, data, url))
        kwargs = {
            "headers": self.headers,
            "verify": self.env.config.get("foreman.verify", "true") == "true",
            "data": data,
            "cookies": self.__cookies
        }
        if self.__cookies is None:
            response = self.__authenticate(method, url, kwargs)
        else:
            response = method(url, **kwargs)

        if response.status_code == 401 and self.__cookies is not None:
            # try to re-authenticate session might be timed out
            response = self.__authenticate(method, url, kwargs)

        if response.ok:
            data = response.json()
            self.log.debug("response %s" % data)
            # check for error
            if "error" in data:
                raise ForemanBackendException(response, method=method_name)
            return data
        else:
            self.log.error("%s request with %s to %s failed: %s" %
                           (method_name, data, url, str(response.content)))
            raise ForemanBackendException(response, method=method_name)
Beispiel #46
0
    def setPasswordRecoveryAnswers(self, user, object_dn, data):
        """
        Set the password recovery answers for a user
        """
        data = loads(data)

        # Do we have read permissions for the requested attribute
        env = Environment.getInstance()
        topic = "%s.objects.%s.attributes.%s" % (env.domain, "User",
                                                 "passwordRecoveryHash")
        aclresolver = PluginRegistry.getInstance("ACLResolver")
        if not aclresolver.check(user, topic, "w", base=object_dn):

            self.__log.debug(
                "user '%s' has insufficient permissions to write %s on %s, required is %s:%s"
                % (user, "isLocked", object_dn, topic, "w"))
            raise ACLException(
                C.make_error('PERMISSION_ACCESS', topic, target=object_dn))

        user = ObjectProxy(object_dn)
        method = user.passwordMethod

        # Try to detect the responsible password method-class
        pwd_o = self.get_method_by_method_type(method)
        if not pwd_o:
            raise PasswordException(
                C.make_error("PASSWORD_UNKNOWN_HASH", type=method))

        # hash the new answers
        for idx, answer in data.items():
            data[idx] = pwd_o.generate_password_hash(self.clean_string(answer),
                                                     method)
            print("%s encrypted with %s as index %s => %s" %
                  (self.clean_string(answer), method, idx, data[idx]))

        # Set the password and commit the changes
        user.passwordRecoveryHash = dumps(data)
        user.commit()
Beispiel #47
0
    def __call__(self, *args, **kwargs):
        data = {}
        if '__user__' in kwargs:
            data['user'] = kwargs['__user__']
            del kwargs['__user__']
        if '__session_id__' in kwargs:
            data['session_id'] = kwargs['__session_id__']
            del kwargs['__session_id__']

        # Default to 'core' queue
        call_id = uuid.uuid4()
        topic = "%s/%s" % (self.__serviceAddress, call_id)

        if isinstance(self.__methods, Future):
            self.__methods = yield self.__methods

        if self.__methods and self.__serviceName not in self.__methods:
            raise NameError("name '%s' not defined" % self.__serviceName)

        # Send
        data.update({
            "method": self.__serviceName,
            "id": "mqttrpc",
            "sender": self.env.uuid
        })
        data["kwparams"] = kwargs
        data["params"] = args
        postdata = dumps(data)

        response = yield self.__handler.send_sync_message(postdata, topic, qos=2)
        resp = loads(response)

        if 'error' in resp and resp['error'] is not None:
            raise JSONRPCException(resp['error'])

        return resp['result']
Beispiel #48
0
def notify_backend(env, mode, user):
    """ Main event loop which will process all registered threads in a loop.
        It will run as long env.active is set to True."""
    log = logging.getLogger(__name__)

    PluginRegistry.modules["ClientCommandRegistry"] = ClientCommandRegistry()
    PluginRegistry.modules["MQTTClientService"] = MQTTClientService()

    dbus_proxy = DBUSProxy()
    dbus_proxy.serve(register_methods=False)

    url = env.config.get("jsonrpc.url", default=None)
    sys_id = env.config.get("core.id", default=None)
    key = env.config.get("jsonrpc.key", default=None)

    # Prepare URL for login
    url = urlparse(url)

    # Try to log in with provided credentials
    connection = '%s://%s%s' % (url.scheme, url.netloc, url.path)
    proxy = JSONServiceProxy(connection)

    # Try to log in
    try:
        if not proxy.login(sys_id, key):
            log.error("connection to GOsa backend failed")
            print(_("Cannot join client: check user name or password!"))
            return False
        else:
            if mode == "start":
                config = proxy.preUserSession(sys_id, user)
                # send config to dbus
                if "menu" in config:
                    # send to client
                    print("sending generated menu for user '%s'" % user)
                    dbus_proxy.callDBusMethod("dbus_configureUserMenu", user,
                                              dumps(config["menu"]))

                if "printer-setup" in config and "printers" in config[
                        "printer-setup"]:
                    dbus_proxy.callDBusMethod("dbus_deleteAllPrinters")
                    for p_conf in config["printer-setup"]["printers"]:
                        print("adding printer '%s'" % p_conf["cn"])
                        p = {
                            key: value if value is not None else ""
                            for (key, value) in p_conf.items()
                        }
                        dbus_proxy.callDBusMethod("dbus_addPrinter", p)

                    if "defaultPrinter" in config["printer-setup"] and config[
                            "printer-setup"]["defaultPrinter"] is not None:
                        print("setting '%s' as default printer" %
                              config["printer-setup"]["defaultPrinter"])
                        dbus_proxy.callDBusMethod(
                            "dbus_defaultPrinter",
                            config["printer-setup"]["defaultPrinter"])

                if "resolution" in config and config[
                        "resolution"] is not None and len(
                            config["resolution"]):
                    print("sending screen resolution: %sx%s for user %s" %
                          (config["resolution"][0], config["resolution"][1],
                           user))
                    dbus_proxy.callDBusMethod("dbus_configureUserScreen", user,
                                              config["resolution"][0],
                                              config["resolution"][1])

            elif mode == "end":
                proxy.postUserSession(sys_id, user)
    except HTTPError as e:
        if e.code == 401:
            log.error("connection to GOsa backend failed")
            print(_("Cannot join client: check user name or password!"))
            return False
        else:
            print("Error: %s " % str(e))
            raise e

    except Exception as e:
        print("Error: %s " % str(e))
        raise e
Beispiel #49
0
 def will_set(self, topic, message, qos=0, retain=False):
     """
     Set a Will to be sent to the broker. If the client disconnects without calling disconnect(),
     the broker will publish the message on its behalf.
     """
     self.client.will_set(topic, dumps(message), qos, retain)
Beispiel #50
0
 def __save_settings(self):
     with open(self.settings_file, "w") as f:
         f.write(dumps(self.__settings))
Beispiel #51
0
 def stop(self):
     settings_file = self.env.config.get("webhooks.registry-store",
                                         "/var/lib/gosa/webhooks")
     with open(settings_file, 'w') as f:
         f.write(dumps(self.__hooks))
Beispiel #52
0
 def __save_settings(self):
     with open(self.settings_file, "w") as f:
         f.write(dumps(self.__settings))
Beispiel #53
0
 def process(self, obj, key, valDict):
     if type(valDict[key]['value'] is not None):
         valDict[key]['value'] = list(map(lambda x: dumps(x), valDict[key]['value']))
     return key, valDict
Beispiel #54
0
 def stop(self):
     settings_file = self.env.config.get("webhooks.registry-store", "/var/lib/gosa/webhooks")
     with open(settings_file, 'w') as f:
         f.write(dumps(self.__hooks))
Beispiel #55
0
    def test_rpc(self):
        service = PluginRegistry.getInstance("MQTTRPCService")
        request_uuid = uuid.uuid4()
        topic = "%s/proxy/fake_client_id/%s" % (
            Environment.getInstance().domain, request_uuid)

        with mock.patch.object(PluginRegistry.getInstance('CommandRegistry'), "dispatch") as m, \
                mock.patch.object(service.mqtt, "send_message") as mq:
            m.return_value = "fake_response"
            # call with wrong json
            service.handle_request(topic, "this is no json: 'string'")
            args, kwargs = mq.call_args
            response = loads(args[0])
            assert "error" in response
            assert kwargs["topic"] == "%s/response" % topic
            assert not m.called
            mq.reset_mock()

            # call without params
            service.handle_request(
                topic,
                dumps({
                    "id": "jsonrpc",
                    "method": "fakeCall",
                    "user": "******"
                }))
            args, kwargs = mq.call_args
            response = loads(args[0])
            assert "error" in response
            assert kwargs["topic"] == "%s/response" % topic
            assert not m.called

            # call with empty params
            service.handle_request(
                topic,
                dumps({
                    "id": "jsonrpc",
                    "method": "fakeCall",
                    "user": "******",
                    "session_id": "fake_session_id",
                    "params": []
                }))
            m.assert_called_with("admin", "fake_session_id", "fakeCall")
            args, kwargs = mq.call_args
            response = loads(args[0])
            assert "result" in response
            assert response["result"] == "fake_response"
            assert kwargs["topic"] == "%s/response" % topic
            mq.reset_mock()
            m.reset_mock()

            service.handle_request(
                topic,
                dumps({
                    "id": "jsonrpc",
                    "method": "fakeCall",
                    "user": "******",
                    "params": ["param1", "param2"]
                }))
            m.assert_called_with("admin", None, "fakeCall", "param1", "param2")
            args, kwargs = mq.call_args
            response = loads(args[0])
            assert "result" in response
            assert response["result"] == "fake_response"
            assert kwargs["topic"] == "%s/response" % topic

            mq.reset_mock()
            m.reset_mock()

            # call without user (client id taken as user)
            service.handle_request(
                topic,
                dumps({
                    "id": "jsonrpc",
                    "method": "fakeCall",
                    "params": []
                }))
            m.assert_called_with("fake_client_id", None, "fakeCall")
            args, kwargs = mq.call_args
            response = loads(args[0])
            assert "result" in response
            assert response["result"] == "fake_response"
            assert kwargs["topic"] == "%s/response" % topic
Beispiel #56
0
    def handle_request(self, request_handler):
        foreman = PluginRegistry.getInstance("Foreman")
        data = loads(request_handler.request.body)
        self.log.debug(data)

        # TODO disable hook logging to file
        with open("foreman-log.json", "a") as f:
            f.write("%s,\n" % dumps(data, indent=4, sort_keys=True))

        if data["event"] in ForemanHookReceiver.skip_next_event and data["object"] in ForemanHookReceiver.skip_next_event[data["event"]]:
            ForemanHookReceiver.skip_next_event[data["event"]].remove(data["object"])
            self.log.info("skipped '%s' event for object: '%s'" % (data["event"], data["object"]))
            return

        data_keys = list(data['data'].keys())
        if len(data_keys) == 1:
            type = data_keys[0]
        else:
            # no type given -> skipping this event as other might come with more information
            self.log.warning("skipping event '%s' for object '%s' as no type information is given in data: '%s'" % (data["event"],
                                                                                                                  data["object"],
                                                                                                                  data["data"]))
            return

        # search for real data
        if len(data['data'][type].keys()) == 1:
            # something like {data: 'host': {host: {...}}}
            #             or {data: 'discovered_host': {host: {...}}}
            payload_data = data['data'][type][list(data['data'][type].keys())[0]]
        else:
            payload_data = data['data'][type]

        if type == "operatingsystem":
            with make_session() as session:
                foreman.sync_release_name(payload_data, session, event=data['event'])
                session.commit()
                return

        factory = ObjectFactory.getInstance()
        foreman_type = type
        if type == "discovered_host":
            type = "host"

        object_types = factory.getObjectNamesWithBackendSetting("Foreman", "type", "%ss" % type)
        object_type = object_types[0] if len(object_types) else None

        backend_attributes = factory.getObjectBackendProperties(object_type) if object_type is not None else None
        self.log.debug("Hookevent: '%s' for '%s' (%s)" % (data['event'], data['object'], object_type))

        uuid_attribute = None
        if "Foreman" in backend_attributes:
            uuid_attribute = backend_attributes["Foreman"]["_uuidSourceAttribute"] \
                if '_uuidSourceAttribute' in backend_attributes["Foreman"] else backend_attributes["Foreman"]["_uuidAttribute"]

        ForemanBackend.modifier = "foreman"
        update_data = {}

        if data['event'] in ["update", "create"] and foreman_type == "host":
            id = payload_data["id"] if "id" in payload_data else None
            try:
                foreman.write_parameters(id if id is not None else data['object'])
            except:
                foreman.mark_for_parameter_setting(data['object'], {
                    "status": "created",
                    "use_id": id
                })

        if data['event'] == "after_commit" or data['event'] == "update" or data['event'] == "after_create" or data['event'] == "create":
            host = None
            if data['event'] == "update" and foreman_type == "host" and "mac" in payload_data and payload_data["mac"] is not None:
                # check if we have an discovered host for this mac
                index = PluginRegistry.getInstance("ObjectIndex")
                res = index.search({
                    "_type": "Device",
                    "extension": ["ForemanHost", "ieee802Device"],
                    "macAddress": payload_data["mac"],
                    "status": "discovered"
                }, {"dn": 1})

                if len(res):
                    self.log.debug("update received for existing host with dn: %s" % res[0]["dn"])
                    host = ObjectProxy(res[0]["dn"])

                    if foreman_type != "discovered_host" and host.is_extended_by("ForemanHost"):
                        host.status = "unknown"

            foreman_object = foreman.get_object(object_type, payload_data[uuid_attribute], create=host is None)
            if foreman_object and host:
                if foreman_object != host:
                    self.log.debug("using known host instead of creating a new one")
                    # host is the formerly discovered host, which might have been changed in GOsa for provisioning
                    # so we want to use this one, foreman_object is the joined one, so copy the credentials from foreman_object to host
                    if not host.is_extended_by("RegisteredDevice"):
                        host.extend("RegisteredDevice")
                    if not host.is_extended_by("simpleSecurityObject"):
                        host.extend("simpleSecurityObject")
                    host.deviceUUID = foreman_object.deviceUUID
                    host.userPassword = foreman_object.userPassword
                    host.otp = foreman_object.otp
                    host.cn = foreman_object.cn

                    # now delete the formerly joined host
                    foreman_object.remove()
                    foreman_object = host

            elif foreman_object is None and host is not None:
                foreman_object = host

            elif foreman_type == "discovered_host":
                self.log.debug("setting discovered state for %s" % payload_data[uuid_attribute])
                if not foreman_object.is_extended_by("ForemanHost"):
                    foreman_object.extend("ForemanHost")
                foreman_object.status = "discovered"

            if foreman_type == "host":
                old_build_state = foreman_object.build

            foreman.update_type(object_type, foreman_object, payload_data, uuid_attribute, update_data=update_data)
            if foreman_type == "host" and old_build_state is True and foreman_object.build is False and \
                            foreman_object.status == "ready":
                # send notification
                e = EventMaker()
                ev = e.Event(e.Notification(
                    e.Title(N_("Host ready")),
                    e.Body(N_("Host '%s' has been successfully build." % foreman_object.cn)),
                    e.Icon("@Ligature/pc"),
                    e.Timeout("10000")
                ))
                event_object = objectify.fromstring(etree.tostring(ev, pretty_print=True).decode('utf-8'))
                SseHandler.notify(event_object)

        elif data['event'] == "after_destroy":
            # print("Payload: %s" % payload_data)
            foreman.remove_type(object_type, payload_data[uuid_attribute])

            # because foreman sends the after_commit event after the after_destroy event
            # we need to skip this event, otherwise the host would be re-created
            if "after_commit" not in ForemanHookReceiver.skip_next_event:
                ForemanHookReceiver.skip_next_event["after_commit"] = [data['object']]
            else:
                ForemanHookReceiver.skip_next_event["after_commit"].append(data['object'])

            # add garbage collection for skip
            sobj = PluginRegistry.getInstance("SchedulerService")
            sobj.getScheduler().add_date_job(self.cleanup_event_skipper,
                                             datetime.datetime.now() + datetime.timedelta(minutes=1),
                                             args=("after_commit", data['object']),
                                             tag='_internal', jobstore='ram')

        else:
            self.log.info("unhandled hook event '%s' received for '%s'" % (data['event'], type))

        ForemanBackend.modifier = None
Beispiel #57
0
    def commandReceived(self, topic, message):
        """
        Process incoming commands, coming in with session and message
        information.

        ================= ==========================
        Parameter         Description
        ================= ==========================
        message           Received MQTT message
        ================= ==========================

        Incoming messages are coming from an
        :class:`gosa.common.components.mqtt_proxy.MQTTServiceProxy`. The command
        result is written to the '<domain>.client.<client-uuid>' queue.
        """
        err = None
        res = None
        name = None
        args = None
        id_ = ''

        response_topic = "%s/to-backend" % "/".join(topic.split("/")[0:4])

        try:
            req = loads(message)
        except Exception as e:
            err = str(e)
            self.log.error("ServiceRequestNotTranslatable: %s" % err)
            req = {'id': topic.split("/")[-2]}

        if err is None:
            try:
                id_ = req['id']
                name = req['method']
                args = req['params']

            except KeyError as e:
                self.log.error("KeyError: %s" % e)
                err = str(BadServiceRequest(message))

        self.log.debug("received call [%s] for %s: %s(%s)" %
                       (id_, topic, name, args))

        # Try to execute
        if err is None:
            try:
                res = self.__cr.dispatch(name, *args)
            except Exception as e:
                err = str(e)

                # Write exception to log
                exc_type, exc_value, exc_traceback = sys.exc_info()
                self.log.error(
                    traceback.format_exception(exc_type, exc_value,
                                               exc_traceback))

        self.log.debug("returning call [%s]: %s / %s" % (id_, res, err))

        response = dumps({"result": res, "id": id_})

        # Get rid of it...
        mqtt = PluginRegistry.getInstance('MQTTClientHandler')
        mqtt.send_message(response, topic=response_topic)
Beispiel #58
0
    def requestPasswordReset(self, uid, step, uuid=None, data=None):
        """
        Request a password reset if the submitted password recovery answers match the stored ones for the given user
        :param uid: user id
        :param step: 'start' to trigger the password reset process by sending an email with activation link to the user
        :param uuid: the recovery uuid
        :param data: optional data required by the current step
        :return: *
        """

        # check for existing uid and status of the users password settings
        index = PluginRegistry.getInstance("ObjectIndex")
        res = index.search({'uid': uid, '_type': 'User'}, {'dn': 1})
        if len(res) == 0:
            raise PasswordException(C.make_error("UID_UNKNOWN", target=uid))
        dn = res[0]['dn']
        user = ObjectProxy(dn)
        if user.mail is None:
            raise PasswordException(
                C.make_error("PASSWORD_RECOVERY_IMPOSSIBLE"))

        recovery_state = loads(
            user.passwordRecoveryState
        ) if user.passwordRecoveryState is not None else {}

        if step != "start":
            # check uuid
            if 'uuid' not in recovery_state or recovery_state['uuid'] != uuid:
                # recovery process has not been started
                raise PasswordException(
                    C.make_error("PASSWORD_RECOVERY_STATE_ERROR"))

        if step == "start":
            # start process by generating an unique password recovery link for this user and sending it to him via mail

            if 'uuid' not in recovery_state:
                # generate a new id
                recovery_state['sent_counter'] = 0
                recovery_state['uuid'] = str(Uuid.uuid4())
                recovery_state['state'] = 'started'

            # send the link to the user
            content = N_(
                "Please open this link to continue your password recovery process."
            ) + ":"
            gui = PluginRegistry.getInstance("HTTPService").get_gui_uri()
            content += "\n\n%s?pwruid=%s&uid=%s\n\n" % (
                "/".join(gui), recovery_state['uuid'], uid)

            mail = PluginRegistry.getInstance("Mail")
            mail.send(user.mail, N_("Password recovery link"), content)
            recovery_state["sent_counter"] += 1

            user.passwordRecoveryState = dumps(recovery_state)
            user.commit()
            return True

        elif step == "get_questions":
            # check correct state
            if recovery_state['state'] is None:
                raise PasswordException(
                    C.make_error("PASSWORD_RECOVERY_STATE_ERROR"))

            # return the indices of the questions the user has answered
            recovery_hashes = loads(user.passwordRecoveryHash)
            # TODO retrieve minimum amount of correct answers from user policy object
            return random.sample(recovery_hashes.keys(), 3)

        elif step == "check_answers":
            # check correct state
            if recovery_state['state'] is None:
                raise PasswordException(
                    C.make_error("PASSWORD_RECOVERY_STATE_ERROR"))

            data = loads(data)
            recovery_hashes = loads(user.passwordRecoveryHash)
            correct_answers = 0
            for idx, answer in data.items():
                if idx not in recovery_hashes:
                    # the user hasn't answered this question
                    continue
                # detect method from existing answer
                pwd_o = self.detect_method_by_hash(recovery_hashes[idx])
                if not pwd_o:
                    raise PasswordException(
                        C.make_error("PASSWORD_RECOVERY_IMPOSSIBLE"))

                # encrypt and compare new answer
                if pwd_o.compare_hash(self.clean_string(answer),
                                      recovery_hashes[idx]):
                    correct_answers += 1

            # TODO retrieve minimum amount of correct answers from user policy object
            if correct_answers >= 3:
                recovery_state['state'] = 'verified'
                user.passwordRecoveryState = dumps(recovery_state)
                user.commit()
                return True
            else:
                return False

        elif step == "change_password":
            # check correct state
            if recovery_state['state'] != 'verified':
                raise PasswordException(
                    C.make_error("PASSWORD_RECOVERY_STATE_ERROR"))

            self.setUserPassword(uid, user.dn, data)

            user.passwordRecoveryState = None
            user.commit()
            return True