Url and views for instance actor and webfinger
This commit is contained in:
parent
e793f8365f
commit
75710638de
|
@ -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')),
|
||||||
|
|
|
@ -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')),
|
||||||
|
}
|
|
@ -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
|
|
@ -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 {}
|
|
@ -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()
|
Loading…
Reference in New Issue