Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_xep_0384.py @ 4216:1a7a3e4b52a4
core (memory/migration):
Update XEP-0384 and `fe3a02cb4bec_convert_legacypickle_columns_to_json.py` migration to
properly handle (de)serialisation of `TrustMessageCacheEntry`.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 05 Mar 2024 17:31:12 +0100 |
parents | 15482dc0b5d1 |
children | c0f3f29377f1 |
comparison
equal
deleted
inserted
replaced
4215:31c84a32c897 | 4216:1a7a3e4b52a4 |
---|---|
432 | 432 |
433 target_jid: jid.JID | 433 target_jid: jid.JID |
434 target_key: bytes | 434 target_key: bytes |
435 target_trust: bool | 435 target_trust: bool |
436 | 436 |
437 def to_dict(self) -> dict[str, Any]: | |
438 """Convert the instance to a serialised dictionary""" | |
439 data = { | |
440 "target_jid": self.target_jid.full(), | |
441 "target_key": self.target_key.hex(), | |
442 "target_trust": self.target_trust, | |
443 } | |
444 return data | |
445 | |
446 @staticmethod | |
447 def from_dict(data: dict[str, Any]) -> "TrustUpdate": | |
448 """Load a serialized dictionary""" | |
449 data["target_jid"] = jid.JID(data["target_jid"]) | |
450 data["target_key"] = bytes.fromhex(data["target_key"]) | |
451 return TrustUpdate(**data) | |
452 | |
437 | 453 |
438 class TrustMessageCacheEntry(NamedTuple): | 454 class TrustMessageCacheEntry(NamedTuple): |
439 # pylint: disable=invalid-name | 455 # pylint: disable=invalid-name |
440 """ | 456 """ |
441 An entry in the trust message cache used by ATM. | 457 An entry in the trust message cache used by ATM. |
443 | 459 |
444 sender_jid: jid.JID | 460 sender_jid: jid.JID |
445 sender_key: bytes | 461 sender_key: bytes |
446 timestamp: datetime | 462 timestamp: datetime |
447 trust_update: TrustUpdate | 463 trust_update: TrustUpdate |
464 | |
465 def to_dict(self) -> dict[str, Any]: | |
466 """Convert the instance to a serialised dictionary""" | |
467 data = { | |
468 "sender_jid": self.sender_jid.full(), | |
469 "sender_key": self.sender_key.hex(), | |
470 "timestamp": self.timestamp.isoformat(), | |
471 "trust_update": self.trust_update.to_dict() | |
472 } | |
473 return data | |
474 | |
475 @staticmethod | |
476 def from_dict(data: dict[str, Any]) -> "TrustMessageCacheEntry": | |
477 """Load a serialized dictionary""" | |
478 data["sender_jid"] = jid.JID(data["sender_jid"]) | |
479 data["sender_key"] = bytes.fromhex(data["sender_key"]) | |
480 data["timestamp"] = datetime.fromisoformat(data["timestamp"]) | |
481 data["trust_update"] = TrustUpdate.from_dict(data["trust_update"]) | |
482 return TrustMessageCacheEntry(**data) | |
448 | 483 |
449 | 484 |
450 class PartialTrustMessage(NamedTuple): | 485 class PartialTrustMessage(NamedTuple): |
451 # pylint: disable=invalid-name | 486 # pylint: disable=invalid-name |
452 """ | 487 """ |
476 "XEP-0384/TM", | 511 "XEP-0384/TM", |
477 client.profile | 512 client.profile |
478 ) | 513 ) |
479 | 514 |
480 # Load cache entries | 515 # Load cache entries |
481 cache_entries = cast( | 516 cache_entries = { |
482 Set[TrustMessageCacheEntry], | 517 TrustMessageCacheEntry.from_dict(d) |
483 await trust_message_cache.get("cache", set()) | 518 for d in await trust_message_cache.get("cache", []) |
484 ) | 519 } |
485 | 520 |
486 # Expire cache entries that were overwritten by the applied trust updates | 521 # Expire cache entries that were overwritten by the applied trust updates |
487 cache_entries_by_target = { | 522 cache_entries_by_target = { |
488 ( | 523 ( |
489 cache_entry.trust_update.target_jid.userhostJID(), | 524 cache_entry.trust_update.target_jid.userhostJID(), |
532 | 567 |
533 # Remove the corresponding cache entry | 568 # Remove the corresponding cache entry |
534 cache_entries.remove(cache_entry) | 569 cache_entries.remove(cache_entry) |
535 | 570 |
536 # Store the updated cache entries | 571 # Store the updated cache entries |
537 await trust_message_cache.force("cache", cache_entries) | 572 await trust_message_cache.force( |
573 "cache", | |
574 [tm.to_dict() for tm in cache_entries] | |
575 ) | |
538 | 576 |
539 # TODO: Notify the user ("feedback") about automatically updated trust? | 577 # TODO: Notify the user ("feedback") about automatically updated trust? |
540 | 578 |
541 if len(new_trust_updates) > 0: | 579 if len(new_trust_updates) > 0: |
542 # If any trust has been updated, recursively perform another run of cache | 580 # If any trust has been updated, recursively perform another run of cache |
1538 to_policy=SCEAffixPolicy.OPTIONAL, | 1576 to_policy=SCEAffixPolicy.OPTIONAL, |
1539 from_policy=SCEAffixPolicy.OPTIONAL, | 1577 from_policy=SCEAffixPolicy.OPTIONAL, |
1540 custom_policies={} | 1578 custom_policies={} |
1541 ) | 1579 ) |
1542 | 1580 |
1543 def __init__(self, sat: LiberviaBackend) -> None: | 1581 def __init__(self, host: LiberviaBackend) -> None: |
1544 """ | 1582 """ |
1545 @param sat: The SAT instance. | 1583 @param sat: The SAT instance. |
1546 """ | 1584 """ |
1547 | 1585 |
1548 self.__sat = sat | 1586 self.host = host |
1549 | 1587 |
1550 # Add configuration option to choose between manual trust and BTBV as the trust | 1588 # Add configuration option to choose between manual trust and BTBV as the trust |
1551 # model | 1589 # model |
1552 sat.memory.update_params(DEFAULT_TRUST_MODEL_PARAM) | 1590 host.memory.update_params(DEFAULT_TRUST_MODEL_PARAM) |
1553 | 1591 |
1554 # Plugins | 1592 # Plugins |
1555 self.__xep_0045 = cast(Optional[XEP_0045], sat.plugins.get("XEP-0045")) | 1593 self._j = cast(XEP_0060, host.plugins["XEP-0060"]) |
1556 self.__xep_0334 = cast(XEP_0334, sat.plugins["XEP-0334"]) | 1594 self.__xep_0045 = cast(Optional[XEP_0045], host.plugins.get("XEP-0045")) |
1557 self.__xep_0359 = cast(Optional[XEP_0359], sat.plugins.get("XEP-0359")) | 1595 self.__xep_0334 = cast(XEP_0334, host.plugins["XEP-0334"]) |
1558 self.__xep_0420 = cast(XEP_0420, sat.plugins["XEP-0420"]) | 1596 self.__xep_0359 = cast(Optional[XEP_0359], host.plugins.get("XEP-0359")) |
1597 self.__xep_0420 = cast(XEP_0420, host.plugins["XEP-0420"]) | |
1559 | 1598 |
1560 # In contrast to one to one messages, MUC messages are reflected to the sender. | 1599 # In contrast to one to one messages, MUC messages are reflected to the sender. |
1561 # Thus, the sender does not add messages to their local message log when sending | 1600 # Thus, the sender does not add messages to their local message log when sending |
1562 # them, but when the reflection is received. This approach does not pair well with | 1601 # them, but when the reflection is received. This approach does not pair well with |
1563 # OMEMO, since for security reasons it is forbidden to encrypt messages for the | 1602 # OMEMO, since for security reasons it is forbidden to encrypt messages for the |
1577 self.__session_manager_waiters: Dict[str, List[defer.Deferred]] = {} | 1616 self.__session_manager_waiters: Dict[str, List[defer.Deferred]] = {} |
1578 | 1617 |
1579 # These triggers are used by oldmemo, which doesn't do SCE and only applies to | 1618 # These triggers are used by oldmemo, which doesn't do SCE and only applies to |
1580 # messages. Temporarily, until a more fitting trigger for SCE-based encryption is | 1619 # messages. Temporarily, until a more fitting trigger for SCE-based encryption is |
1581 # added, the message_received trigger is also used for twomemo. | 1620 # added, the message_received trigger is also used for twomemo. |
1582 sat.trigger.add( | 1621 host.trigger.add( |
1583 "message_received", | 1622 "message_received", |
1584 self._message_received_trigger, | 1623 self._message_received_trigger, |
1585 priority=100050 | 1624 priority=100050 |
1586 ) | 1625 ) |
1587 | 1626 |
1588 sat.trigger.add("send", self.__send_trigger, priority=0) | 1627 host.trigger.add("send", self.__send_trigger, priority=0) |
1589 # TODO: Add new triggers here for freshly received and about-to-be-sent stanzas, | 1628 # TODO: Add new triggers here for freshly received and about-to-be-sent stanzas, |
1590 # including IQs. | 1629 # including IQs. |
1591 | 1630 |
1592 # Give twomemo a (slightly) higher priority than oldmemo | 1631 # Give twomemo a (slightly) higher priority than oldmemo |
1593 sat.register_encryption_plugin(self, "OMEMO", twomemo.twomemo.NAMESPACE, 101) | 1632 host.register_encryption_plugin(self, "OMEMO", twomemo.twomemo.NAMESPACE, 101) |
1594 sat.register_encryption_plugin( | 1633 host.register_encryption_plugin( |
1595 self, "OMEMO_legacy", oldmemo.oldmemo.NAMESPACE, 100 | 1634 self, "OMEMO_legacy", oldmemo.oldmemo.NAMESPACE, 100 |
1596 ) | 1635 ) |
1597 | 1636 |
1598 xep_0163 = cast(XEP_0163, sat.plugins["XEP-0163"]) | 1637 xep_0163 = cast(XEP_0163, host.plugins["XEP-0163"]) |
1599 xep_0163.add_pep_event( | 1638 xep_0163.add_pep_event( |
1600 "TWOMEMO_DEVICES", | 1639 "TWOMEMO_DEVICES", |
1601 TWOMEMO_DEVICE_LIST_NODE, | 1640 TWOMEMO_DEVICE_LIST_NODE, |
1602 lambda items_event, profile: defer.ensureDeferred( | 1641 lambda items_event, profile: defer.ensureDeferred( |
1603 self.__on_device_list_update(items_event, profile) | 1642 self.__on_device_list_update(items_event, profile) |
1610 self.__on_device_list_update(items_event, profile) | 1649 self.__on_device_list_update(items_event, profile) |
1611 ) | 1650 ) |
1612 ) | 1651 ) |
1613 | 1652 |
1614 try: | 1653 try: |
1615 self.__text_commands = cast(TextCommands, sat.plugins[C.TEXT_CMDS]) | 1654 self.__text_commands = cast(TextCommands, host.plugins[C.TEXT_CMDS]) |
1616 except KeyError: | 1655 except KeyError: |
1617 log.info(_("Text commands not available")) | 1656 log.info(_("Text commands not available")) |
1618 else: | 1657 else: |
1619 self.__text_commands.register_text_commands(self) | 1658 self.__text_commands.register_text_commands(self) |
1620 | 1659 |
1757 target_key=device.identity_key, | 1796 target_key=device.identity_key, |
1758 target_trust=target_trust | 1797 target_trust=target_trust |
1759 )) | 1798 )) |
1760 | 1799 |
1761 # Check whether ATM is enabled and handle everything in case it is | 1800 # Check whether ATM is enabled and handle everything in case it is |
1762 trust_system = cast(str, self.__sat.memory.param_get_a( | 1801 trust_system = cast(str, self.host.memory.param_get_a( |
1763 PARAM_NAME, | 1802 PARAM_NAME, |
1764 PARAM_CATEGORY, | 1803 PARAM_CATEGORY, |
1765 profile_key=profile | 1804 profile_key=profile |
1766 )) | 1805 )) |
1767 | 1806 |
1779 frozenset(trust_updates) | 1818 frozenset(trust_updates) |
1780 ) | 1819 ) |
1781 | 1820 |
1782 return {} | 1821 return {} |
1783 | 1822 |
1784 submit_id = self.__sat.register_callback(callback, with_data=True, one_shot=True) | 1823 submit_id = self.host.register_callback(callback, with_data=True, one_shot=True) |
1785 | 1824 |
1786 result = xml_tools.XMLUI( | 1825 result = xml_tools.XMLUI( |
1787 panel_type=C.XMLUI_FORM, | 1826 panel_type=C.XMLUI_FORM, |
1788 title=D_("OMEMO trust management"), | 1827 title=D_("OMEMO trust management"), |
1789 submit_id=submit_id | 1828 submit_id=submit_id |
1919 self.__session_manager_waiters[profile] = [] | 1958 self.__session_manager_waiters[profile] = [] |
1920 | 1959 |
1921 # Build and store the session manager | 1960 # Build and store the session manager |
1922 try: | 1961 try: |
1923 session_manager = await prepare_for_profile( | 1962 session_manager = await prepare_for_profile( |
1924 self.__sat, | 1963 self.host, |
1925 profile, | 1964 profile, |
1926 initial_own_label="Libervia" | 1965 initial_own_label="Libervia" |
1927 ) | 1966 ) |
1928 except Exception as e: | 1967 except Exception as e: |
1929 # In case of an error during initalization, notify the waiters accordingly | 1968 # In case of an error during initalization, notify the waiters accordingly |
2019 target_trust=False | 2058 target_trust=False |
2020 ) | 2059 ) |
2021 )) | 2060 )) |
2022 | 2061 |
2023 # Load existing cache entries | 2062 # Load existing cache entries |
2024 existing_cache_entries = cast( | 2063 existing_cache_entries = { |
2025 Set[TrustMessageCacheEntry], | 2064 TrustMessageCacheEntry.from_dict(d) |
2026 await trust_message_cache.get("cache", set()) | 2065 for d in await trust_message_cache.get("cache", []) |
2027 ) | 2066 } |
2028 | 2067 |
2029 # Discard cache entries by timestamp comparison | 2068 # Discard cache entries by timestamp comparison |
2030 existing_by_target = { | 2069 existing_by_target = { |
2031 ( | 2070 ( |
2032 cache_entry.trust_update.target_jid.userhostJID(), | 2071 cache_entry.trust_update.target_jid.userhostJID(), |
2081 new_cache_entries.remove(cache_entry) | 2120 new_cache_entries.remove(cache_entry) |
2082 | 2121 |
2083 # Store the remaining existing and new cache entries | 2122 # Store the remaining existing and new cache entries |
2084 await trust_message_cache.force( | 2123 await trust_message_cache.force( |
2085 "cache", | 2124 "cache", |
2086 existing_cache_entries | new_cache_entries | 2125 [tm.to_dict() for tm in existing_cache_entries | new_cache_entries] |
2087 ) | 2126 ) |
2088 | 2127 |
2089 # If the trust of at least one device was modified, run the ATM cache update logic | 2128 # If the trust of at least one device was modified, run the ATM cache update logic |
2090 if len(applied_trust_updates) > 0: | 2129 if len(applied_trust_updates) > 0: |
2091 await manage_trust_message_cache( | 2130 await manage_trust_message_cache( |