Beispiel #1
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 #2
0
    def sendEvent(self, user, data):
        """
        Sends an event to the SSE handler and the zope event bus. Data must be in XML format,
        see :ref:`Events handling <events>` for details.

        ========== ============
        Parameter  Description
        ========== ============
        data       valid event
        ========== ============

        *sendEvent* will indirectly validate the event against the bundled "XSD".
        """

        try:
            event = "<?xml version='1.0'?>\n"

            if isinstance(data, str):
                event += data
            elif isinstance(data, bytes):
                event += data.decode('utf-8')
            else:
                event += etree.tostring(data,
                                        pretty_print=True).decode('utf-8')

            # Validate event
            xml = objectify.fromstring(event, PluginRegistry.getEventParser())
            event_type = list(xml.__dict__.keys())[0]
            # If a user was supplied, check if she's authorized...
            if user:
                acl = PluginRegistry.getInstance("ACLResolver")
                topic = ".".join([self.env.domain, 'event', event_type])
                if not acl.check(user, topic, "x"):
                    raise EventNotAuthorized(
                        "sending the event '%s' is not permitted" % topic)

            if event_type in [
                    'ObjectChanged', 'Notification', 'ObjectCloseAnnouncement'
            ]:
                params = {'channel': 'broadcast'}

                if event_type == "Notification" and xml.Notification.Target.text != "all":
                    params[
                        'channel'] = "user.%s" % xml.Notification.Target.text
                if event_type == "ObjectCloseAnnouncement":
                    params[
                        'channel'] = "user.%s" % xml.ObjectCloseAnnouncement.Target.text

                SseHandler.notify(xml, **params)

            zope.event.notify(Event(data=xml, emitter=user))

        except etree.XMLSyntaxError as e:
            if self.env:
                self.log.error("event rejected (%s): %s" % (str(e), data))
            raise
Beispiel #3
0
    def sendEvent(self, user, data):
        """
        Sends an event to the SSE handler and the zope event bus. Data must be in XML format,
        see :ref:`Events handling <events>` for details.

        ========== ============
        Parameter  Description
        ========== ============
        data       valid event
        ========== ============

        *sendEvent* will indirectly validate the event against the bundled "XSD".
        """

        try:
            event = "<?xml version='1.0'?>\n"

            if isinstance(data, str):
                event += data
            elif isinstance(data, bytes):
                event += data.decode('utf-8')
            else:
                event += etree.tostring(data, pretty_print=True).decode('utf-8')

            # Validate event
            xml = objectify.fromstring(event, PluginRegistry.getEventParser())
            event_type = list(xml.__dict__.keys())[0]
            # If a user was supplied, check if she's authorized...
            if user:
                acl = PluginRegistry.getInstance("ACLResolver")
                topic = ".".join([self.env.domain, 'event', event_type])
                if not acl.check(user, topic, "x"):
                    raise EventNotAuthorized("sending the event '%s' is not permitted" % topic)

            if event_type in ['ObjectChanged', 'Notification', 'ObjectCloseAnnouncement']:
                params = {'channel': 'broadcast'}

                if event_type == "Notification" and xml.Notification.Target.text != "all":
                    params['channel'] = "user.%s" % xml.Notification.Target.text
                if event_type == "ObjectCloseAnnouncement":
                    params['channel'] = "user.%s" % xml.ObjectCloseAnnouncement.Target.text

                SseHandler.notify(xml, **params)

            zope.event.notify(Event(data=xml, emitter=user))

        except etree.XMLSyntaxError as e:
            if self.env:
                self.log.error("event rejected (%s): %s" % (str(e), data))
            raise
