From f5db2ab83500d2b1409f978ea2679f7fff71d9ac Mon Sep 17 00:00:00 2001 From: Jason Robinson Date: Sat, 29 Jun 2019 01:21:33 +0300 Subject: [PATCH] Add support for outgoing unfollow in ActivityPub Sent as Unfo{Follow}. Also don't send an accept to an Undo{Follow}.. --- federation/entities/activitypub/entities.py | 31 ++++++++++++++----- .../entities/activitypub/test_entities.py | 26 ++++++++++++++++ federation/tests/fixtures/entities.py | 10 ++++++ 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/federation/entities/activitypub/entities.py b/federation/entities/activitypub/entities.py index 2c1a6e3..d36a797 100644 --- a/federation/entities/activitypub/entities.py +++ b/federation/entities/activitypub/entities.py @@ -36,6 +36,9 @@ class ActivitypubFollow(ActivitypubObjectMixin, Follow): """ Post receive hook - send back follow ack. """ + 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 @@ -76,13 +79,27 @@ class ActivitypubFollow(ActivitypubObjectMixin, Follow): logger.exception("ActivitypubFollow.post_receive - Failed to send Accept back") def to_as2(self) -> Dict: - as2 = { - "@context": CONTEXTS_DEFAULT, - "id": self.activity_id, - "type": self._type, - "actor": self.actor_id, - "object": self.target_id, - } + if self.following: + as2 = { + "@context": CONTEXTS_DEFAULT, + "id": self.activity_id, + "type": self._type, + "actor": self.actor_id, + "object": self.target_id, + } + else: + as2 = { + "@context": CONTEXTS_DEFAULT, + "id": self.activity_id, + "type": ActivityType.UNDO.value, + "actor": self.actor_id, + "object": { + "id": f"{self.actor_id}#follow-{uuid.uuid4()}", + "type": self._type, + "actor": self.actor_id, + "object": self.target_id, + }, + } return as2 diff --git a/federation/tests/entities/activitypub/test_entities.py b/federation/tests/entities/activitypub/test_entities.py index 68f50d7..23bef3f 100644 --- a/federation/tests/entities/activitypub/test_entities.py +++ b/federation/tests/entities/activitypub/test_entities.py @@ -20,6 +20,32 @@ class TestEntitiesConvertToAS2: "object": "https://example.com/follow/1234", } + def test_follow_to_as2(self, activitypubfollow): + result = activitypubfollow.to_as2() + assert result == { + "@context": CONTEXTS_DEFAULT, + "id": "https://localhost/follow", + "type": "Follow", + "actor": "https://localhost/profile", + "object": "https://example.com/profile" + } + + def test_follow_to_as2__undo(self, activitypubundofollow): + result = activitypubundofollow.to_as2() + result["object"]["id"] = "https://localhost/follow" # Real object will have a random UUID postfix here + assert result == { + "@context": CONTEXTS_DEFAULT, + "id": "https://localhost/undo", + "type": "Undo", + "actor": "https://localhost/profile", + "object": { + "id": "https://localhost/follow", + "type": "Follow", + "actor": "https://localhost/profile", + "object": "https://example.com/profile", + } + } + def test_post_to_as2(self, activitypubpost): result = activitypubpost.to_as2() assert result == { diff --git a/federation/tests/fixtures/entities.py b/federation/tests/fixtures/entities.py index 03df259..019a69b 100644 --- a/federation/tests/fixtures/entities.py +++ b/federation/tests/fixtures/entities.py @@ -59,6 +59,16 @@ def activitypubprofile(): ) +@pytest.fixture +def activitypubundofollow(): + return ActivitypubFollow( + activity_id="https://localhost/undo", + actor_id="https://localhost/profile", + target_id="https://example.com/profile", + following=False, + ) + + @pytest.fixture def profile(): return Profile(