Mercurial > libervia-backend
diff libervia/backend/plugins/plugin_xep_0373.py @ 4212:5f2d496c633f
core: get rid of `pickle`:
Use of `pickle` to serialise data was a technical legacy that was causing trouble to store
in database, to update (if a class was serialised, a change could break update), and to
security (pickle can lead to code execution).
This patch remove all use of Pickle in favour in JSON, notably:
- for caching data, a Pydantic model is now used instead
- for SQLAlchemy model, the LegacyPickle is replaced by JSON serialisation
- in XEP-0373 a class `PublicKeyMetadata` was serialised. New method `from_dict` and
`to_dict` method have been implemented to do serialisation.
- new methods to (de)serialise data can now be specified with Identity data types. It is
notably used to (de)serialise `path` of avatars.
A migration script has been created to convert data (for upgrade or downgrade), with
special care for XEP-0373 case. Depending of size of database, this migration script can
be long to run.
rel 443
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 23 Feb 2024 13:31:04 +0100 |
parents | 7c5654c54fed |
children | b53b6dc1f929 |
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_xep_0373.py Fri Feb 16 18:46:06 2024 +0100 +++ b/libervia/backend/plugins/plugin_xep_0373.py Fri Feb 23 13:31:04 2024 +0100 @@ -20,12 +20,13 @@ import base64 from datetime import datetime, timezone import enum +import json import secrets import string from typing import Any, Dict, Iterable, List, Literal, Optional, Set, Tuple, cast from xml.sax.saxutils import quoteattr -from typing_extensions import Final, NamedTuple, Never, assert_never +from typing import Final, NamedTuple, Never, assert_never from wokkel import muc, pubsub from wokkel.disco import DiscoFeature, DiscoInfo import xmlschema @@ -812,10 +813,21 @@ """ Metadata about a published public key. """ - fingerprint: str timestamp: datetime + def to_dict(self) -> dict: + # Convert the instance to a dictionary and handle datetime serialization + data = self._asdict() + data['timestamp'] = self.timestamp.isoformat() + return data + + @staticmethod + def from_dict(data: dict) -> 'PublicKeyMetadata': + # Load a serialised dictionary + data['timestamp'] = datetime.fromisoformat(data['timestamp']) + return PublicKeyMetadata(**data) + @enum.unique class TrustLevel(enum.Enum): @@ -1102,10 +1114,10 @@ storage_key = STR_KEY_PUBLIC_KEYS_METADATA.format(sender.userhost()) - local_public_keys_metadata = cast( - Set[PublicKeyMetadata], - await self.__storage[profile].get(storage_key, set()) - ) + local_public_keys_metadata = { + PublicKeyMetadata.from_dict(pkm) + for pkm in await self.__storage[profile].get(storage_key, []) + } unchanged_keys = new_public_keys_metadata & local_public_keys_metadata changed_or_new_keys = new_public_keys_metadata - unchanged_keys @@ -1149,7 +1161,10 @@ await self.publish_public_keys_list(client, new_public_keys_metadata) - await self.__storage[profile].force(storage_key, new_public_keys_metadata) + await self.__storage[profile].force( + storage_key, + [pkm.to_dict() for pkm in new_public_keys_metadata] + ) def list_public_keys(self, client: SatXMPPClient, jid: jid.JID) -> Set[GPGPublicKey]: """List GPG public keys available for a JID. @@ -1191,10 +1206,10 @@ storage_key = STR_KEY_PUBLIC_KEYS_METADATA.format(client.jid.userhost()) - public_keys_list = cast( - Set[PublicKeyMetadata], - await self.__storage[client.profile].get(storage_key, set()) - ) + public_keys_list = { + PublicKeyMetadata.from_dict(pkm) + for pkm in await self.__storage[client.profile].get(storage_key, []) + } public_keys_list.add(PublicKeyMetadata( fingerprint=secret_key.public_key.fingerprint, @@ -1508,10 +1523,10 @@ storage_key = STR_KEY_PUBLIC_KEYS_METADATA.format(entity_jid.userhost()) - public_keys_metadata = cast( - Set[PublicKeyMetadata], - await self.__storage[client.profile].get(storage_key, set()) - ) + public_keys_metadata = { + PublicKeyMetadata.from_dict(pkm) + for pkm in await self.__storage[client.profile].get(storage_key, []) + } if not public_keys_metadata: public_keys_metadata = await self.download_public_keys_list( client, entity_jid @@ -1522,7 +1537,8 @@ ) else: await self.__storage[client.profile].aset( - storage_key, public_keys_metadata + storage_key, + [pkm.to_dict() for pkm in public_keys_metadata] )