diff --git a/CHANGELOG.md b/CHANGELOG.md index 789ed19..cbd9554 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Additionally, Diaspora entity mappers `message_to_objects` and `element_to_objec * Add source payload object to the entity at `_source_object` when processing it. * Add sender public key to the entity at `_sender_key`, but only if it was used for validating signatures. * Add support for the new Diaspora payload properties coming in the next protocol version. Old XML payloads are and will be still supported. +* `DiasporaComment` and `DiasporaLike` will get the order of elements in the XML payload as a list in `xml_tags`. For implementers who want to recreate payloads for these relayables, this list should be saved for later use. ### Changed * Refactor processing of Diaspora payload XML into entities. Diaspora protocol is dropping the `` wrapper for the payloads. Payloads with the wrapper will still be parsed as before. diff --git a/federation/entities/diaspora/entities.py b/federation/entities/diaspora/entities.py index dfb1694..e81bfa6 100644 --- a/federation/entities/diaspora/entities.py +++ b/federation/entities/diaspora/entities.py @@ -32,6 +32,7 @@ class DiasporaEntityMixin(BaseEntity): class DiasporaRelayableMixin(DiasporaEntityMixin): + _xml_tags = [] parent_signature = "" def __init__(self, *args, **kwargs): diff --git a/federation/entities/diaspora/mappers.py b/federation/entities/diaspora/mappers.py index 66da0d5..57828b3 100644 --- a/federation/entities/diaspora/mappers.py +++ b/federation/entities/diaspora/mappers.py @@ -7,6 +7,7 @@ from federation.entities.base import Image, Relationship, Post, Reaction, Commen from federation.entities.diaspora.entities import ( DiasporaPost, DiasporaComment, DiasporaLike, DiasporaRequest, DiasporaProfile, DiasporaRetraction, DiasporaRelayableMixin) +from federation.protocols.diaspora.signatures import get_element_child_info from federation.utils.diaspora import retrieve_and_parse_profile logger = logging.getLogger("federation") @@ -74,6 +75,7 @@ def element_to_objects(element, sender_key_fetcher=None): entity._source_object = element # If relayable, fetch sender key for validation if issubclass(cls, DiasporaRelayableMixin): + entity._xml_tags = get_element_child_info(element, "tag") if sender_key_fetcher: entity._sender_key = sender_key_fetcher(entity.handle) else: @@ -124,7 +126,7 @@ def message_to_objects(message, sender_key_fetcher=None): def transform_attributes(attrs, cls): """Transform some attribute keys. - + :param attrs: Properties from the XML :type attrs: dict :param cls: Class of the entity diff --git a/federation/protocols/diaspora/signatures.py b/federation/protocols/diaspora/signatures.py index dfc1265..18a0bdb 100644 --- a/federation/protocols/diaspora/signatures.py +++ b/federation/protocols/diaspora/signatures.py @@ -5,11 +5,23 @@ from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 -def _create_signature_hash(doc): +def get_element_child_info(doc, attr): + """Get information from child elements of this elementas a list since order is important. + + Don't include signature tags. + + :param doc: XML element + :param attr: Attribute to get from the elements, for example "tag" or "text". + """ props = [] for child in doc: if child.tag not in ["author_signature", "parent_author_signature"]: - props.append(child.text) + props.append(getattr(child, attr)) + return props + + +def _create_signature_hash(doc): + props = get_element_child_info(doc, "text") content = ";".join(props) return SHA256.new(content.encode("ascii")) diff --git a/federation/tests/entities/diaspora/test_mappers.py b/federation/tests/entities/diaspora/test_mappers.py index 848cfc8..21c8173 100644 --- a/federation/tests/entities/diaspora/test_mappers.py +++ b/federation/tests/entities/diaspora/test_mappers.py @@ -47,7 +47,7 @@ class TestDiasporaEntityMappersReceive(): assert post.raw_content == "((status message))" assert post.guid == "((guidguidguidguidguidguidguid))" assert post.handle == "alice@alice.diaspora.example.org" - assert post.public == False + assert post.public is False assert post.created_at == datetime(2011, 7, 20, 1, 36, 7) assert post.provider_display_name == "Socialhome" @@ -88,6 +88,9 @@ class TestDiasporaEntityMappersReceive(): assert comment.participation == "comment" assert comment.raw_content == "((text))" assert comment.signature == "((signature))" + assert comment._xml_tags == [ + "guid", "parent_guid", "text", "author", + ] mock_validate.assert_called_once_with() @patch("federation.entities.diaspora.mappers.DiasporaLike._validate_signatures") @@ -103,6 +106,9 @@ class TestDiasporaEntityMappersReceive(): assert like.participation == "reaction" assert like.reaction == "like" assert like.signature == "((signature))" + assert like._xml_tags == [ + "parent_type", "guid", "parent_guid", "positive", "author", + ] mock_validate.assert_called_once_with() def test_message_to_objects_request(self):