import json import requests from django.conf import settings from funkwhale_api.common import session from . import actors from . import models from . import serializers from . import signing from . import webfinger def scan_from_account_name(account_name): """ Given an account name such as library@test.library, will: 1. Perform the webfinger lookup 2. Perform the actor lookup 3. Perform the library's collection lookup and return corresponding data in a dictionary. """ data = {} try: username, domain = webfinger.clean_acct(account_name, ensure_local=False) except serializers.ValidationError: return {"webfinger": {"errors": ["Invalid account string"]}} system_library = actors.SYSTEM_ACTORS["library"].get_actor_instance() library = ( models.Library.objects.filter( actor__domain=domain, actor__preferred_username=username ) .select_related("actor") .first() ) data["local"] = {"following": False, "awaiting_approval": False} try: follow = models.Follow.objects.get( target__preferred_username=username, target__domain=username, actor=system_library, ) data["local"]["awaiting_approval"] = not bool(follow.approved) data["local"]["following"] = True except models.Follow.DoesNotExist: pass try: data["webfinger"] = webfinger.get_resource("acct:{}".format(account_name)) except requests.ConnectionError: return {"webfinger": {"errors": ["This webfinger resource is not reachable"]}} except requests.HTTPError as e: return { "webfinger": { "errors": [ "Error {} during webfinger request".format(e.response.status_code) ] } } except json.JSONDecodeError as e: return {"webfinger": {"errors": ["Could not process webfinger response"]}} try: data["actor"] = actors.get_actor_data(data["webfinger"]["actor_url"]) except requests.ConnectionError: data["actor"] = {"errors": ["This actor is not reachable"]} return data except requests.HTTPError as e: data["actor"] = { "errors": ["Error {} during actor request".format(e.response.status_code)] } return data serializer = serializers.LibraryActorSerializer(data=data["actor"]) if not serializer.is_valid(): data["actor"] = {"errors": ["Invalid ActivityPub actor"]} return data data["library"] = get_library_data(serializer.validated_data["library_url"]) return data def get_library_data(library_url): actor = actors.SYSTEM_ACTORS["library"].get_actor_instance() auth = signing.get_auth(actor.private_key, actor.private_key_id) try: response = session.get_session().get( library_url, auth=auth, timeout=5, verify=settings.EXTERNAL_REQUESTS_VERIFY_SSL, headers={"Content-Type": "application/activity+json"}, ) except requests.ConnectionError: return {"errors": ["This library is not reachable"]} scode = response.status_code if scode == 401: return {"errors": ["This library requires authentication"]} elif scode == 403: return {"errors": ["Permission denied while scanning library"]} elif scode >= 400: return {"errors": ["Error {} while fetching the library".format(scode)]} serializer = serializers.PaginatedCollectionSerializer(data=response.json()) if not serializer.is_valid(): return {"errors": ["Invalid ActivityPub response from remote library"]} return serializer.validated_data def get_library_page(library, page_url): actor = actors.SYSTEM_ACTORS["library"].get_actor_instance() auth = signing.get_auth(actor.private_key, actor.private_key_id) response = session.get_session().get( page_url, auth=auth, timeout=5, verify=settings.EXTERNAL_REQUESTS_VERIFY_SSL, headers={"Content-Type": "application/activity+json"}, ) serializer = serializers.CollectionPageSerializer( data=response.json(), context={"library": library, "item_serializer": serializers.AudioSerializer}, ) serializer.is_valid(raise_exception=True) return serializer.validated_data