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)
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
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
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)
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")
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")
def __process_event(self, topic, message): super(MqttEventConsumer, self)._process_event(etree.fromstring(message, PluginRegistry.getEventParser()))
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)
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")
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")
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)
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)