We are now able to receive a toot and react to it
This commit is contained in:
parent
6fbf8fa44c
commit
3cf1a17087
|
@ -344,7 +344,12 @@ REST_FRAMEWORK = {
|
||||||
),
|
),
|
||||||
'DEFAULT_PAGINATION_CLASS': 'funkwhale_api.common.pagination.FunkwhalePagination',
|
'DEFAULT_PAGINATION_CLASS': 'funkwhale_api.common.pagination.FunkwhalePagination',
|
||||||
'PAGE_SIZE': 25,
|
'PAGE_SIZE': 25,
|
||||||
|
'DEFAULT_PARSER_CLASSES': (
|
||||||
|
'rest_framework.parsers.JSONParser',
|
||||||
|
'rest_framework.parsers.FormParser',
|
||||||
|
'rest_framework.parsers.MultiPartParser',
|
||||||
|
'funkwhale_api.federation.parsers.ActivityParser',
|
||||||
|
),
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
'funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS',
|
'funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS',
|
||||||
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
|
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
|
||||||
|
ACTIVITY_TYPES = [
|
||||||
|
'Accept',
|
||||||
|
'Add',
|
||||||
|
'Announce',
|
||||||
|
'Arrive',
|
||||||
|
'Block',
|
||||||
|
'Create',
|
||||||
|
'Delete',
|
||||||
|
'Dislike',
|
||||||
|
'Flag',
|
||||||
|
'Follow',
|
||||||
|
'Ignore',
|
||||||
|
'Invite',
|
||||||
|
'Join',
|
||||||
|
'Leave',
|
||||||
|
'Like',
|
||||||
|
'Listen',
|
||||||
|
'Move',
|
||||||
|
'Offer',
|
||||||
|
'Question',
|
||||||
|
'Reject',
|
||||||
|
'Read',
|
||||||
|
'Remove',
|
||||||
|
'TentativeReject',
|
||||||
|
'TentativeAccept',
|
||||||
|
'Travel',
|
||||||
|
'Undo',
|
||||||
|
'Update',
|
||||||
|
'View',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
OBJECT_TYPES = [
|
||||||
|
'Article',
|
||||||
|
'Audio',
|
||||||
|
'Document',
|
||||||
|
'Event',
|
||||||
|
'Image',
|
||||||
|
'Note',
|
||||||
|
'Page',
|
||||||
|
'Place',
|
||||||
|
'Profile',
|
||||||
|
'Relationship',
|
||||||
|
'Tombstone',
|
||||||
|
'Video',
|
||||||
|
]
|
||||||
|
|
||||||
|
def deliver(content, on_behalf_of, to=[]):
|
||||||
|
pass
|
|
@ -1,14 +1,23 @@
|
||||||
import requests
|
import requests
|
||||||
|
import xml
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from rest_framework.exceptions import PermissionDenied
|
||||||
|
|
||||||
from dynamic_preferences.registries import global_preferences_registry
|
from dynamic_preferences.registries import global_preferences_registry
|
||||||
|
|
||||||
|
from . import activity
|
||||||
from . import models
|
from . import models
|
||||||
|
from . import serializers
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
|
def remove_tags(text):
|
||||||
|
return ''.join(xml.etree.ElementTree.fromstring(text).itertext())
|
||||||
|
|
||||||
|
|
||||||
def get_actor_data(actor_url):
|
def get_actor_data(actor_url):
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
actor_url,
|
actor_url,
|
||||||
|
@ -23,39 +32,132 @@ def get_actor_data(actor_url):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Invalid actor payload: {}'.format(response.text))
|
'Invalid actor payload: {}'.format(response.text))
|
||||||
|
|
||||||
|
|
||||||
|
class SystemActor(object):
|
||||||
|
additional_attributes = {}
|
||||||
|
|
||||||
|
def get_actor_instance(self):
|
||||||
|
a = models.Actor(
|
||||||
|
**self.get_instance_argument(
|
||||||
|
self.id,
|
||||||
|
name=self.name,
|
||||||
|
summary=self.summary,
|
||||||
|
**self.additional_attributes
|
||||||
|
)
|
||||||
|
)
|
||||||
|
a.pk = self.id
|
||||||
|
return a
|
||||||
|
|
||||||
|
def get_instance_argument(self, id, name, summary, **kwargs):
|
||||||
|
preferences = global_preferences_registry.manager()
|
||||||
|
p = {
|
||||||
|
'preferred_username': id,
|
||||||
|
'domain': settings.FEDERATION_HOSTNAME,
|
||||||
|
'type': 'Person',
|
||||||
|
'name': name.format(host=settings.FEDERATION_HOSTNAME),
|
||||||
|
'manually_approves_followers': True,
|
||||||
|
'url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-detail',
|
||||||
|
kwargs={'actor': id})),
|
||||||
|
'shared_inbox_url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-inbox',
|
||||||
|
kwargs={'actor': id})),
|
||||||
|
'inbox_url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-inbox',
|
||||||
|
kwargs={'actor': id})),
|
||||||
|
'outbox_url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-outbox',
|
||||||
|
kwargs={'actor': id})),
|
||||||
|
'public_key': preferences['federation__public_key'],
|
||||||
|
'summary': summary.format(host=settings.FEDERATION_HOSTNAME)
|
||||||
|
}
|
||||||
|
p.update(kwargs)
|
||||||
|
return p
|
||||||
|
|
||||||
|
def get_inbox(self, data, actor=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def post_inbox(self, data, actor=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_outbox(self, data, actor=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def post_outbox(self, data, actor=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class LibraryActor(SystemActor):
|
||||||
|
id = 'library'
|
||||||
|
name = '{host}\'s library'
|
||||||
|
summary = 'Bot account to federate with {host}\'s library'
|
||||||
|
additional_attributes = {
|
||||||
|
'manually_approves_followers': True
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestActor(SystemActor):
|
||||||
|
id = 'test'
|
||||||
|
name = '{host}\'s test account'
|
||||||
|
summary = (
|
||||||
|
'Bot account to test federation with {host}. '
|
||||||
|
'Send me /ping and I\'ll answer you.'
|
||||||
|
)
|
||||||
|
additional_attributes = {
|
||||||
|
'manually_approves_followers': False
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_outbox(self, data, actor=None):
|
||||||
|
return {
|
||||||
|
"@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": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def post_inbox(self, data, actor=None):
|
||||||
|
if actor is None:
|
||||||
|
raise PermissionDenied('Actor not authenticated')
|
||||||
|
|
||||||
|
serializer = serializers.ActivitySerializer(
|
||||||
|
data=data, context={'actor': actor})
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
ac = serializer.validated_data
|
||||||
|
if ac['type'] == 'Create' and ac['object']['type'] == 'Note':
|
||||||
|
# we received a toot \o/
|
||||||
|
command = self.parse_command(ac['object']['content'])
|
||||||
|
if command == 'ping':
|
||||||
|
activity.deliver(
|
||||||
|
content='Pong!',
|
||||||
|
to=[ac['actor']],
|
||||||
|
on_behalf_of=self.get_actor_instance())
|
||||||
|
|
||||||
|
def parse_command(self, message):
|
||||||
|
"""
|
||||||
|
Remove any links or fancy markup to extract /command from
|
||||||
|
a note message.
|
||||||
|
"""
|
||||||
|
raw = remove_tags(message)
|
||||||
|
try:
|
||||||
|
return raw.split('/')[1]
|
||||||
|
except IndexError:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
SYSTEM_ACTORS = {
|
SYSTEM_ACTORS = {
|
||||||
'library': {
|
'library': LibraryActor(),
|
||||||
'get_actor': lambda: models.Actor(**get_base_system_actor_arguments('library')),
|
'test': TestActor(),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_base_system_actor_arguments(name):
|
|
||||||
preferences = global_preferences_registry.manager()
|
|
||||||
return {
|
|
||||||
'preferred_username': name,
|
|
||||||
'domain': settings.FEDERATION_HOSTNAME,
|
|
||||||
'type': 'Person',
|
|
||||||
'name': '{}\'s library'.format(settings.FEDERATION_HOSTNAME),
|
|
||||||
'manually_approves_followers': True,
|
|
||||||
'url': utils.full_url(
|
|
||||||
reverse(
|
|
||||||
'federation:instance-actors-detail',
|
|
||||||
kwargs={'actor': name})),
|
|
||||||
'shared_inbox_url': utils.full_url(
|
|
||||||
reverse(
|
|
||||||
'federation:instance-actors-inbox',
|
|
||||||
kwargs={'actor': name})),
|
|
||||||
'inbox_url': utils.full_url(
|
|
||||||
reverse(
|
|
||||||
'federation:instance-actors-inbox',
|
|
||||||
kwargs={'actor': name})),
|
|
||||||
'outbox_url': utils.full_url(
|
|
||||||
reverse(
|
|
||||||
'federation:instance-actors-outbox',
|
|
||||||
kwargs={'actor': name})),
|
|
||||||
'public_key': preferences['federation__public_key'],
|
|
||||||
'summary': 'Bot account to federate with {}\'s library'.format(
|
|
||||||
settings.FEDERATION_HOSTNAME
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ class SignatureAuthentication(authentication.BaseAuthentication):
|
||||||
return serializer.build()
|
return serializer.build()
|
||||||
|
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
|
setattr(request, 'actor', None)
|
||||||
actor = self.authenticate_actor(request)
|
actor = self.authenticate_actor(request)
|
||||||
user = AnonymousUser()
|
user = AnonymousUser()
|
||||||
setattr(request, 'actor', actor)
|
setattr(request, 'actor', actor)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
from rest_framework import parsers
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityParser(parsers.JSONParser):
|
||||||
|
media_type = 'application/activity+json'
|
|
@ -6,6 +6,7 @@ from django.conf import settings
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from dynamic_preferences.registries import global_preferences_registry
|
from dynamic_preferences.registries import global_preferences_registry
|
||||||
|
|
||||||
|
from . import activity
|
||||||
from . import models
|
from . import models
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
|
@ -105,3 +106,70 @@ class ActorWebfingerSerializer(serializers.ModelSerializer):
|
||||||
instance.url
|
instance.url
|
||||||
]
|
]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class ActivitySerializer(serializers.Serializer):
|
||||||
|
actor = serializers.URLField()
|
||||||
|
id = serializers.URLField()
|
||||||
|
type = serializers.ChoiceField(
|
||||||
|
choices=[(c, c) for c in activity.ACTIVITY_TYPES])
|
||||||
|
object = serializers.JSONField()
|
||||||
|
|
||||||
|
def validate_object(self, value):
|
||||||
|
try:
|
||||||
|
type = value['type']
|
||||||
|
except KeyError:
|
||||||
|
raise serializers.ValidationError('Missing object type')
|
||||||
|
|
||||||
|
try:
|
||||||
|
object_serializer = OBJECT_SERIALIZERS[type]
|
||||||
|
except KeyError:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
'Unsupported type {}'.format(type))
|
||||||
|
|
||||||
|
serializer = object_serializer(data=value)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
def validate_actor(self, value):
|
||||||
|
request_actor = self.context.get('actor')
|
||||||
|
if request_actor and request_actor.url != value:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
'The actor making the request do not match'
|
||||||
|
' the activity actor'
|
||||||
|
)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectSerializer(serializers.Serializer):
|
||||||
|
id = serializers.URLField()
|
||||||
|
url = serializers.URLField(required=False, allow_null=True)
|
||||||
|
type = serializers.ChoiceField(
|
||||||
|
choices=[(c, c) for c in activity.OBJECT_TYPES])
|
||||||
|
content = serializers.CharField(
|
||||||
|
required=False, allow_null=True)
|
||||||
|
summary = serializers.CharField(
|
||||||
|
required=False, allow_null=True)
|
||||||
|
name = serializers.CharField(
|
||||||
|
required=False, allow_null=True)
|
||||||
|
published = serializers.DateTimeField(
|
||||||
|
required=False, allow_null=True)
|
||||||
|
updated = serializers.DateTimeField(
|
||||||
|
required=False, allow_null=True)
|
||||||
|
to = serializers.ListField(
|
||||||
|
child=serializers.URLField(),
|
||||||
|
required=False, allow_null=True)
|
||||||
|
cc = serializers.ListField(
|
||||||
|
child=serializers.URLField(),
|
||||||
|
required=False, allow_null=True)
|
||||||
|
bto = serializers.ListField(
|
||||||
|
child=serializers.URLField(),
|
||||||
|
required=False, allow_null=True)
|
||||||
|
bcc = serializers.ListField(
|
||||||
|
child=serializers.URLField(),
|
||||||
|
required=False, allow_null=True)
|
||||||
|
|
||||||
|
OBJECT_SERIALIZERS = {
|
||||||
|
t: ObjectSerializer
|
||||||
|
for t in activity.OBJECT_TYPES
|
||||||
|
}
|
||||||
|
|
|
@ -36,18 +36,35 @@ class InstanceActorViewSet(FederationMixin, viewsets.GenericViewSet):
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
actor_conf = self.get_object()
|
system_actor = self.get_object()
|
||||||
actor = actor_conf['get_actor']()
|
actor = system_actor.get_actor_instance()
|
||||||
serializer = serializers.ActorSerializer(actor)
|
serializer = serializers.ActorSerializer(actor)
|
||||||
return response.Response(serializer.data, status=200)
|
return response.Response(serializer.data, status=200)
|
||||||
|
|
||||||
@detail_route(methods=['get'])
|
@detail_route(methods=['get', 'post'])
|
||||||
def inbox(self, request, *args, **kwargs):
|
def inbox(self, request, *args, **kwargs):
|
||||||
raise NotImplementedError()
|
system_actor = self.get_object()
|
||||||
|
handler = getattr(system_actor, '{}_inbox'.format(
|
||||||
|
request.method.lower()
|
||||||
|
))
|
||||||
|
|
||||||
@detail_route(methods=['get'])
|
try:
|
||||||
|
data = handler(request.data, actor=request.actor)
|
||||||
|
except NotImplementedError:
|
||||||
|
return response.Response(status=405)
|
||||||
|
return response.Response(data, status=200)
|
||||||
|
|
||||||
|
@detail_route(methods=['get', 'post'])
|
||||||
def outbox(self, request, *args, **kwargs):
|
def outbox(self, request, *args, **kwargs):
|
||||||
raise NotImplementedError()
|
system_actor = self.get_object()
|
||||||
|
handler = getattr(system_actor, '{}_outbox'.format(
|
||||||
|
request.method.lower()
|
||||||
|
))
|
||||||
|
try:
|
||||||
|
data = handler(request.data, actor=request.actor)
|
||||||
|
except NotImplementedError:
|
||||||
|
return response.Response(status=405)
|
||||||
|
return response.Response(data, status=200)
|
||||||
|
|
||||||
|
|
||||||
class WellKnownViewSet(FederationMixin, viewsets.GenericViewSet):
|
class WellKnownViewSet(FederationMixin, viewsets.GenericViewSet):
|
||||||
|
@ -82,5 +99,5 @@ class WellKnownViewSet(FederationMixin, viewsets.GenericViewSet):
|
||||||
|
|
||||||
def handler_acct(self, clean_result):
|
def handler_acct(self, clean_result):
|
||||||
username, hostname = clean_result
|
username, hostname = clean_result
|
||||||
actor = actors.SYSTEM_ACTORS[username]['get_actor']()
|
actor = actors.SYSTEM_ACTORS[username].get_actor_instance()
|
||||||
return serializers.ActorWebfingerSerializer(actor).data
|
return serializers.ActorWebfingerSerializer(actor).data
|
||||||
|
|
|
@ -30,7 +30,8 @@ def clean_acct(acct_string):
|
||||||
raise forms.ValidationError('Invalid format')
|
raise forms.ValidationError('Invalid format')
|
||||||
|
|
||||||
if hostname != settings.FEDERATION_HOSTNAME:
|
if hostname != settings.FEDERATION_HOSTNAME:
|
||||||
raise forms.ValidationError('Invalid hostname')
|
raise forms.ValidationError(
|
||||||
|
'Invalid hostname {}'.format(hostname))
|
||||||
|
|
||||||
if username not in actors.SYSTEM_ACTORS:
|
if username not in actors.SYSTEM_ACTORS:
|
||||||
raise forms.ValidationError('Invalid username')
|
raise forms.ValidationError('Invalid username')
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from rest_framework import exceptions
|
||||||
|
|
||||||
from funkwhale_api.federation import actors
|
from funkwhale_api.federation import actors
|
||||||
|
from funkwhale_api.federation import serializers
|
||||||
from funkwhale_api.federation import utils
|
from funkwhale_api.federation import utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,10 +41,106 @@ def test_get_library(settings, preferences):
|
||||||
reverse(
|
reverse(
|
||||||
'federation:instance-actors-inbox',
|
'federation:instance-actors-inbox',
|
||||||
kwargs={'actor': 'library'})),
|
kwargs={'actor': 'library'})),
|
||||||
|
'outbox_url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-outbox',
|
||||||
|
kwargs={'actor': 'library'})),
|
||||||
'public_key': 'public_key',
|
'public_key': 'public_key',
|
||||||
'summary': 'Bot account to federate with {}\'s library'.format(
|
'summary': 'Bot account to federate with {}\'s library'.format(
|
||||||
settings.FEDERATION_HOSTNAME),
|
settings.FEDERATION_HOSTNAME),
|
||||||
}
|
}
|
||||||
actor = actors.SYSTEM_ACTORS['library']['get_actor']()
|
actor = actors.SYSTEM_ACTORS['library'].get_actor_instance()
|
||||||
for key, value in expected.items():
|
for key, value in expected.items():
|
||||||
assert getattr(actor, key) == value
|
assert getattr(actor, key) == value
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_test(settings, preferences):
|
||||||
|
preferences['federation__public_key'] = 'public_key'
|
||||||
|
expected = {
|
||||||
|
'preferred_username': 'test',
|
||||||
|
'domain': settings.FEDERATION_HOSTNAME,
|
||||||
|
'type': 'Person',
|
||||||
|
'name': '{}\'s test account'.format(settings.FEDERATION_HOSTNAME),
|
||||||
|
'manually_approves_followers': False,
|
||||||
|
'url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-detail',
|
||||||
|
kwargs={'actor': 'test'})),
|
||||||
|
'shared_inbox_url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-inbox',
|
||||||
|
kwargs={'actor': 'test'})),
|
||||||
|
'inbox_url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-inbox',
|
||||||
|
kwargs={'actor': 'test'})),
|
||||||
|
'outbox_url': utils.full_url(
|
||||||
|
reverse(
|
||||||
|
'federation:instance-actors-outbox',
|
||||||
|
kwargs={'actor': 'test'})),
|
||||||
|
'public_key': 'public_key',
|
||||||
|
'summary': 'Bot account to test federation with {}. Send me /ping and I\'ll answer you.'.format(
|
||||||
|
settings.FEDERATION_HOSTNAME),
|
||||||
|
}
|
||||||
|
actor = actors.SYSTEM_ACTORS['test'].get_actor_instance()
|
||||||
|
for key, value in expected.items():
|
||||||
|
assert getattr(actor, key) == value
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_get_outbox():
|
||||||
|
expected = {
|
||||||
|
"@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": []
|
||||||
|
}
|
||||||
|
|
||||||
|
data = actors.SYSTEM_ACTORS['test'].get_outbox({}, actor=None)
|
||||||
|
|
||||||
|
assert data == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_post_inbox_requires_authenticated_actor():
|
||||||
|
with pytest.raises(exceptions.PermissionDenied):
|
||||||
|
actors.SYSTEM_ACTORS['test'].post_inbox({}, actor=None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_post_outbox_validates_actor(nodb_factories):
|
||||||
|
actor = nodb_factories['federation.Actor']()
|
||||||
|
data = {
|
||||||
|
'actor': 'noop'
|
||||||
|
}
|
||||||
|
with pytest.raises(exceptions.ValidationError) as exc_info:
|
||||||
|
actors.SYSTEM_ACTORS['test'].post_inbox(data, actor=actor)
|
||||||
|
msg = 'The actor making the request do not match'
|
||||||
|
assert msg in exc_info.value
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_post_outbox_handles_create_note(mocker, factories):
|
||||||
|
deliver = mocker.patch(
|
||||||
|
'funkwhale_api.federation.activity.deliver')
|
||||||
|
actor = factories['federation.Actor']()
|
||||||
|
data = {
|
||||||
|
'actor': actor.url,
|
||||||
|
'type': 'Create',
|
||||||
|
'id': 'http://test.federation/activity',
|
||||||
|
'object': {
|
||||||
|
'type': 'Note',
|
||||||
|
'id': 'http://test.federation/object',
|
||||||
|
'content': '<p><a>@mention</a> /ping</p>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actors.SYSTEM_ACTORS['test'].post_inbox(data, actor=actor)
|
||||||
|
deliver.assert_called_once_with(
|
||||||
|
content='Pong!',
|
||||||
|
to=[actor.url],
|
||||||
|
on_behalf_of=actors.SYSTEM_ACTORS['test'].get_actor_instance()
|
||||||
|
)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from funkwhale_api.federation import webfinger
|
||||||
|
|
||||||
@pytest.mark.parametrize('system_actor', actors.SYSTEM_ACTORS.keys())
|
@pytest.mark.parametrize('system_actor', actors.SYSTEM_ACTORS.keys())
|
||||||
def test_instance_actors(system_actor, db, settings, api_client):
|
def test_instance_actors(system_actor, db, settings, api_client):
|
||||||
actor = actors.SYSTEM_ACTORS[system_actor]['get_actor']()
|
actor = actors.SYSTEM_ACTORS[system_actor].get_actor_instance()
|
||||||
url = reverse(
|
url = reverse(
|
||||||
'federation:instance-actors-detail',
|
'federation:instance-actors-detail',
|
||||||
kwargs={'actor': system_actor})
|
kwargs={'actor': system_actor})
|
||||||
|
@ -27,7 +27,7 @@ def test_instance_actors(system_actor, db, settings, api_client):
|
||||||
('instance-actors-detail', {'actor': 'library'}),
|
('instance-actors-detail', {'actor': 'library'}),
|
||||||
('well-known-webfinger', {}),
|
('well-known-webfinger', {}),
|
||||||
])
|
])
|
||||||
def test_instance_inbox_405_if_federation_disabled(
|
def test_instance_endpoints_405_if_federation_disabled(
|
||||||
authenticated_actor, db, settings, api_client, route, kwargs):
|
authenticated_actor, db, settings, api_client, route, kwargs):
|
||||||
settings.FEDERATION_ENABLED = False
|
settings.FEDERATION_ENABLED = False
|
||||||
url = reverse('federation:{}'.format(route), kwargs=kwargs)
|
url = reverse('federation:{}'.format(route), kwargs=kwargs)
|
||||||
|
@ -53,7 +53,7 @@ def test_wellknown_webfinger_validates_resource(
|
||||||
@pytest.mark.parametrize('system_actor', actors.SYSTEM_ACTORS.keys())
|
@pytest.mark.parametrize('system_actor', actors.SYSTEM_ACTORS.keys())
|
||||||
def test_wellknown_webfinger_system(
|
def test_wellknown_webfinger_system(
|
||||||
system_actor, db, api_client, settings, mocker):
|
system_actor, db, api_client, settings, mocker):
|
||||||
actor = actors.SYSTEM_ACTORS[system_actor]['get_actor']()
|
actor = actors.SYSTEM_ACTORS[system_actor].get_actor_instance()
|
||||||
url = reverse('federation:well-known-webfinger')
|
url = reverse('federation:well-known-webfinger')
|
||||||
response = api_client.get(
|
response = api_client.get(
|
||||||
url, data={'resource': 'acct:{}'.format(actor.webfinger_subject)})
|
url, data={'resource': 'acct:{}'.format(actor.webfinger_subject)})
|
||||||
|
|
|
@ -32,7 +32,7 @@ def test_webfinger_clean_acct(settings):
|
||||||
|
|
||||||
@pytest.mark.parametrize('resource,message', [
|
@pytest.mark.parametrize('resource,message', [
|
||||||
('service', 'Invalid format'),
|
('service', 'Invalid format'),
|
||||||
('service@test.com', 'Invalid hostname'),
|
('service@test.com', 'Invalid hostname test.com'),
|
||||||
('noop@test.federation', 'Invalid account'),
|
('noop@test.federation', 'Invalid account'),
|
||||||
])
|
])
|
||||||
def test_webfinger_clean_acct_errors(resource, message, settings):
|
def test_webfinger_clean_acct_errors(resource, message, settings):
|
||||||
|
|
Loading…
Reference in New Issue