Skip to content
Snippets Groups Projects
Verified Commit d8f86c4f authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Factorized follow logic between system actors, Library can now accept follows

parent b833a11f
No related branches found
No related tags found
No related merge requests found
......@@ -2,7 +2,9 @@ import logging
import json
import requests
import requests_http_signature
import uuid
from . import models
from . import signing
logger = logging.getLogger(__name__)
......@@ -117,3 +119,20 @@ def get_accept_follow(accept_id, accept_actor, follow, follow_actor):
"object": accept_actor.url
},
}
def accept_follow(target, follow, actor):
accept_uuid = uuid.uuid4()
accept = get_accept_follow(
accept_id=accept_uuid,
accept_actor=target,
follow=follow,
follow_actor=actor)
deliver(
accept,
to=[actor.url],
on_behalf_of=target)
return models.Follow.objects.get_or_create(
actor=actor,
target=target,
)
......@@ -132,6 +132,20 @@ class SystemActor(object):
return handler(data, actor)
def handle_follow(self, ac, sender):
system_actor = self.get_actor_instance()
if self.manually_approves_followers:
fr, created = models.FollowRequest.objects.get_or_create(
actor=sender,
target=system_actor,
approved=None,
)
return fr
return activity.accept_follow(
system_actor, ac, sender
)
class LibraryActor(SystemActor):
id = 'library'
......@@ -140,6 +154,7 @@ class LibraryActor(SystemActor):
additional_attributes = {
'manually_approves_followers': True
}
@property
def manually_approves_followers(self):
return settings.FEDERATION_MUSIC_NEEDS_APPROVAL
......@@ -159,18 +174,18 @@ class TestActor(SystemActor):
def get_outbox(self, data, actor=None):
return {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"id": utils.full_url(
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"id": utils.full_url(
reverse(
'federation:instance-actors-outbox',
kwargs={'actor': self.id})),
"type": "OrderedCollection",
"totalItems": 0,
"orderedItems": []
"type": "OrderedCollection",
"totalItems": 0,
"orderedItems": []
}
def parse_command(self, message):
......@@ -204,10 +219,10 @@ class TestActor(SystemActor):
)
reply_activity = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
'type': 'Create',
'actor': test_actor.url,
'id': '{}/activity'.format(reply_url),
......@@ -240,25 +255,9 @@ class TestActor(SystemActor):
on_behalf_of=test_actor)
def handle_follow(self, ac, sender):
# on a follow we:
# 1. send the accept answer
# 2. follow back
#
super().handle_follow(ac, sender)
# also, we follow back
test_actor = self.get_actor_instance()
accept_uuid = uuid.uuid4()
accept = activity.get_accept_follow(
accept_id=accept_uuid,
accept_actor=test_actor,
follow=ac,
follow_actor=sender)
activity.deliver(
accept,
to=[ac['actor']],
on_behalf_of=test_actor)
models.Follow.objects.get_or_create(
actor=sender,
target=test_actor,
)
follow_uuid = uuid.uuid4()
follow = activity.get_follow(
follow_id=follow_uuid,
......
import uuid
from funkwhale_api.federation import activity
def test_deliver(nodb_factories, r_mock, mocker):
to = nodb_factories['federation.Actor']()
mocker.patch(
......@@ -30,3 +33,42 @@ def test_deliver(nodb_factories, r_mock, mocker):
assert r_mock.call_count == 1
assert request.url == to.inbox_url
assert request.headers['content-type'] == 'application/activity+json'
def test_accept_follow(mocker, factories):
deliver = mocker.patch(
'funkwhale_api.federation.activity.deliver')
actor = factories['federation.Actor']()
target = factories['federation.Actor'](local=True)
follow = {
'actor': actor.url,
'type': 'Follow',
'id': 'http://test.federation/user#follows/267',
'object': target.url,
}
uid = uuid.uuid4()
mocker.patch('uuid.uuid4', return_value=uid)
expected_accept = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"id": target.url + '#accepts/follows/{}'.format(uid),
"type": "Accept",
"actor": target.url,
"object": {
"id": follow['id'],
"type": "Follow",
"actor": actor.url,
"object": target.url
},
}
activity.accept_follow(
target, follow, actor
)
deliver.assert_called_once_with(
expected_accept, to=[actor.url], on_behalf_of=target
)
follow_instance = actor.emitted_follows.first()
assert follow_instance.target == target
......@@ -93,18 +93,18 @@ def test_get_test(settings, preferences):
def test_test_get_outbox():
expected = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"id": utils.full_url(
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"id": utils.full_url(
reverse(
'federation:instance-actors-outbox',
kwargs={'actor': 'test'})),
"type": "OrderedCollection",
"totalItems": 0,
"orderedItems": []
"type": "OrderedCollection",
"totalItems": 0,
"orderedItems": []
}
data = actors.SYSTEM_ACTORS['test'].get_outbox({}, actor=None)
......@@ -248,7 +248,7 @@ def test_system_actor_handle(mocker, nodb_factories):
)
assert serializer.is_valid()
actors.SYSTEM_ACTORS['test'].handle(activity, actor)
handler.assert_called_once_with(serializer.data, actor)
handler.assert_called_once_with(activity, actor)
def test_test_actor_handles_follow(
......@@ -258,6 +258,8 @@ def test_test_actor_handles_follow(
actor = factories['federation.Actor']()
now = timezone.now()
mocker.patch('django.utils.timezone.now', return_value=now)
accept_follow = mocker.patch(
'funkwhale_api.federation.activity.accept_follow')
test_actor = actors.SYSTEM_ACTORS['test'].get_actor_instance()
data = {
'actor': actor.url,
......@@ -267,22 +269,6 @@ def test_test_actor_handles_follow(
}
uid = uuid.uuid4()
mocker.patch('uuid.uuid4', return_value=uid)
expected_accept = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"id": test_actor.url + '#accepts/follows/{}'.format(uid),
"type": "Accept",
"actor": test_actor.url,
"object": {
"id": data['id'],
"type": "Follow",
"actor": actor.url,
"object": test_actor.url
},
}
expected_follow = {
'@context': serializers.AP_CONTEXT,
'actor': test_actor.url,
......@@ -292,12 +278,10 @@ def test_test_actor_handles_follow(
}
actors.SYSTEM_ACTORS['test'].post_inbox(data, actor=actor)
accept_follow.assert_called_once_with(
test_actor, data, actor
)
expected_calls = [
mocker.call(
expected_accept,
to=[actor.url],
on_behalf_of=test_actor,
),
mocker.call(
expected_follow,
to=[actor.url],
......@@ -306,10 +290,6 @@ def test_test_actor_handles_follow(
]
deliver.assert_has_calls(expected_calls)
follow = test_actor.received_follows.first()
assert follow.actor == actor
assert follow.target == test_actor
def test_test_actor_handles_undo_follow(
settings, mocker, factories):
......@@ -344,3 +324,42 @@ def test_test_actor_handles_undo_follow(
on_behalf_of=test_actor,)
assert models.Follow.objects.count() == 0
def test_library_actor_handles_follow_manual_approval(
settings, mocker, factories):
settings.FEDERATION_MUSIC_NEEDS_APPROVAL = True
actor = factories['federation.Actor']()
now = timezone.now()
mocker.patch('django.utils.timezone.now', return_value=now)
library_actor = actors.SYSTEM_ACTORS['library'].get_actor_instance()
data = {
'actor': actor.url,
'type': 'Follow',
'id': 'http://test.federation/user#follows/267',
'object': library_actor.url,
}
library_actor.system_conf.post_inbox(data, actor=actor)
fr = library_actor.received_follow_requests.first()
assert library_actor.received_follow_requests.count() == 1
assert fr.target == library_actor
assert fr.actor == actor
assert fr.approved is None
def test_library_actor_handles_follow_auto_approval(
settings, mocker, factories):
settings.FEDERATION_MUSIC_NEEDS_APPROVAL = True
actor = factories['federation.Actor']()
accept_follow = mocker.patch(
'funkwhale_api.federation.activity.accept_follow')
library_actor = actors.SYSTEM_ACTORS['library'].get_actor_instance()
data = {
'actor': actor.url,
'type': 'Follow',
'id': 'http://test.federation/user#follows/267',
'object': library_actor.url,
}
library_actor.system_conf.post_inbox(data, actor=actor)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment