Example #1
0
 def test_visibility_is_not_added_to_public_content(self):
     entity = entities.PostFactory(public=True)
     entity._receivers = [
         UserType(id=self.receiving_profile.fid,
                  receiver_variant=ReceiverVariant.ACTOR)
     ]
     process_entity_post(entity, ProfileFactory())
     content = Content.objects.get(fid=entity.id)
     self.assertEqual(content.limited_visibilities.count(), 0)
Example #2
0
 def test_message_to_objects_receivers_are_saved__single_receiver(self):
     # noinspection PyTypeChecker
     entities = message_to_objects(DIASPORA_POST_SIMPLE,
                                   "*****@*****.**",
                                   user=Mock(id="*****@*****.**"))
     entity = entities[0]
     assert entity._receivers == [
         UserType(id="*****@*****.**",
                  receiver_variant=ReceiverVariant.ACTOR)
     ]
Example #3
0
    def test_message_to_objects_receivers_are_saved(self):
        # noinspection PyTypeChecker
        entities = message_to_objects(
            ACTIVITYPUB_POST,
            "https://diaspodon.fr/users/jaywink",
        )
        entity = entities[0]

        assert set(entity._receivers) == {
            UserType(
                id='https://diaspodon.fr/users/jaywink',
                receiver_variant=ReceiverVariant.FOLLOWERS,
            ),
            UserType(
                id=
                'https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/',
                receiver_variant=ReceiverVariant.ACTOR,
            )
        }
Example #4
0
 def test_visibility__added_to_receiving_profile(self):
     self.comment._receivers = [
         UserType(id=self.receiving_profile.fid,
                  receiver_variant=ReceiverVariant.ACTOR)
     ]
     process_entity_comment(self.comment, ProfileFactory())
     content = Content.objects.get(fid=self.comment.id, parent=self.content)
     self.assertEqual(
         set(content.limited_visibilities.all()),
         {self.receiving_profile},
     )
Example #5
0
 def test_build_send_uses_outbound_doc(self, mock_me):
     protocol = self.init_protocol()
     outbound_doc = etree.fromstring("<xml>foo</xml>")
     entity = Mock(outbound_doc=outbound_doc)
     from_user = UserType(
         id="*****@*****.**", private_key=get_dummy_private_key(), handle="*****@*****.**",
     )
     protocol.build_send(entity, from_user)
     mock_me.assert_called_once_with(
         b"<xml>foo</xml>", private_key=from_user.private_key, author_handle="*****@*****.**",
     )
Example #6
0
 def test_visibility_is_added_to_receiving_profile(self):
     entity = entities.PostFactory()
     entity._receivers = [
         UserType(id=self.receiving_profile.fid,
                  receiver_variant=ReceiverVariant.ACTOR)
     ]
     process_entity_post(entity, ProfileFactory())
     content = Content.objects.get(fid=entity.id)
     self.assertEqual(
         set(content.limited_visibilities.all()),
         {self.receiving_profile},
     )
Example #7
0
 def test_visibility__added_to_receiving_followers(self):
     self.comment.actor_id = self.profile.fid
     self.comment._receivers = [
         UserType(id=self.comment.actor_id,
                  receiver_variant=ReceiverVariant.FOLLOWERS)
     ]
     process_entity_comment(self.comment, self.profile)
     content = Content.objects.get(fid=self.comment.id)
     self.assertEqual(
         set(content.limited_visibilities.all()),
         {self.local_user.profile, self.local_user2.profile},
     )
