2018-06-10 08:55:16 +00:00
|
|
|
import pytest
|
2018-04-06 15:59:06 +00:00
|
|
|
from django.core.paginator import Paginator
|
2018-04-12 16:42:21 +00:00
|
|
|
from django.urls import reverse
|
2018-09-06 18:35:02 +00:00
|
|
|
|
2019-01-30 10:54:43 +00:00
|
|
|
from funkwhale_api.federation import actors, serializers, webfinger
|
2018-03-28 22:00:47 +00:00
|
|
|
|
2018-03-31 13:47:21 +00:00
|
|
|
|
2019-06-26 08:22:29 +00:00
|
|
|
def test_authenticate_skips_anonymous_fetch_when_allow_list_enabled(
|
|
|
|
preferences, api_client
|
|
|
|
):
|
|
|
|
preferences["moderation__allow_list_enabled"] = True
|
|
|
|
actor = actors.get_service_actor()
|
|
|
|
url = reverse(
|
|
|
|
"federation:actors-detail",
|
|
|
|
kwargs={"preferred_username": actor.preferred_username},
|
|
|
|
)
|
|
|
|
response = api_client.get(url)
|
|
|
|
|
|
|
|
assert response.status_code == 403
|
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
def test_wellknown_webfinger_validates_resource(db, api_client, settings, mocker):
|
|
|
|
clean = mocker.spy(webfinger, "clean_resource")
|
|
|
|
url = reverse("federation:well-known-webfinger")
|
|
|
|
response = api_client.get(url, data={"resource": "something"})
|
2018-03-28 22:00:47 +00:00
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
clean.assert_called_once_with("something")
|
|
|
|
assert url == "/.well-known/webfinger"
|
2018-03-28 22:00:47 +00:00
|
|
|
assert response.status_code == 400
|
2018-06-09 13:36:16 +00:00
|
|
|
assert response.data["errors"]["resource"] == ("Missing webfinger resource type")
|
2018-03-28 22:00:47 +00:00
|
|
|
|
|
|
|
|
2018-05-07 20:28:46 +00:00
|
|
|
def test_wellknown_nodeinfo(db, preferences, api_client, settings):
|
|
|
|
expected = {
|
2018-06-09 13:36:16 +00:00
|
|
|
"links": [
|
2018-05-07 20:28:46 +00:00
|
|
|
{
|
2018-06-09 13:36:16 +00:00
|
|
|
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
|
|
|
"href": "{}{}".format(
|
|
|
|
settings.FUNKWHALE_URL, reverse("api:v1:instance:nodeinfo-2.0")
|
|
|
|
),
|
2018-05-07 20:28:46 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("federation:well-known-nodeinfo")
|
|
|
|
response = api_client.get(url, HTTP_ACCEPT="application/jrd+json")
|
2018-05-07 20:28:46 +00:00
|
|
|
assert response.status_code == 200
|
2018-06-09 13:36:16 +00:00
|
|
|
assert response["Content-Type"] == "application/jrd+json"
|
2018-05-07 20:28:46 +00:00
|
|
|
assert response.data == expected
|
|
|
|
|
|
|
|
|
|
|
|
def test_wellknown_nodeinfo_disabled(db, preferences, api_client):
|
2018-06-09 13:36:16 +00:00
|
|
|
preferences["instance__nodeinfo_enabled"] = False
|
|
|
|
url = reverse("federation:well-known-nodeinfo")
|
2018-05-07 20:28:46 +00:00
|
|
|
response = api_client.get(url)
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
def test_local_actor_detail(factories, api_client):
|
|
|
|
user = factories["users.User"](with_actor=True)
|
2018-04-11 19:58:41 +00:00
|
|
|
url = reverse(
|
2018-09-06 18:35:02 +00:00
|
|
|
"federation:actors-detail",
|
|
|
|
kwargs={"preferred_username": user.actor.preferred_username},
|
2018-06-09 13:36:16 +00:00
|
|
|
)
|
2018-09-06 18:35:02 +00:00
|
|
|
serializer = serializers.ActorSerializer(user.actor)
|
|
|
|
response = api_client.get(url)
|
2018-04-11 19:58:41 +00:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-09-06 18:35:02 +00:00
|
|
|
assert response.data == serializer.data
|
2018-04-11 19:58:41 +00:00
|
|
|
|
|
|
|
|
2019-01-30 10:54:43 +00:00
|
|
|
def test_service_actor_detail(factories, api_client):
|
|
|
|
actor = actors.get_service_actor()
|
|
|
|
url = reverse(
|
|
|
|
"federation:actors-detail",
|
|
|
|
kwargs={"preferred_username": actor.preferred_username},
|
|
|
|
)
|
|
|
|
serializer = serializers.ActorSerializer(actor)
|
|
|
|
response = api_client.get(url)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert response.data == serializer.data
|
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
def test_local_actor_inbox_post_requires_auth(factories, api_client):
|
|
|
|
user = factories["users.User"](with_actor=True)
|
2018-04-11 19:58:41 +00:00
|
|
|
url = reverse(
|
2018-09-06 18:35:02 +00:00
|
|
|
"federation:actors-inbox",
|
|
|
|
kwargs={"preferred_username": user.actor.preferred_username},
|
2018-06-09 13:36:16 +00:00
|
|
|
)
|
2018-09-06 18:35:02 +00:00
|
|
|
response = api_client.post(url, {"hello": "world"})
|
2018-04-11 19:58:41 +00:00
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
assert response.status_code == 403
|
2018-04-12 16:42:21 +00:00
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
def test_local_actor_inbox_post(factories, api_client, mocker, authenticated_actor):
|
|
|
|
patched_receive = mocker.patch("funkwhale_api.federation.activity.receive")
|
|
|
|
user = factories["users.User"](with_actor=True)
|
2018-04-12 16:42:21 +00:00
|
|
|
url = reverse(
|
2018-09-06 18:35:02 +00:00
|
|
|
"federation:actors-inbox",
|
|
|
|
kwargs={"preferred_username": user.actor.preferred_username},
|
2018-06-09 13:36:16 +00:00
|
|
|
)
|
2018-09-06 18:35:02 +00:00
|
|
|
response = api_client.post(url, {"hello": "world"}, format="json")
|
2018-04-12 16:42:21 +00:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-09-06 18:35:02 +00:00
|
|
|
patched_receive.assert_called_once_with(
|
2018-09-13 15:18:23 +00:00
|
|
|
activity={"hello": "world"}, on_behalf_of=authenticated_actor
|
2018-06-09 13:36:16 +00:00
|
|
|
)
|
2018-04-12 18:38:06 +00:00
|
|
|
|
2018-04-14 16:50:37 +00:00
|
|
|
|
2019-03-05 14:15:37 +00:00
|
|
|
def test_local_actor_inbox_post_receive(
|
|
|
|
factories, api_client, mocker, authenticated_actor
|
|
|
|
):
|
|
|
|
payload = {
|
|
|
|
"to": [
|
|
|
|
"https://test.server/federation/music/libraries/956af6c9-1eb9-4117-8d17-b15e7b34afeb/followers"
|
|
|
|
],
|
|
|
|
"type": "Create",
|
|
|
|
"actor": authenticated_actor.fid,
|
|
|
|
"object": {
|
|
|
|
"id": "https://test.server/federation/music/uploads/fe564a47-b1d4-4596-bf96-008ccf407672",
|
|
|
|
"type": "Audio",
|
|
|
|
},
|
|
|
|
"@context": [
|
|
|
|
"https://www.w3.org/ns/activitystreams",
|
|
|
|
"https://w3id.org/security/v1",
|
|
|
|
{},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
user = factories["users.User"](with_actor=True)
|
|
|
|
url = reverse(
|
|
|
|
"federation:actors-inbox",
|
|
|
|
kwargs={"preferred_username": user.actor.preferred_username},
|
|
|
|
)
|
|
|
|
response = api_client.post(url, payload, format="json")
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
2018-09-22 12:29:30 +00:00
|
|
|
def test_shared_inbox_post(factories, api_client, mocker, authenticated_actor):
|
|
|
|
patched_receive = mocker.patch("funkwhale_api.federation.activity.receive")
|
|
|
|
url = reverse("federation:shared-inbox")
|
|
|
|
response = api_client.post(url, {"hello": "world"}, format="json")
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
patched_receive.assert_called_once_with(
|
|
|
|
activity={"hello": "world"}, on_behalf_of=authenticated_actor
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
def test_wellknown_webfinger_local(factories, api_client, settings, mocker):
|
|
|
|
user = factories["users.User"](with_actor=True)
|
|
|
|
url = reverse("federation:well-known-webfinger")
|
|
|
|
response = api_client.get(
|
|
|
|
url,
|
|
|
|
data={"resource": "acct:{}".format(user.actor.webfinger_subject)},
|
|
|
|
HTTP_ACCEPT="application/jrd+json",
|
|
|
|
)
|
|
|
|
serializer = serializers.ActorWebfingerSerializer(user.actor)
|
2018-04-14 16:50:37 +00:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-09-06 18:35:02 +00:00
|
|
|
assert response["Content-Type"] == "application/jrd+json"
|
|
|
|
assert response.data == serializer.data
|
2018-04-14 16:50:37 +00:00
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
@pytest.mark.parametrize("privacy_level", ["me", "instance", "everyone"])
|
|
|
|
def test_music_library_retrieve(factories, api_client, privacy_level):
|
|
|
|
library = factories["music.Library"](privacy_level=privacy_level)
|
|
|
|
expected = serializers.LibrarySerializer(library).data
|
2018-04-14 16:50:37 +00:00
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
|
|
|
|
response = api_client.get(url)
|
2018-04-14 16:50:37 +00:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-09-06 18:35:02 +00:00
|
|
|
assert response.data == expected
|
2018-05-23 19:50:23 +00:00
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
def test_music_library_retrieve_page_public(factories, api_client):
|
|
|
|
library = factories["music.Library"](privacy_level="everyone")
|
2018-09-24 18:44:22 +00:00
|
|
|
upload = factories["music.Upload"](library=library, import_status="finished")
|
2018-09-06 18:35:02 +00:00
|
|
|
id = library.get_federation_id()
|
|
|
|
expected = serializers.CollectionPageSerializer(
|
|
|
|
{
|
|
|
|
"id": id,
|
2018-09-22 12:29:30 +00:00
|
|
|
"item_serializer": serializers.UploadSerializer,
|
2018-09-06 18:35:02 +00:00
|
|
|
"actor": library.actor,
|
2018-09-22 12:29:30 +00:00
|
|
|
"page": Paginator([upload], 1).page(1),
|
2018-09-06 18:35:02 +00:00
|
|
|
"name": library.name,
|
|
|
|
"summary": library.description,
|
|
|
|
}
|
|
|
|
).data
|
2018-05-23 19:50:23 +00:00
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
|
|
|
|
response = api_client.get(url, {"page": 1})
|
2018-05-23 19:50:23 +00:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert response.data == expected
|
2018-07-22 10:20:16 +00:00
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
@pytest.mark.parametrize("privacy_level", ["me", "instance"])
|
|
|
|
def test_music_library_retrieve_page_private(factories, api_client, privacy_level):
|
|
|
|
library = factories["music.Library"](privacy_level=privacy_level)
|
|
|
|
url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
|
|
|
|
response = api_client.get(url, {"page": 1})
|
2018-07-22 10:20:16 +00:00
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
assert response.status_code == 403
|
2018-07-22 10:20:16 +00:00
|
|
|
|
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
@pytest.mark.parametrize("approved,expected", [(True, 200), (False, 403)])
|
|
|
|
def test_music_library_retrieve_page_follow(
|
|
|
|
factories, api_client, authenticated_actor, approved, expected
|
|
|
|
):
|
|
|
|
library = factories["music.Library"](privacy_level="me")
|
|
|
|
factories["federation.LibraryFollow"](
|
|
|
|
actor=authenticated_actor, target=library, approved=approved
|
2018-07-22 10:20:16 +00:00
|
|
|
)
|
2018-09-06 18:35:02 +00:00
|
|
|
url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
|
|
|
|
response = api_client.get(url, {"page": 1})
|
2018-07-22 10:20:16 +00:00
|
|
|
|
2018-09-06 18:35:02 +00:00
|
|
|
assert response.status_code == expected
|
2019-02-28 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"factory, serializer_class, namespace",
|
|
|
|
[
|
|
|
|
("music.Artist", serializers.ArtistSerializer, "artists"),
|
|
|
|
("music.Album", serializers.AlbumSerializer, "albums"),
|
|
|
|
("music.Track", serializers.TrackSerializer, "tracks"),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_music_local_entity_detail(
|
|
|
|
factories, api_client, factory, serializer_class, namespace, settings
|
|
|
|
):
|
|
|
|
obj = factories[factory](fid="http://{}/1".format(settings.FEDERATION_HOSTNAME))
|
|
|
|
url = reverse(
|
|
|
|
"federation:music:{}-detail".format(namespace), kwargs={"uuid": obj.uuid}
|
|
|
|
)
|
|
|
|
response = api_client.get(url)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert response.data == serializer_class(obj).data
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"factory, namespace",
|
|
|
|
[("music.Artist", "artists"), ("music.Album", "albums"), ("music.Track", "tracks")],
|
|
|
|
)
|
|
|
|
def test_music_non_local_entity_detail(
|
|
|
|
factories, api_client, factory, namespace, settings
|
|
|
|
):
|
|
|
|
obj = factories[factory](fid="http://wrong-domain/1")
|
|
|
|
url = reverse(
|
|
|
|
"federation:music:{}-detail".format(namespace), kwargs={"uuid": obj.uuid}
|
|
|
|
)
|
|
|
|
response = api_client.get(url)
|
|
|
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"privacy_level, expected", [("me", 404), ("instance", 404), ("everyone", 200)]
|
|
|
|
)
|
|
|
|
def test_music_upload_detail(factories, api_client, privacy_level, expected):
|
|
|
|
upload = factories["music.Upload"](
|
|
|
|
library__privacy_level=privacy_level,
|
|
|
|
library__actor__local=True,
|
|
|
|
import_status="finished",
|
|
|
|
)
|
|
|
|
url = reverse("federation:music:uploads-detail", kwargs={"uuid": upload.uuid})
|
|
|
|
response = api_client.get(url)
|
|
|
|
|
|
|
|
assert response.status_code == expected
|
|
|
|
if expected == 200:
|
|
|
|
assert response.data == serializers.UploadSerializer(upload).data
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("privacy_level", ["me", "instance"])
|
|
|
|
def test_music_upload_detail_private_approved_follow(
|
|
|
|
factories, api_client, authenticated_actor, privacy_level
|
|
|
|
):
|
|
|
|
upload = factories["music.Upload"](
|
|
|
|
library__privacy_level=privacy_level,
|
|
|
|
library__actor__local=True,
|
|
|
|
import_status="finished",
|
|
|
|
)
|
|
|
|
factories["federation.LibraryFollow"](
|
|
|
|
actor=authenticated_actor, target=upload.library, approved=True
|
|
|
|
)
|
|
|
|
url = reverse("federation:music:uploads-detail", kwargs={"uuid": upload.uuid})
|
|
|
|
response = api_client.get(url)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|