Beispiel #4
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 #5
0
    def __handle_events(self, event):
        if isinstance(event, objectify.ObjectifiedElement):
            self.__backend_change_processor(event)

        elif isinstance(event, ObjectChanged):
            change_type = None
            _uuid = event.uuid
            _dn = None
            _last_changed = datetime.datetime.now()

            # Try to find the affected DN
            e = self.__session.query(ObjectInfoIndex).filter(ObjectInfoIndex.uuid == _uuid).one_or_none()
            if e:

                # New pre-events don't have a dn. Just skip is in this case...
                if hasattr(e, 'dn'):
                    _dn = e.dn
                    _last_changed = e._last_modified
                else:
                    _dn = "not known yet"

            if event.reason == "post object remove":
                self.log.debug("removing object index for %s" % _uuid)
                self.remove_by_uuid(_uuid)
                change_type = "remove"

            if event.reason == "post object move":
                self.log.debug("updating object index for %s" % _uuid)
                obj = ObjectProxy(event.dn)
                self.update(obj)
                _dn = obj.dn
                change_type = "move"

            if event.reason == "post object create":
                self.log.debug("creating object index for %s" % _uuid)
                obj = ObjectProxy(event.dn)
                self.insert(obj)
                _dn = obj.dn
                change_type = "create"

            if event.reason in ["post object update"]:
                self.log.debug("updating object index for %s" % _uuid)
                if not event.dn:
                    dn = self.__session.query(ObjectInfoIndex.dn).filter(ObjectInfoIndex.uuid == _uuid).one_or_none()
                    if dn:
                        event.dn = dn

                obj = ObjectProxy(event.dn)
                self.update(obj)
                change_type = "update"

            # send the event to the clients
            e = EventMaker()

            if event.reason[0:4] == "post" and _uuid and _dn and change_type:

                ev = e.Event(e.ObjectChanged(
                    e.UUID(_uuid),
                    e.DN(_dn),
                    e.ModificationTime(_last_changed.strftime("%Y%m%d%H%M%SZ")),
                    e.ChangeType(change_type)
                ))
                event = "<?xml version='1.0'?>\n%s" % etree.tostring(ev, pretty_print=True).decode('utf-8')

                # Validate event
                xml = objectify.fromstring(event, PluginRegistry.getEventParser())

                SseHandler.notify(xml, channel="broadcast")
Beispiel #6
0
    def __backend_change_processor(self, data):
        """
        This method gets called if an external backend reports
        a modification of an entry under its hood.

        We use it to update / create / delete existing index
        entries.
        """
        ObjectIndex.importing = True
        data = data.BackendChange
        dn = data.DN.text if hasattr(data, 'DN') else None
        new_dn = data.NewDN.text if hasattr(data, 'NewDN') else None
        change_type = data.ChangeType.text
        _uuid = data.UUID.text if hasattr(data, 'UUID') else None
        _last_changed = datetime.datetime.strptime(data.ModificationTime.text, "%Y%m%d%H%M%SZ")
        obj = None

        if not _uuid and not dn:
            return

        # Set importing flag to true in order to be able to post process incoming
        # objects.
        ObjectIndex.importing = True

        # Setup or refresh timer job to run the post processing
        sched = PluginRegistry.getInstance("SchedulerService").getScheduler()
        next_run = datetime.datetime.now() + datetime.timedelta(0, 5)
        if self._post_process_job:
            sched.reschedule_date_job(self._post_process_job, next_run)
        else:
            self._post_process_job = sched.add_date_job(self._post_process_by_timer, next_run, tag='_internal', jobstore="ram", )

        # Resolve dn from uuid if needed
        if not dn:
            dn = self.__session.query(ObjectInfoIndex.dn).filter(ObjectInfoIndex.uuid == _uuid).one_or_none()

        # Modification
        if change_type == "modify":

            # Get object
            obj = self._get_object(dn)
            if not obj:
                return

            # Check if the entry exists - if not, maybe let create it
            entry = self.__session.query(ObjectInfoIndex.dn).filter(
                or_(
                    ObjectInfoIndex.uuid == _uuid,
                    func.lower(ObjectInfoIndex.dn) == func.lower(dn)
                )).one_or_none()
            if entry:
                self.update(obj)

            else:
                self.insert(obj)

        # Add
        if change_type == "add":

            # Get object
            obj = self._get_object(dn)
            if not obj:
                return

            self.insert(obj)

        # Delete
        if change_type == "delete":
            self.log.info("object has changed in backend: indexing %s" % dn)
            self.log.warning("external delete might not take care about references")
            if _uuid is not None:
                self.remove_by_uuid(_uuid)
            else:
                obj = self._get_object(dn)
                if not obj:
                    return

                self.remove(obj)

        # Move
        if change_type in ['modrdn', 'moddn']:

            # Get object
            obj = self._get_object(new_dn)
            if not obj:
                return

            # Check if the entry exists - if not, maybe let create it
            entry = self.__session.query(ObjectInfoIndex.dn).filter(
                or_(
                    ObjectInfoIndex.uuid == _uuid,
                    func.lower(ObjectInfoIndex.dn) == func.lower(dn)
                )).one_or_none()

            if entry:
                self.update(obj)

            else:
                self.insert(obj)

        # send the event to the clients
        event_change_type = "update"
        if change_type == "add":
            event_change_type = "create"
        elif change_type == "delete":
            event_change_type = "remove"

        e = EventMaker()
        if obj:
            ev = e.Event(e.ObjectChanged(
                e.UUID(obj.uuid),
                e.DN(obj.dn),
                e.ModificationTime(_last_changed.strftime("%Y%m%d%H%M%SZ")),
                e.ChangeType(event_change_type)
            ))
        else:
            ev = e.Event(e.ObjectChanged(
                e.UUID(_uuid),
                e.DN(dn),
                e.ModificationTime(_last_changed.strftime("%Y%m%d%H%M%SZ")),
                e.ChangeType(event_change_type)
            ))

        event = "<?xml version='1.0'?>\n%s" % etree.tostring(ev, pretty_print=True).decode('utf-8')

        # Validate event
        xml = objectify.fromstring(event, PluginRegistry.getEventParser())

        SseHandler.notify(xml, channel="broadcast")
Beispiel #7
0
 def __process_event(self, topic, message):
     super(MqttEventConsumer, self)._process_event(etree.fromstring(message, PluginRegistry.getEventParser()))
Beispiel #8
0
 def handleRequest(self, request):
     # read and validate event
     xml = objectify.fromstring(request.body, PluginRegistry.getEventParser())
     # forward incoming event to internal event bus
     zope.event.notify(xml)
Beispiel #9
0
    def __handle_events(self, event):
        if isinstance(event, objectify.ObjectifiedElement):
            self.__backend_change_processor(event)

        elif isinstance(event, ObjectChanged):
            change_type = None
            _uuid = event.uuid
            _dn = None
            _last_changed = datetime.datetime.now()

            # Try to find the affected DN
            e = self.__session.query(ObjectInfoIndex).filter(
                ObjectInfoIndex.uuid == _uuid).one_or_none()
            if e:

                # New pre-events don't have a dn. Just skip is in this case...
                if hasattr(e, 'dn'):
                    _dn = e.dn
                    _last_changed = e._last_modified
                else:
                    _dn = "not known yet"

            if event.reason == "post object remove":
                self.log.debug("removing object index for %s" % _uuid)
                self.remove_by_uuid(_uuid)
                change_type = "remove"

            if event.reason == "post object move":
                self.log.debug("updating object index for %s" % _uuid)
                obj = ObjectProxy(event.dn)
                self.update(obj)
                _dn = obj.dn
                change_type = "move"

            if event.reason == "post object create":
                self.log.debug("creating object index for %s" % _uuid)
                obj = ObjectProxy(event.dn)
                self.insert(obj)
                _dn = obj.dn
                change_type = "create"

            if event.reason in ["post object update"]:
                self.log.debug("updating object index for %s" % _uuid)
                if not event.dn:
                    dn = self.__session.query(ObjectInfoIndex.dn).filter(
                        ObjectInfoIndex.uuid == _uuid).one_or_none()
                    if dn:
                        event.dn = dn

                obj = ObjectProxy(event.dn)
                self.update(obj)
                change_type = "update"

            # send the event to the clients
            e = EventMaker()

            if event.reason[0:4] == "post" and _uuid and _dn and change_type:

                ev = e.Event(
                    e.ObjectChanged(
                        e.UUID(_uuid), e.DN(_dn),
                        e.ModificationTime(
                            _last_changed.strftime("%Y%m%d%H%M%SZ")),
                        e.ChangeType(change_type)))
                event = "<?xml version='1.0'?>\n%s" % etree.tostring(
                    ev, pretty_print=True).decode('utf-8')

                # Validate event
                xml = objectify.fromstring(event,
                                           PluginRegistry.getEventParser())

                SseHandler.notify(xml, channel="broadcast")