Example #8
0
    def post_receive(self) -> None:
        """
        Post receive hook - send back follow ack.
        """
        super().post_receive()
        if not self.following:
            return

        from federation.utils.activitypub import retrieve_and_parse_profile  # Circulars
        try:
            from federation.utils.django import get_function_from_config
        except ImportError:
            logger.warning(
                "ActivitypubFollow.post_receive - Unable to send automatic Accept back, only supported on "
                "Django currently")
            return
        get_private_key_function = get_function_from_config(
            "get_private_key_function")
        key = get_private_key_function(self.target_id)
        if not key:
            logger.warning(
                "ActivitypubFollow.post_receive - Failed to send automatic Accept back: could not find "
                "profile to sign it with")
            return
        accept = ActivitypubAccept(
            activity_id=f"{self.target_id}#accept-{uuid.uuid4()}",
            actor_id=self.target_id,
            target_id=self.activity_id,
            object=self.to_as2(),
        )
        try:
            profile = retrieve_and_parse_profile(self.actor_id)
        except Exception:
            profile = None
        if not profile:
            logger.warning(
                "ActivitypubFollow.post_receive - Failed to fetch remote profile for sending back Accept"
            )
            return
        try:
            handle_send(
                accept,
                UserType(id=self.target_id, private_key=key),
                recipients=[{
                    "endpoint": profile.inboxes["private"],
                    "fid": self.actor_id,
                    "protocol": "activitypub",
                    "public": False,
                }],
            )
        except Exception:
            logger.exception(
                "ActivitypubFollow.post_receive - Failed to send Accept back")
Example #9
0
 def test_visibility_is_added_to_receiving_followers(self):
     entity = entities.PostFactory(actor_id=self.profile.fid)
     entity._receivers = [
         UserType(id=entity.actor_id,
                  receiver_variant=ReceiverVariant.FOLLOWERS)
     ]
     process_entity_post(entity, self.profile)
     content = Content.objects.get(fid=entity.id)
     self.assertEqual(
         set(content.limited_visibilities.all()),
         {self.local_user.profile, self.local_user2.profile},
     )
Example #10
0
 def test_message_to_objects_receivers_are_saved__followers_receiver(self):
     # noinspection PyTypeChecker
     entities = message_to_objects(
         DIASPORA_POST_SIMPLE,
         "*****@*****.**",
     )
     entity = entities[0]
     assert entity._receivers == [
         UserType(
             id="*****@*****.**",
             receiver_variant=ReceiverVariant.FOLLOWERS,
         )
     ]
Example #11
0
def extract_receiver(payload: Dict, receiver: str) -> Optional[UserType]:
    """
    Transform a single receiver ID to a UserType.
    """
    actor = payload.get("actor") or payload.get("attributedTo") or ""
    if receiver == NAMESPACE_PUBLIC:
        # Ignore since we already store "public" as a boolean on the entity
        return
    # Check for this being a list reference to followers of an actor?
    # TODO: terrible hack! the way some platforms deliver to sharedInbox using just
    #   the followers collection as a target is annoying to us since we would have to
    #   store the followers collection references on application side, which we don't
    #   want to do since it would make application development another step more complex.
    #   So for now we're going to do a terrible assumption that
    #     1) if "followers" in ID and
    #     2) if ID starts with actor ID
    #     then; assume this is the followers collection of said actor ID.
    #   When we have a caching system, just fetch each receiver and check what it is.
    #   Without caching this would be too expensive to do.
    elif receiver.find("followers") > -1 and receiver.startswith(actor):
        return UserType(id=actor, receiver_variant=ReceiverVariant.FOLLOWERS)
    # Assume actor ID
    return UserType(id=receiver, receiver_variant=ReceiverVariant.ACTOR)
Example #12
0
 def test_visibility_is_not_added_if_public_parent_content(self):
     comment = base.Comment(
         id="https://example.com/comment2",
         target_id=self.public_content.fid,
         raw_content="foobar",
         actor_id="https://example.com/profile2",
     )
     comment._receivers = [
         UserType(id=self.receiving_profile.fid,
                  receiver_variant=ReceiverVariant.ACTOR)
     ]
     process_entity_comment(comment, ProfileFactory())
     content = Content.objects.get(fid=comment.id,
                                   parent=self.public_content)
     self.assertEqual(content.limited_visibilities.count(), 0)
Example #13
0
 def test_build_send_does_right_calls(self, mock_me):
     mock_render = Mock(return_value="rendered")
     mock_me_instance = Mock(render=mock_render)
     mock_me.return_value = mock_me_instance
     protocol = Protocol()
     entity = DiasporaPost()
     private_key = get_dummy_private_key()
     outbound_entity = get_outbound_entity(entity, private_key)
     data = protocol.build_send(outbound_entity, from_user=UserType(
         private_key=private_key, id="johnny@localhost",
         handle="johnny@localhost",
     ))
     mock_me.assert_called_once_with(
         etree.tostring(entity.to_xml()), private_key=private_key, author_handle="johnny@localhost",
     )
     mock_render.assert_called_once_with()
     assert data == "rendered"
Example #14
0
 def test_visibility__added_if_public_parent_content_if_visibility_on_comment__non_public_comment(
         self):
     comment = ActivitypubComment(
         id="https://example.com/comment2",
         target_id=self.public_content.fid,
         raw_content="foobar",
         actor_id="https://example.com/profile2",
         public=False,
     )
     comment._receivers = [
         UserType(id=self.receiving_profile.fid,
                  receiver_variant=ReceiverVariant.ACTOR)
     ]
     process_entity_comment(comment, ProfileFactory())
     content = Content.objects.get(fid=comment.id,
                                   parent=self.public_content)
     self.assertEqual(
         set(content.limited_visibilities.all()),
         {self.receiving_profile},
     )
Example #15
0
    def test_survives_sending_share_if_diaspora_payload_cannot_be_created(
            self, mock_send, share):
        key = get_dummy_private_key()
        share.target_handle = None  # Ensure diaspora payload fails
        recipients = [{
            "endpoint": "https://example.com/receive/public",
            "public": True,
            "protocol": "diaspora",
            "fid": "",
        }, {
            "endpoint": "https://example.tld/receive/public",
            "public": True,
            "protocol": "diaspora",
            "fid": "",
        }, {
            "endpoint": "https://example.net/inbox",
            "fid": "https://example.net/foobar",
            "public": True,
            "protocol": "activitypub",
        }]
        author = UserType(
            private_key=key,
            id="*****@*****.**",
            handle="*****@*****.**",
        )
        handle_send(share, author, recipients)

        # Ensure first call is a public activitypub payload
        args, kwargs = mock_send.call_args_list[0]
        assert args[0] == "https://example.net/inbox"
        assert kwargs['headers'] == {
            'Content-Type':
            'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
        }
        assert encode_if_text(
            "https://www.w3.org/ns/activitystreams#Public") in args[1]

        # Should only be one call
        assert mock_send.call_count == 1
Example #16
0
    def test_calls_handle_create_payload(self, mock_send, profile):
        key = get_dummy_private_key()
        recipients = [
            {
                "endpoint": "https://127.0.0.1/receive/users/1234",
                "public_key": key.publickey(),
                "public": False,
                "protocol": "diaspora",
                "fid": "",
            },
            {
                "endpoint": "https://example.com/receive/public",
                "public": True,
                "protocol": "diaspora",
                "fid": "",
            },
            {
                "endpoint": "https://example.net/receive/public",
                "public": True,
                "protocol": "diaspora",
                "fid": "",
            },
            # Same twice to ensure one delivery only per unique
            {
                "endpoint": "https://example.net/receive/public",
                "public": True,
                "protocol": "diaspora",
                "fid": "",
            },
            {
                "endpoint": "https://example.net/foobar/inbox",
                "fid": "https://example.net/foobar",
                "public": False,
                "protocol": "activitypub",
            },
            {
                "endpoint": "https://example.net/inbox",
                "fid": "https://example.net/foobar",
                "public": True,
                "protocol": "activitypub",
            }
        ]
        author = UserType(
            private_key=key,
            id="*****@*****.**",
            handle="*****@*****.**",
        )
        handle_send(profile, author, recipients)

        # Ensure first call is a private diaspora payload
        args, kwargs = mock_send.call_args_list[0]
        assert args[0] == "https://127.0.0.1/receive/users/1234"
        assert "aes_key" in args[1]
        assert "encrypted_magic_envelope" in args[1]
        assert kwargs['headers'] == {'Content-Type': 'application/json'}

        # Ensure second call is a private activitypub payload
        args, kwargs = mock_send.call_args_list[1]
        assert args[0] == "https://example.net/foobar/inbox"
        assert kwargs['headers'] == {
            'Content-Type':
            'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
        }
        assert encode_if_text(
            "https://www.w3.org/ns/activitystreams#Public") not in args[1]

        # Ensure third call is a public activitypub payload
        args, kwargs = mock_send.call_args_list[2]
        assert args[0] == "https://example.net/inbox"
        assert kwargs['headers'] == {
            'Content-Type':
            'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
        }
        assert encode_if_text(
            "https://www.w3.org/ns/activitystreams#Public") in args[1]

        # Ensure diaspora public payloads and recipients, one per unique host
        args3, kwargs3 = mock_send.call_args_list[3]
        args4, kwargs4 = mock_send.call_args_list[4]
        public_endpoints = {args3[0], args4[0]}
        assert public_endpoints == {
            "https://example.net/receive/public",
            "https://example.com/receive/public",
        }
        assert args3[1].startswith("<me:env xmlns:me=")
        assert args4[1].startswith("<me:env xmlns:me=")
        assert kwargs3['headers'] == {
            'Content-Type': 'application/magic-envelope+xml'
        }
        assert kwargs4['headers'] == {
            'Content-Type': 'application/magic-envelope+xml'
        }

        with pytest.raises(IndexError):
            # noinspection PyStatementEffect
            mock_send.call_args_list[5]
Example #17
0
 def federable(self):
     return UserType(
         id=self.fid or self.handle,
         private_key=self.rsa_private_key,
         handle=self.handle,
     )
Example #18
0
def usertype():
    return UserType(
        id="https://localhost/profile",
        private_key=get_dummy_private_key(),
    )
Example #19
0
def element_to_objects(
        element: etree.ElementTree, sender: str, sender_key_fetcher: Callable[[str], str] = None, user: UserType = None,
) -> List:
    """Transform an Element to a list of entities recursively.

    Possible child entities are added to each entity ``_children`` list.

    Optional parameter ``sender_key_fetcher`` can be a function to fetch sender public key.
    If not given, key will always be fetched over the network. The function should take sender as the only parameter.
    """
    entities = []
    cls = MAPPINGS.get(element.tag)
    if not cls:
        return []

    attrs = xml_children_as_dict(element)
    transformed = transform_attributes(attrs, cls)
    if hasattr(cls, "fill_extra_attributes"):
        transformed = cls.fill_extra_attributes(transformed)
    entity = cls(**transformed)
    # Add protocol name
    entity._source_protocol = "diaspora"
    # Save element object to entity for possible later use
    entity._source_object = etree.tostring(element)

    # Save receivers on the entity
    if user:
        # Single receiver
        entity._receivers = [UserType(id=user.id, receiver_variant=ReceiverVariant.ACTOR)]
    else:
        # Followers
        entity._receivers = [UserType(id=sender, receiver_variant=ReceiverVariant.FOLLOWERS)]

    if issubclass(cls, DiasporaRelayableMixin):
        # If relayable, fetch sender key for validation
        entity._xml_tags = get_element_child_info(element, "tag")
        if sender_key_fetcher:
            entity._sender_key = sender_key_fetcher(entity.actor_id)
        else:
            profile = retrieve_and_parse_profile(entity.handle)
            if profile:
                entity._sender_key = profile.public_key
    else:
        # If not relayable, ensure handles match
        if not check_sender_and_entity_handle_match(sender, entity.handle):
            return []
    try:
        entity.validate()
    except ValueError as ex:
        logger.error("Failed to validate entity %s: %s", entity, ex, extra={
            "attrs": attrs,
            "transformed": transformed,
        })
        return []

    # Extract mentions
    if hasattr(entity, "extract_mentions"):
        entity.extract_mentions()

    # Do child elements
    for child in element:
        # noinspection PyProtectedMember
        entity._children.extend(element_to_objects(child, sender, user=user))
    # Add to entities list
    entities.append(entity)
    return entities
Example #20
0
 def test_get_json_payload_magic_envelope(self, mock_decrypt):
     protocol = Protocol()
     protocol.user = UserType(id="foobar", private_key=get_dummy_private_key())
     protocol.get_json_payload_magic_envelope("payload")
     mock_decrypt.assert_called_once_with(payload="payload", private_key=get_dummy_private_key())