diff --git a/CHANGELOG.md b/CHANGELOG.md index 681b259..2aeef6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,10 @@ ### Added * New style Diaspora public payloads are now supported (see [here](https://github.com/diaspora/diaspora_federation/issues/30)). Old style payloads are still supported. Payloads are also still sent out old style. * Add new `Follow` base entity and support for the new Diaspora "contact" payload. The simple `Follow` maps to Diaspora contact entity with following/sharing both true or false. Sharing as a separate concept is not currently supported. +* Added `_receiving_guid` to all entities. This is filled with `user.guid` if `user` is passed to `federation.inbound.handle_receive` and it has a `guid`. Normally in for example Diaspora, this will always be done in private payloads. ### Fixed -* Legacy Diaspora retraction of sharing/following is now supported correctly. The end result is a `DiasporaRetraction` for entity type `Profile`. +* Legacy Diaspora retraction of sharing/following is now supported correctly. The end result is a `DiasporaRetraction` for entity type `Profile`. Since the payload doesn't contain the receiving user for a sharing/following retraction in legacy Diaspora protocol, we store the guid of the user in the entity as `_receiving_guid`, assuming it was passed in for processing. ## [0.11.0] - 2017-05-08 diff --git a/federation/entities/base.py b/federation/entities/base.py index 97a055a..0f066ad 100644 --- a/federation/entities/base.py +++ b/federation/entities/base.py @@ -13,6 +13,8 @@ class BaseEntity(object): _required = [] _children = [] _allowed_children = () + # If we have a receiver for a private payload, store receiving user guid here + _receiving_guid = "" _source_protocol = "" _source_object = None _sender_key = "" diff --git a/federation/entities/diaspora/mappers.py b/federation/entities/diaspora/mappers.py index 22a7cd3..58a0439 100644 --- a/federation/entities/diaspora/mappers.py +++ b/federation/entities/diaspora/mappers.py @@ -52,7 +52,7 @@ def xml_children_as_dict(node): return dict((e.tag, e.text) for e in node) -def element_to_objects(element, sender_key_fetcher=None): +def element_to_objects(element, sender_key_fetcher=None, user=None): """Transform an Element to a list of entities recursively. Possible child entities are added to each entity `_children` list. @@ -60,6 +60,7 @@ def element_to_objects(element, sender_key_fetcher=None): :param tree: Element :param sender_key_fetcher: Function to fetch sender public key. If not given, key will always be fetched over network. The function should take sender handle as the only parameter. + :param user: Optional receiving user object. If given, should have a `handle`. :returns: list of entities """ entities = [] @@ -76,6 +77,9 @@ def element_to_objects(element, sender_key_fetcher=None): entity._source_protocol = "diaspora" # Save element object to entity for possible later use entity._source_object = element + # Save receiving guid to object + if user and hasattr(user, "guid"): + entity._receiving_guid = user.guid # If relayable, fetch sender key for validation if issubclass(cls, DiasporaRelayableMixin): entity._xml_tags = get_element_child_info(element, "tag") @@ -106,24 +110,25 @@ def element_to_objects(element, sender_key_fetcher=None): return entities -def message_to_objects(message, sender_key_fetcher=None): +def message_to_objects(message, sender_key_fetcher=None, user=None): """Takes in a message extracted by a protocol and maps it to entities. :param message: XML payload :type message: str :param sender_key_fetcher: Function to fetch sender public key. If not given, key will always be fetched over network. The function should take sender handle as the only parameter. + :param user: Optional receiving user object. If given, should have a `handle`. :returns: list of entities """ doc = etree.fromstring(message) # Future Diaspora protocol version contains the element at top level if doc.tag in TAGS: - return element_to_objects(doc, sender_key_fetcher) + return element_to_objects(doc, sender_key_fetcher, user) # Legacy Diaspora protocol wraps the element in , so find the right element for tag in TAGS: element = doc.find(".//%s" % tag) if element is not None: - return element_to_objects(element, sender_key_fetcher) + return element_to_objects(element, sender_key_fetcher, user) return [] diff --git a/federation/inbound.py b/federation/inbound.py index 3ce8bc0..8c6edb3 100644 --- a/federation/inbound.py +++ b/federation/inbound.py @@ -23,7 +23,8 @@ def handle_receive(payload, user=None, sender_key_fetcher=None, skip_author_veri could actually be a different identity. :arg payload: Payload blob (str) - :arg user: User that will be passed to `protocol.receive` (required on private encrypted content) + :arg user: User that will be passed to `protocol.receive` (only required on private encrypted content) + MUST have a `private_key` and `guid` if given. :arg sender_key_fetcher: Function that accepts sender handle and returns public key (optional) :arg skip_author_verification: Don't verify sender (test purposes, false default) :returns: Tuple of sender handle, protocol name and list of entity objects @@ -47,7 +48,7 @@ def handle_receive(payload, user=None, sender_key_fetcher=None, skip_author_veri raise NoSuitableProtocolFoundError() mappers = importlib.import_module("federation.entities.%s.mappers" % found_protocol.PROTOCOL_NAME) - entities = mappers.message_to_objects(message, sender_key_fetcher) + entities = mappers.message_to_objects(message, sender_key_fetcher, user) logger.debug("handle_receive: entities %s", entities) return sender, found_protocol.PROTOCOL_NAME, entities diff --git a/federation/tests/entities/diaspora/test_mappers.py b/federation/tests/entities/diaspora/test_mappers.py index 9f623dc..207a9f8 100644 --- a/federation/tests/entities/diaspora/test_mappers.py +++ b/federation/tests/entities/diaspora/test_mappers.py @@ -156,13 +156,14 @@ class TestDiasporaEntityMappersReceive(): assert entity.entity_type == "Post" def test_message_to_objects_retraction_legacy_request(self): - entities = message_to_objects(DIASPORA_LEGACY_REQUEST_RETRACTION) + entities = message_to_objects(DIASPORA_LEGACY_REQUEST_RETRACTION, user=Mock(guid="swfeuihiwehuifhiwheiuf")) assert len(entities) == 1 entity = entities[0] assert isinstance(entity, Retraction) assert entity.handle == "jaywink@iliketoast.net" assert entity.target_guid == "7ed1555bc6ae03db" assert entity.entity_type == "Profile" + assert entity._receiving_guid == "swfeuihiwehuifhiwheiuf" def test_message_to_objects_contact(self): entities = message_to_objects(DIASPORA_CONTACT)