From b8c7e960c3a073bc9b25adfde5f9dca27466e815 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Sun, 8 Apr 2018 10:42:10 +0200
Subject: [PATCH] Now validate incoming webfinger

---
 api/funkwhale_api/federation/serializers.py | 25 ++++++++++++++++----
 api/funkwhale_api/federation/webfinger.py   | 19 +++++++++++++--
 api/tests/federation/test_webfinger.py      | 26 +++++++++++++++++++++
 3 files changed, 64 insertions(+), 6 deletions(-)

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
-- 
GitLab