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(