diff libervia/backend/memory/sqla_mapping.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 2074b2bbe616
children
line wrap: on
line diff
--- a/libervia/backend/memory/sqla_mapping.py	Fri Feb 16 18:46:06 2024 +0100
+++ b/libervia/backend/memory/sqla_mapping.py	Fri Feb 23 13:31:04 2024 +0100
@@ -19,7 +19,6 @@
 from datetime import datetime
 import enum
 import json
-import pickle
 import time
 from typing import Any, Dict
 
@@ -132,42 +131,6 @@
     URGENT = 40
 
 
-class LegacyPickle(TypeDecorator):
-    """Handle troubles with data pickled by former version of SàT
-
-    This type is temporary until we do migration to a proper data type
-    """
-
-    # Blob is used on SQLite but gives errors when used here, while Text works fine
-    impl = Text
-    cache_ok = True
-
-    def process_bind_param(self, value, dialect):
-        if value is None:
-            return None
-        return pickle.dumps(value, 0)
-
-    def process_result_value(self, value, dialect):
-        if value is None:
-            return None
-        # value types are inconsistent (probably a consequence of Python 2/3 port
-        # and/or SQLite dynamic typing)
-        try:
-            value = value.encode()
-        except AttributeError:
-            pass
-        # "utf-8" encoding is needed to handle Python 2 pickled data
-        try:
-            return pickle.loads(value, encoding="utf-8")
-        except ModuleNotFoundError:
-            # FIXME: workaround due to package renaming, need to move all pickle code to
-            #   JSON
-            return pickle.loads(
-                value.replace(b"sat.plugins", b"libervia.backend.plugins"),
-                encoding="utf-8",
-            )
-
-
 class Json(TypeDecorator):
     """Handle JSON field in DB independant way"""
 
@@ -178,7 +141,7 @@
     def process_bind_param(self, value, dialect):
         if value is None:
             return None
-        return json.dumps(value)
+        return json.dumps(value, ensure_ascii=False)
 
     def process_result_value(self, value, dialect):
         if value is None:
@@ -296,7 +259,7 @@
         ),
         nullable=False,
     )
-    extra = Column(LegacyPickle)
+    extra = Column(JSON)
 
     profile = relationship("Profile")
     messages = relationship(
@@ -573,7 +536,7 @@
 
     namespace = Column(Text, primary_key=True)
     key = Column(Text, primary_key=True)
-    value = Column(LegacyPickle)
+    value = Column(JSON)
 
 
 class PrivateIndBin(Base):
@@ -582,7 +545,7 @@
     namespace = Column(Text, primary_key=True)
     key = Column(Text, primary_key=True)
     profile_id = Column(ForeignKey("profiles.id", ondelete="CASCADE"), primary_key=True)
-    value = Column(LegacyPickle)
+    value = Column(JSON)
 
     profile = relationship("Profile", back_populates="private_bin_data")