Beispiel #10
0
    def __backend_change_processor(self, data):
        """
        This method gets called if an external backend reports
        a modification of an entry under its hood.

        We use it to update / create / delete existing index
        entries.
        """
        ObjectIndex.importing = True
        data = data.BackendChange
        dn = data.DN.text if hasattr(data, 'DN') else None
        new_dn = data.NewDN.text if hasattr(data, 'NewDN') else None
        change_type = data.ChangeType.text
        _uuid = data.UUID.text if hasattr(data, 'UUID') else None
        _last_changed = datetime.datetime.strptime(data.ModificationTime.text,
                                                   "%Y%m%d%H%M%SZ")
        obj = None

        if not _uuid and not dn:
            return

        # Set importing flag to true in order to be able to post process incoming
        # objects.
        ObjectIndex.importing = True

        # Setup or refresh timer job to run the post processing
        sched = PluginRegistry.getInstance("SchedulerService").getScheduler()
        next_run = datetime.datetime.now() + datetime.timedelta(0, 5)
        if self._post_process_job:
            sched.reschedule_date_job(self._post_process_job, next_run)
        else:
            self._post_process_job = sched.add_date_job(
                self._post_process_by_timer,
                next_run,
                tag='_internal',
                jobstore="ram",
            )

        # Resolve dn from uuid if needed
        if not dn:
            dn = self.__session.query(ObjectInfoIndex.dn).filter(
                ObjectInfoIndex.uuid == _uuid).one_or_none()

        # Modification
        if change_type == "modify":

            # Get object
            obj = self._get_object(dn)
            if not obj:
                return

            # Check if the entry exists - if not, maybe let create it
            entry = self.__session.query(ObjectInfoIndex.dn).filter(
                or_(ObjectInfoIndex.uuid == _uuid,
                    func.lower(
                        ObjectInfoIndex.dn) == func.lower(dn))).one_or_none()
            if entry:
                self.update(obj)

            else:
                self.insert(obj)

        # Add
        if change_type == "add":

            # Get object
            obj = self._get_object(dn)
            if not obj:
                return

            self.insert(obj)

        # Delete
        if change_type == "delete":
            self.log.info("object has changed in backend: indexing %s" % dn)
            self.log.warning(
                "external delete might not take care about references")
            if _uuid is not None:
                self.remove_by_uuid(_uuid)
            else:
                obj = self._get_object(dn)
                if not obj:
                    return

                self.remove(obj)

        # Move
        if change_type in ['modrdn', 'moddn']:

            # Get object
            obj = self._get_object(new_dn)
            if not obj:
                return

            # Check if the entry exists - if not, maybe let create it
            entry = self.__session.query(ObjectInfoIndex.dn).filter(
                or_(ObjectInfoIndex.uuid == _uuid,
                    func.lower(
                        ObjectInfoIndex.dn) == func.lower(dn))).one_or_none()

            if entry:
                self.update(obj)

            else:
                self.insert(obj)

        # send the event to the clients
        event_change_type = "update"
        if change_type == "add":
            event_change_type = "create"
        elif change_type == "delete":
            event_change_type = "remove"

        e = EventMaker()
        if obj:
            ev = e.Event(
                e.ObjectChanged(
                    e.UUID(obj.uuid), e.DN(obj.dn),
                    e.ModificationTime(
                        _last_changed.strftime("%Y%m%d%H%M%SZ")),
                    e.ChangeType(event_change_type)))
        else:
            ev = e.Event(
                e.ObjectChanged(
                    e.UUID(_uuid), e.DN(dn),
                    e.ModificationTime(
                        _last_changed.strftime("%Y%m%d%H%M%SZ")),
                    e.ChangeType(event_change_type)))

        event = "<?xml version='1.0'?>\n%s" % etree.tostring(
            ev, pretty_print=True).decode('utf-8')

        # Validate event
        xml = objectify.fromstring(event, PluginRegistry.getEventParser())

        SseHandler.notify(xml, channel="broadcast")
Beispiel #11
0
 def handle_request(self, requestHandler):
     # read and validate event
     xml = objectify.fromstring(requestHandler.request.body, PluginRegistry.getEventParser())
     # forward incoming event to internal event bus
     zope.event.notify(xml)
Beispiel #12
0
 def handle_request(self, requestHandler):
     # read and validate event
     self.log.debug('Received event via webhook: %s' % requestHandler.request.body)
     xml = objectify.fromstring(requestHandler.request.body, PluginRegistry.getEventParser())
     # forward incoming event to internal event bus
     zope.event.notify(xml)