Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0384.py @ 3214:8d92d4d829fb
plugin XEP-0384: use "max_items=1" for devices and bundles nodes:
- max_items = 1 is requested with publish-options when publishing the bundle or the
devices list. If the constraint is failed, a warning will be shown, and the publication
will be done without this option
- fixed return values when devices list is badly formed
| author | Goffi <goffi@goffi.org> |
|---|---|
| date | Wed, 11 Mar 2020 20:44:49 +0100 |
| parents | dcebc585c29f |
| children | 806a7936a591 |
comparison
equal
deleted
inserted
replaced
| 3213:c2f958dde5d2 | 3214:8d92d4d829fb |
|---|---|
| 21 from sat.core.log import getLogger | 21 from sat.core.log import getLogger |
| 22 from sat.core import exceptions | 22 from sat.core import exceptions |
| 23 from twisted.internet import defer, reactor | 23 from twisted.internet import defer, reactor |
| 24 from twisted.words.xish import domish | 24 from twisted.words.xish import domish |
| 25 from twisted.words.protocols.jabber import jid | 25 from twisted.words.protocols.jabber import jid |
| 26 from twisted.words.protocols.jabber import error | 26 from twisted.words.protocols.jabber import error as jabber_error |
| 27 from sat.memory import persistent | 27 from sat.memory import persistent |
| 28 from functools import partial | 28 from functools import partial |
| 29 from sat.tools import xml_tools | 29 from sat.tools import xml_tools |
| 30 import logging | 30 import logging |
| 31 import random | 31 import random |
| 395 self._sid = host.plugins.get("XEP-0359") | 395 self._sid = host.plugins.get("XEP-0359") |
| 396 host.trigger.add("messageReceived", self._messageReceivedTrigger, priority=100050) | 396 host.trigger.add("messageReceived", self._messageReceivedTrigger, priority=100050) |
| 397 host.trigger.add("sendMessageData", self._sendMessageDataTrigger) | 397 host.trigger.add("sendMessageData", self._sendMessageDataTrigger) |
| 398 self.host.registerEncryptionPlugin(self, "OMEMO", NS_OMEMO, 100) | 398 self.host.registerEncryptionPlugin(self, "OMEMO", NS_OMEMO, 100) |
| 399 pep = host.plugins['XEP-0163'] | 399 pep = host.plugins['XEP-0163'] |
| 400 pep.addPEPEvent("OMEMO_DEVICES", NS_OMEMO_DEVICES, self.onNewDevices) | 400 pep.addPEPEvent( |
| 401 "OMEMO_DEVICES", NS_OMEMO_DEVICES, | |
| 402 lambda itemsEvent, profile: defer.ensureDeferred( | |
| 403 self.onNewDevices(itemsEvent, profile)) | |
| 404 ) | |
| 401 | 405 |
| 402 @defer.inlineCallbacks | 406 @defer.inlineCallbacks |
| 403 def trustUICb(self, xmlui_data, trust_data, expect_problems=None, | 407 def trustUICb(self, xmlui_data, trust_data, expect_problems=None, |
| 404 profile=C.PROF_KEY_NONE): | 408 profile=C.PROF_KEY_NONE): |
| 405 if C.bool(xmlui_data.get('cancelled', 'false')): | 409 if C.bool(xmlui_data.get('cancelled', 'false')): |
| 568 while device_id in devices: | 572 while device_id in devices: |
| 569 device_id = random.randint(1, 2**31-1) | 573 device_id = random.randint(1, 2**31-1) |
| 570 # and we save it | 574 # and we save it |
| 571 persistent_dict[KEY_DEVICE_ID] = device_id | 575 persistent_dict[KEY_DEVICE_ID] = device_id |
| 572 | 576 |
| 577 log.debug(f"our OMEMO device id is {device_id}") | |
| 578 | |
| 573 if device_id not in devices: | 579 if device_id not in devices: |
| 580 log.debug(f"our device id ({device_id}) is not in the list, adding it") | |
| 574 devices.add(device_id) | 581 devices.add(device_id) |
| 575 yield self.setDevices(client, devices) | 582 yield defer.ensureDeferred(self.setDevices(client, devices)) |
| 576 | 583 |
| 577 all_jids = yield persistent_dict.get(KEY_ALL_JIDS, set()) | 584 all_jids = yield persistent_dict.get(KEY_ALL_JIDS, set()) |
| 578 | 585 |
| 579 omemo_storage = OmemoStorage(client, device_id, all_jids, persistent_dict) | 586 omemo_storage = OmemoStorage(client, device_id, all_jids, persistent_dict) |
| 580 omemo_session = yield OmemoSession.create(client, omemo_storage, device_id) | 587 omemo_session = yield OmemoSession.create(client, omemo_storage, device_id) |
| 583 client._xep_0384_device_id = device_id | 590 client._xep_0384_device_id = device_id |
| 584 yield omemo_session.newDeviceList(client.jid, devices) | 591 yield omemo_session.newDeviceList(client.jid, devices) |
| 585 if omemo_session.republish_bundle: | 592 if omemo_session.republish_bundle: |
| 586 log.info(_("Saving public bundle for this device ({device_id})").format( | 593 log.info(_("Saving public bundle for this device ({device_id})").format( |
| 587 device_id=device_id)) | 594 device_id=device_id)) |
| 588 yield self.setBundle(client, omemo_session.public_bundle, device_id) | 595 yield defer.ensureDeferred( |
| 596 self.setBundle(client, omemo_session.public_bundle, device_id) | |
| 597 ) | |
| 589 client._xep_0384_ready.callback(None) | 598 client._xep_0384_ready.callback(None) |
| 590 del client._xep_0384_ready | 599 del client._xep_0384_ready |
| 591 | 600 |
| 592 ## XMPP PEP nodes manipulation | 601 ## XMPP PEP nodes manipulation |
| 593 | 602 |
| 606 if items: | 615 if items: |
| 607 try: | 616 try: |
| 608 list_elt = next(items[0].elements(NS_OMEMO, 'list')) | 617 list_elt = next(items[0].elements(NS_OMEMO, 'list')) |
| 609 except StopIteration: | 618 except StopIteration: |
| 610 log.warning(_("no list element found in OMEMO devices list")) | 619 log.warning(_("no list element found in OMEMO devices list")) |
| 611 return | 620 return devices |
| 612 for device_elt in list_elt.elements(NS_OMEMO, 'device'): | 621 for device_elt in list_elt.elements(NS_OMEMO, 'device'): |
| 613 try: | 622 try: |
| 614 device_id = int(device_elt['id']) | 623 device_id = int(device_elt['id']) |
| 615 except KeyError: | 624 except KeyError: |
| 616 log.warning(_('device element is missing "id" attribute: {elt}') | 625 log.warning(_('device element is missing "id" attribute: {elt}') |
| 639 defer.returnValue(set()) | 648 defer.returnValue(set()) |
| 640 | 649 |
| 641 devices = self.parseDevices(items) | 650 devices = self.parseDevices(items) |
| 642 defer.returnValue(devices) | 651 defer.returnValue(devices) |
| 643 | 652 |
| 644 def setDevicesEb(self, failure_): | 653 async def setDevices(self, client, devices): |
| 645 log.warning(_("Can't set devices: {reason}").format(reason=failure_)) | 654 log.debug(f"setting devices with {', '.join(devices)}") |
| 646 | |
| 647 def setDevices(self, client, devices): | |
| 648 list_elt = domish.Element((NS_OMEMO, 'list')) | 655 list_elt = domish.Element((NS_OMEMO, 'list')) |
| 649 for device in devices: | 656 for device in devices: |
| 650 device_elt = list_elt.addElement('device') | 657 device_elt = list_elt.addElement('device') |
| 651 device_elt['id'] = str(device) | 658 device_elt['id'] = str(device) |
| 652 d = self._p.sendItem( | 659 try: |
| 653 client, None, NS_OMEMO_DEVICES, list_elt, item_id=self._p.ID_SINGLETON) | 660 await self._p.sendItem( |
| 654 d.addErrback(self.setDevicesEb) | 661 client, None, NS_OMEMO_DEVICES, list_elt, |
| 655 return d | 662 item_id=self._p.ID_SINGLETON, |
| 663 extra={ | |
| 664 self._p.EXTRA_PUBLISH_OPTIONS: {self._p.OPT_MAX_ITEMS: 1}, | |
| 665 self._p.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options", | |
| 666 } | |
| 667 ) | |
| 668 except Exception as e: | |
| 669 log.warning(_("Can't set devices: {reason}").format(reason=e)) | |
| 656 | 670 |
| 657 # bundles | 671 # bundles |
| 658 | 672 |
| 659 @defer.inlineCallbacks | 673 @defer.inlineCallbacks |
| 660 def getBundles(self, client, entity_jid, devices_ids): | 674 def getBundles(self, client, entity_jid, devices_ids): |
| 678 except exceptions.NotFound: | 692 except exceptions.NotFound: |
| 679 log.warning(_("Bundle missing for device {device_id}") | 693 log.warning(_("Bundle missing for device {device_id}") |
| 680 .format(device_id=device_id)) | 694 .format(device_id=device_id)) |
| 681 missing.add(device_id) | 695 missing.add(device_id) |
| 682 continue | 696 continue |
| 683 except error.StanzaError as e: | 697 except jabber_error.StanzaError as e: |
| 684 log.warning(_("Can't get bundle for device {device_id}: {reason}") | 698 log.warning(_("Can't get bundle for device {device_id}: {reason}") |
| 685 .format(device_id=device_id, reason=e)) | 699 .format(device_id=device_id, reason=e)) |
| 686 continue | 700 continue |
| 687 if not items: | 701 if not items: |
| 688 log.warning(_("no item found in node {node}, can't get public bundle " | 702 log.warning(_("no item found in node {node}, can't get public bundle " |
| 735 bundles[device_id] = ExtendedPublicBundle.parse(omemo_backend, ik, spk, | 749 bundles[device_id] = ExtendedPublicBundle.parse(omemo_backend, ik, spk, |
| 736 spkSignature, otpks) | 750 spkSignature, otpks) |
| 737 | 751 |
| 738 defer.returnValue((bundles, missing)) | 752 defer.returnValue((bundles, missing)) |
| 739 | 753 |
| 740 def setBundleEb(self, failure_): | 754 async def setBundle(self, client, bundle, device_id): |
| 741 log.warning(_("Can't set bundle: {reason}").format(reason=failure_)) | |
| 742 | |
| 743 def setBundle(self, client, bundle, device_id): | |
| 744 """Set public bundle for this device. | 755 """Set public bundle for this device. |
| 745 | 756 |
| 746 @param bundle(ExtendedPublicBundle): bundle to publish | 757 @param bundle(ExtendedPublicBundle): bundle to publish |
| 747 """ | 758 """ |
| 748 log.debug(_("updating bundle for {device_id}").format(device_id=device_id)) | 759 log.debug(_("updating bundle for {device_id}").format(device_id=device_id)) |
| 767 'preKeyPublic', | 778 'preKeyPublic', |
| 768 content=b64enc(otpk["key"])) | 779 content=b64enc(otpk["key"])) |
| 769 preKeyPublic_elt['preKeyId'] = str(otpk['id']) | 780 preKeyPublic_elt['preKeyId'] = str(otpk['id']) |
| 770 | 781 |
| 771 node = NS_OMEMO_BUNDLE.format(device_id=device_id) | 782 node = NS_OMEMO_BUNDLE.format(device_id=device_id) |
| 772 d = self._p.sendItem(client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON) | 783 try: |
| 773 d.addErrback(self.setBundleEb) | 784 await self._p.sendItem( |
| 774 return d | 785 client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON, |
| 786 extra={ | |
| 787 self._p.EXTRA_PUBLISH_OPTIONS: {self._p.OPT_MAX_ITEMS: 1}, | |
| 788 self._p.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options", | |
| 789 } | |
| 790 ) | |
| 791 except Exception as e: | |
| 792 log.warning(_("Can't set bundle: {reason}").format(reason=e)) | |
| 775 | 793 |
| 776 ## PEP node events callbacks | 794 ## PEP node events callbacks |
| 777 | 795 |
| 778 @defer.inlineCallbacks | 796 async def onNewDevices(self, itemsEvent, profile): |
| 779 def onNewDevices(self, itemsEvent, profile): | |
| 780 client = self.host.getClient(profile) | 797 client = self.host.getClient(profile) |
| 781 try: | 798 try: |
| 782 omemo_session = client._xep_0384_session | 799 omemo_session = client._xep_0384_session |
| 783 except AttributeError: | 800 except AttributeError: |
| 784 yield client._xep_0384_ready | 801 await client._xep_0384_ready |
| 785 omemo_session = client._xep_0384_session | 802 omemo_session = client._xep_0384_session |
| 786 entity = itemsEvent.sender | 803 entity = itemsEvent.sender |
| 787 | 804 |
| 788 devices = self.parseDevices(itemsEvent.items) | 805 devices = self.parseDevices(itemsEvent.items) |
| 789 omemo_session.newDeviceList(entity, devices) | 806 await omemo_session.newDeviceList(entity, devices) |
| 790 | 807 |
| 791 if entity == client.jid.userhostJID(): | 808 if entity == client.jid.userhostJID(): |
| 792 own_device = client._xep_0384_device_id | 809 own_device = client._xep_0384_device_id |
| 793 if own_device not in devices: | 810 if own_device not in devices: |
| 794 log.warning(_("Our own device is missing from devices list, fixing it")) | 811 log.warning(_("Our own device is missing from devices list, fixing it")) |
| 795 devices.add(own_device) | 812 devices.add(own_device) |
| 796 yield self.setDevices(client, devices) | 813 await self.setDevices(client, devices) |
| 797 | 814 |
| 798 ## triggers | 815 ## triggers |
| 799 | 816 |
| 800 @defer.inlineCallbacks | 817 @defer.inlineCallbacks |
| 801 def handleProblems(self, client, feedback_jid, bundles, expect_problems, problems): | 818 def handleProblems(self, client, feedback_jid, bundles, expect_problems, problems): |
| 1096 finally: | 1113 finally: |
| 1097 if omemo_session.republish_bundle: | 1114 if omemo_session.republish_bundle: |
| 1098 # we don't wait for the Deferred (i.e. no yield) on purpose | 1115 # we don't wait for the Deferred (i.e. no yield) on purpose |
| 1099 # there is no need to block the whole message workflow while | 1116 # there is no need to block the whole message workflow while |
| 1100 # updating the bundle | 1117 # updating the bundle |
| 1101 self.setBundle(client, omemo_session.public_bundle, device_id) | 1118 defer.ensureDeferred( |
| 1119 self.setBundle(client, omemo_session.public_bundle, device_id) | |
| 1120 ) | |
| 1102 | 1121 |
| 1103 message_elt.children.remove(encrypted_elt) | 1122 message_elt.children.remove(encrypted_elt) |
| 1104 if plaintext: | 1123 if plaintext: |
| 1105 message_elt.addElement("body", content=plaintext) | 1124 message_elt.addElement("body", content=plaintext) |
| 1106 post_treat.addCallback(client.encryption.markAsEncrypted) | 1125 post_treat.addCallback(client.encryption.markAsEncrypted) |
