Url and views for instance actor and webfinger

This commit is contained in:
Eliot Berriot 2018-03-29 00:00:47 +02:00
parent e793f8365f
commit 75710638de
No known key found for this signature in database
GPG Key ID: DD6965E2476E5C27
5 changed files with 181 additions and 0 deletions

View File

@ -13,6 +13,9 @@ urlpatterns = [
url(settings.ADMIN_URL, admin.site.urls), url(settings.ADMIN_URL, admin.site.urls),
url(r'^api/', include(("config.api_urls", 'api'), namespace="api")), url(r'^api/', include(("config.api_urls", 'api'), namespace="api")),
url(r'^', include(
('funkwhale_api.federation.urls', 'federation'),
namespace="federation")),
url(r'^api/v1/auth/', include('rest_auth.urls')), url(r'^api/v1/auth/', include('rest_auth.urls')),
url(r'^api/v1/auth/registration/', include('funkwhale_api.users.rest_auth_urls')), url(r'^api/v1/auth/registration/', include('funkwhale_api.users.rest_auth_urls')),
url(r'^accounts/', include('allauth.urls')), url(r'^accounts/', include('allauth.urls')),

View File

@ -0,0 +1,21 @@
from django.urls import reverse
from django.conf import settings
from . import utils
def repr_instance_actor():
"""
We do not use a serializer here, since it's pretty static
"""
return {
'@context': [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1',
{},
],
'id': utils.full_url(reverse('federation:instance-actor')),
'type': 'Service',
'inbox': utils.full_url(reverse('federation:instance-inbox')),
'outbox': utils.full_url(reverse('federation:instance-outbox')),
}

View File

@ -0,0 +1,15 @@
from rest_framework import routers
from . import views
router = routers.SimpleRouter(trailing_slash=False)
router.register(
r'instance',
views.InstanceViewSet,
'instance')
router.register(
r'.well-known',
views.WellKnownViewSet,
'well-known')
urlpatterns = router.urls

View File

@ -0,0 +1,73 @@
from django import forms
from django.conf import settings
from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework import views
from rest_framework import response
from rest_framework.decorators import list_route
from . import serializers
from . import webfinger
class FederationMixin(object):
def dispatch(self, request, *args, **kwargs):
if not settings.FEDERATION_ENABLED:
return HttpResponse(status=405)
return super().dispatch(request, *args, **kwargs)
class InstanceViewSet(FederationMixin, viewsets.GenericViewSet):
authentication_classes = []
permission_classes = []
@list_route(methods=['get'])
def actor(self, request, *args, **kwargs):
return response.Response(serializers.repr_instance_actor())
@list_route(methods=['get'])
def inbox(self, request, *args, **kwargs):
raise NotImplementedError()
@list_route(methods=['get'])
def outbox(self, request, *args, **kwargs):
raise NotImplementedError()
class WellKnownViewSet(FederationMixin, viewsets.GenericViewSet):
authentication_classes = []
permission_classes = []
@list_route(methods=['get'])
def webfinger(self, request, *args, **kwargs):
try:
resource_type, resource = webfinger.clean_resource(
request.GET['resource'])
cleaner = getattr(webfinger, 'clean_{}'.format(resource_type))
result = cleaner(resource)
except forms.ValidationError as e:
return response.Response({
'errors': {
'resource': e.message
}
}, status=400)
except KeyError:
return response.Response({
'errors': {
'resource': 'This field is required',
}
}, status=400)
handler = getattr(self, 'handler_{}'.format(resource_type))
data = handler(result)
return response.Response(
data,
content_type='application/jrd+json; charset=utf-8')
def handler_acct(self, clean_result):
username, hostname = clean_result
if username == 'service':
return webfinger.serialize_system_acct()
return {}

View File

@ -0,0 +1,69 @@
from django.urls import reverse
import pytest
from funkwhale_api.federation import webfinger
def test_instance_actor(db, settings, api_client):
settings.FUNKWHALE_URL = 'http://test.com'
url = reverse('federation:instance-actor')
response = api_client.get(url)
assert response.data['id'] == (
settings.FUNKWHALE_URL + url
)
assert response.data['type'] == 'Service'
assert response.data['inbox'] == (
settings.FUNKWHALE_URL + reverse('federation:instance-inbox')
)
assert response.data['outbox'] == (
settings.FUNKWHALE_URL + reverse('federation:instance-outbox')
)
assert response.data['@context'] == [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1',
{},
]
@pytest.mark.parametrize('route', [
'instance-outbox',
'instance-inbox',
'instance-actor',
'well-known-webfinger',
])
def test_instance_inbox_405_if_federation_disabled(
db, settings, api_client, route):
settings.FEDERATION_ENABLED = False
url = reverse('federation:{}'.format(route))
response = api_client.get(url)
assert response.status_code == 405
def test_wellknown_webfinger_validates_resource(
db, api_client, settings, mocker):
clean = mocker.spy(webfinger, 'clean_resource')
settings.FEDERATION_ENABLED = True
url = reverse('federation:well-known-webfinger')
response = api_client.get(url, data={'resource': 'something'})
clean.assert_called_once_with('something')
assert url == '/.well-known/webfinger'
assert response.status_code == 400
assert response.data['errors']['resource'] == (
'Missing webfinger resource type'
)
def test_wellknown_webfinger_system(
db, api_client, settings, mocker):
settings.FEDERATION_ENABLED = True
settings.FEDERATION_HOSTNAME = 'test.federation'
url = reverse('federation:well-known-webfinger')
response = api_client.get(
url, data={'resource': 'acct:service@test.federation'})
assert response.status_code == 200
assert response['Content-Type'] == 'application/jrd+json; charset=utf-8'
assert response.data == webfinger.serialize_system_acct()