kopia lustrzana https://gitlab.com/jaywink/federation
Merge branch 'undo-follow' into 'master'
Support ActivityPub unfollow See merge request jaywink/federation!146merge-requests/147/head
commit
1e629dd09c
|
@ -37,6 +37,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
|
||||
|
@ -78,13 +81,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
|
||||
|
||||
|
||||
|
|
|
@ -14,10 +14,11 @@ logger = logging.getLogger("federation")
|
|||
MAPPINGS = {
|
||||
"Accept": ActivitypubAccept,
|
||||
"Article": ActivitypubPost,
|
||||
"Follow": ActivitypubFollow,
|
||||
"Follow": ActivitypubFollow, # Technically not correct, but for now we support only following profiles
|
||||
"Note": ActivitypubPost,
|
||||
"Page": ActivitypubPost,
|
||||
"Person": ActivitypubProfile,
|
||||
"Undo": ActivitypubFollow, # Technically not correct, but for now we support only undoing a follow of a profile
|
||||
}
|
||||
|
||||
|
||||
|
@ -158,8 +159,10 @@ def transform_attribute(key: str, value: Union[str, Dict, int], transformed: Dic
|
|||
transformed["name"] = value
|
||||
elif key == "object":
|
||||
if isinstance(value, dict):
|
||||
if cls in (ActivitypubAccept, ActivitypubFollow):
|
||||
if cls == ActivitypubAccept:
|
||||
transformed["target_id"] = value.get("id")
|
||||
elif cls == ActivitypubFollow:
|
||||
transformed["target_id"] = value.get("object")
|
||||
else:
|
||||
transform_attributes(value, cls, transformed, is_object=True)
|
||||
else:
|
||||
|
@ -173,6 +176,9 @@ def transform_attribute(key: str, value: Union[str, Dict, int], transformed: Dic
|
|||
elif key in ("to", "cc"):
|
||||
if isinstance(value, list) and NAMESPACE_PUBLIC in value:
|
||||
transformed["public"] = True
|
||||
elif key == "type":
|
||||
if value == "Undo":
|
||||
transformed["following"] = False
|
||||
elif key == "url":
|
||||
transformed["url"] = value
|
||||
|
||||
|
|
|
@ -26,6 +26,32 @@ class TestEntitiesConvertToAS2:
|
|||
},
|
||||
}
|
||||
|
||||
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 == {
|
||||
|
|
|
@ -5,7 +5,8 @@ import pytest
|
|||
from federation.entities.activitypub.entities import ActivitypubFollow, ActivitypubAccept, ActivitypubProfile
|
||||
from federation.entities.activitypub.mappers import message_to_objects, get_outbound_entity
|
||||
from federation.entities.base import Accept, Follow, Profile
|
||||
from federation.tests.fixtures.payloads import ACTIVITYPUB_FOLLOW, ACTIVITYPUB_PROFILE, ACTIVITYPUB_PROFILE_INVALID
|
||||
from federation.tests.fixtures.payloads import (
|
||||
ACTIVITYPUB_FOLLOW, ACTIVITYPUB_PROFILE, ACTIVITYPUB_PROFILE_INVALID, ACTIVITYPUB_UNDO_FOLLOW)
|
||||
|
||||
|
||||
class TestActivitypubEntityMappersReceive:
|
||||
|
@ -23,6 +24,15 @@ class TestActivitypubEntityMappersReceive:
|
|||
assert entity.target_id == "https://example.org/actor"
|
||||
assert entity.following is True
|
||||
|
||||
def test_message_to_objects__unfollow(self):
|
||||
entities = message_to_objects(ACTIVITYPUB_UNDO_FOLLOW, "https://example.com/actor")
|
||||
assert len(entities) == 1
|
||||
entity = entities[0]
|
||||
assert isinstance(entity, ActivitypubFollow)
|
||||
assert entity.actor_id == "https://example.com/actor"
|
||||
assert entity.target_id == "https://example.org/actor"
|
||||
assert entity.following is False
|
||||
|
||||
@pytest.mark.skip
|
||||
def test_message_to_objects_mentions_are_extracted(self):
|
||||
entities = message_to_objects(
|
||||
|
|
|
@ -58,6 +58,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(
|
||||
|
|
|
@ -7,12 +7,6 @@ ACTIVITYPUB_FOLLOW = {
|
|||
"type": "Follow",
|
||||
"actor": "https://example.com/actor",
|
||||
"object": "https://example.org/actor",
|
||||
"signature": {
|
||||
"type": "RsaSignature2017",
|
||||
"creator": "https://example.com/actor#main-key",
|
||||
"created": "2018-10-11T15:59:32Z",
|
||||
"signatureValue": "foobar"
|
||||
}
|
||||
}
|
||||
|
||||
ACTIVITYPUB_PROFILE = {
|
||||
|
@ -99,3 +93,19 @@ ACTIVITYPUB_PROFILE_INVALID = {
|
|||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVbaT5wvaZobfIB044ai\nhJg/XooEn2jSTnTY1K4mPmhdqYUmszpdXKp64OwA+f3SBuIUIkLAYUSB9Fu19zh+\nzOsoGI5gvA32DHY1vaqdKnT9gt3jKS5AdQ3bl0t9f4pPkO2I5YtQOWV1FvBcwPXG\nB0dIqj0fTqNK37FmyybrRD6uhjySddklN9gNsULTqYVDa0QSXVswTIW2jQudnNlp\nnEf3SfjlK9J8eKPF3hFK3PNXBTTZ4NydBSL3cVBinU0cFg8lUJOK8RI4qaetrVoQ\neKd7gCTSQ7RZh8kmkYmdlweb+ZtORT6Y5ZsotR8jwhAOFAqCt36B5+LX2UIw68Pk\nOwIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
},
|
||||
}
|
||||
|
||||
ACTIVITYPUB_UNDO_FOLLOW = {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
],
|
||||
"id": "https://example.com/undo",
|
||||
"type": "Undo",
|
||||
"actor": "https://example.com/actor",
|
||||
"object": {
|
||||
"id": "https://example.com/follow",
|
||||
"type": "Follow",
|
||||
"actor": "https://example.com/actor",
|
||||
"object": "https://example.org/actor",
|
||||
},
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue