diff --git a/api/funkwhale_api/federation/serializers.py b/api/funkwhale_api/federation/serializers.py index a3ba80749..7e84e575a 100644 --- a/api/funkwhale_api/federation/serializers.py +++ b/api/funkwhale_api/federation/serializers.py @@ -116,10 +116,27 @@ class FollowSerializer(serializers.ModelSerializer): return ret -class ActorWebfingerSerializer(serializers.ModelSerializer): - class Meta: - model = models.Actor - fields = ['url'] +class ActorWebfingerSerializer(serializers.Serializer): + subject = serializers.CharField() + aliases = serializers.ListField(child=serializers.URLField()) + links = serializers.ListField() + actor_url = serializers.URLField(required=False) + + def validate(self, validated_data): + validated_data['actor_url'] = None + for l in validated_data['links']: + try: + if not l['rel'] == 'self': + continue + if not l['type'] == 'application/activity+json': + continue + validated_data['actor_url'] = l['href'] + break + except KeyError: + pass + if validated_data['actor_url'] is None: + raise serializers.ValidationError('No valid actor url found') + return validated_data def to_representation(self, instance): data = {} diff --git a/api/funkwhale_api/federation/webfinger.py b/api/funkwhale_api/federation/webfinger.py index 4e9753385..011dcf576 100644 --- a/api/funkwhale_api/federation/webfinger.py +++ b/api/funkwhale_api/federation/webfinger.py @@ -2,8 +2,11 @@ from django import forms from django.conf import settings from django.urls import reverse +from funkwhale_api.common import session + from . import actors from . import utils +from . import serializers VALID_RESOURCE_TYPES = ['acct'] @@ -23,13 +26,13 @@ def clean_resource(resource_string): return resource_type, resource -def clean_acct(acct_string): +def clean_acct(acct_string, ensure_local=True): try: username, hostname = acct_string.split('@') except ValueError: raise forms.ValidationError('Invalid format') - if hostname.lower() != settings.FEDERATION_HOSTNAME: + if ensure_local and hostname.lower() != settings.FEDERATION_HOSTNAME: raise forms.ValidationError( 'Invalid hostname {}'.format(hostname)) @@ -37,3 +40,15 @@ def clean_acct(acct_string): raise forms.ValidationError('Invalid username') return username, hostname + + +def get_resource(resource_string): + resource_type, resource = clean_resource(resource_string) + username, hostname = clean_acct(resource, ensure_local=False) + url = 'https://{}/.well-known/webfinger?resource={}'.format( + hostname, resource_string) + response = session.get_session().get(url) + response.raise_for_status() + serializer = serializers.ActorWebfingerSerializer(data=response.json()) + serializer.is_valid(raise_exception=True) + return serializer.validated_data diff --git a/api/tests/federation/test_webfinger.py b/api/tests/federation/test_webfinger.py index 96258455a..4b8dca207 100644 --- a/api/tests/federation/test_webfinger.py +++ b/api/tests/federation/test_webfinger.py @@ -40,3 +40,29 @@ def test_webfinger_clean_acct_errors(resource, message, settings): webfinger.clean_resource(resource) assert message == str(excinfo) + + +def test_webfinger_get_resource(r_mock): + resource = 'acct:test@test.webfinger' + payload = { + 'subject': resource, + 'aliases': ['https://test.webfinger'], + 'links': [ + { + 'rel': 'self', + 'type': 'application/activity+json', + 'href': 'https://test.webfinger/user/test' + } + ] + } + r_mock.get( + 'https://test.webfinger/.well-known/webfinger?resource={}'.format( + resource + ), + json=payload + ) + + data = webfinger.get_resource('acct:test@test.webfinger') + + assert data['actor_url'] == 'https://test.webfinger/user/test' + assert data['subject'] == resource