def test_sendEvent(self): e = EventMaker() backendChangeData = e.Event( e.BackendChange(e.ModificationTime("20150101000000Z"), e.ChangeType("modify"))) data = e.Event( e.ObjectChanged(e.ModificationTime("20150101000000Z"), e.ChangeType("modify"))) data_str = '<Event xmlns="http://www.gonicus.de/Events"><ObjectChanged><ModificationTime>20150101000000Z</ModificationTime><ChangeType>modify</ChangeType></ObjectChanged></Event>' mocked_resolver = mock.MagicMock() mocked_resolver.check.return_value = False mocked_resolver.isAdmin.return_value = False with mock.patch.dict("gosa.backend.command.PluginRegistry.modules", {'ACLResolver': mocked_resolver}),\ mock.patch("gosa.backend.command.SseHandler.send_message") as mocked_sse: with pytest.raises(EventNotAuthorized): self.reg.sendEvent('admin', data) mocked_resolver.check.return_value = True with pytest.raises(etree.XMLSyntaxError): # message without content self.reg.sendEvent('admin', e.Event(e.ObjectChanged())) # add listener handle_event = mock.MagicMock() ZopeEventConsumer(event_type='ObjectChanged', callback=handle_event.process) self.reg.sendEvent('admin', backendChangeData) assert not handle_event.process.called # send data as str self.reg.sendEvent('admin', data_str) assert handle_event.process.called args, kwargs = handle_event.process.call_args called_string = etree.tostring(args[0]) assert called_string.decode() == data_str handle_event.reset_mock() # send data as bytes self.reg.sendEvent('admin', bytes(data_str, 'utf-8')) assert handle_event.process.called args, kwargs = handle_event.process.call_args called_string = etree.tostring(args[0]) assert called_string.decode() == data_str handle_event.reset_mock() # send data as xml self.reg.sendEvent('admin', data) assert handle_event.process.called args, kwargs = handle_event.process.call_args called_string = etree.tostring(args[0]) assert called_string.decode() == data_str handle_event.reset_mock()
def test_post(self): # create webhook post e = EventMaker() update = e.Event( e.BackendChange( e.DN("cn=Test,ou=people,dc=example,dc=net"), e.ModificationTime(datetime.now().strftime("%Y%m%d%H%M%SZ")), e.ChangeType("update") ) ) payload = etree.tostring(update) token = bytes(Environment.getInstance().config.get("webhooks.ldap_monitor_token"), 'ascii') signature_hash = hmac.new(token, msg=payload, digestmod="sha512") signature = 'sha1=' + signature_hash.hexdigest() headers = { 'Content-Type': 'application/vnd.gosa.event+xml', 'HTTP_X_HUB_SENDER': 'backend-monitor', 'HTTP_X_HUB_SIGNATURE': signature } with mock.patch("gosa.backend.plugins.webhook.registry.zope.event.notify") as m_notify: AsyncHTTPTestCase.fetch(self, "/hooks/", method="POST", headers=headers, body=payload) assert m_notify.called m_notify.reset_mock() # unregistered sender headers['HTTP_X_HUB_SENDER'] = 'unknown' resp = AsyncHTTPTestCase.fetch(self, "/hooks/", method="POST", headers=headers, body=payload) assert resp.code == 401 assert not m_notify.called # wrong signature headers['HTTP_X_HUB_SENDER'] = 'backend-monitor' headers['HTTP_X_HUB_SIGNATURE'] = 'sha1=823rjadfkjlasasddfdgasdfgasd' resp = AsyncHTTPTestCase.fetch(self, "/hooks/", method="POST", headers=headers, body=payload) assert resp.code == 401 assert not m_notify.called # no signature del headers['HTTP_X_HUB_SIGNATURE'] resp = AsyncHTTPTestCase.fetch(self, "/hooks/", method="POST", headers=headers, body=payload) assert resp.code == 401 assert not m_notify.called # no handler for content type headers['HTTP_X_HUB_SIGNATURE'] = signature headers['Content-Type'] = 'application/vnd.gosa.unknown+xml' resp = AsyncHTTPTestCase.fetch(self, "/hooks/", method="POST", headers=headers, body=payload) assert resp.code == 401 assert not m_notify.called
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
def monitor(path, modifier, token, webhook_target, initially_failed=False): # Initialize dn, timestamp and change type. dn = None ts = None ct = None try: with open(path, encoding='utf-8', errors='ignore') as f: # Collect lines until a newline occurs, fill # dn, ts and ct accordingly. Entries that only # change administrative values. for line in tail(f, initially_failed): # Catch dn if line.startswith("dn::"): dn = b64decode(line[5:]).decode('utf-8') continue elif line.startswith("dn:"): dn = line[4:] continue # Catch modifyTimestamp if line.startswith("modifyTimestamp:"): ts = line[17:] continue # Catch changetype if line.startswith("changetype:"): ct = line[12:] continue # Check modifiers name and if it's the # gosa-backend who triggered the change, # just reset the DN, because we don't need # to propagate this change. if line.startswith("modifiersName:"): if line[14:].lower() == modifier.lower(): dn = None continue # Trigger on newline. if line == "": if dn: if not ts: ts = datetime.now().strftime("%Y%m%d%H%M%SZ") e = EventMaker() update = e.Event( e.BackendChange( e.DN(dn), e.ModificationTime(ts), e.ChangeType(ct) ) ) payload = etree.tostring(update) headers = { 'Content-Type': 'application/vnd.gosa.event+xml', 'HTTP_X_HUB_SENDER': 'backend-monitor', 'HTTP_X_HUB_SIGNATURE': get_signature(token, payload) } requests.post(webhook_target, data=payload, headers=headers) dn = ts = ct = None except Exception as e: print("Error:", str(e))