moviewyrm/bookwyrm/views/inbox.py

96 lines
3.3 KiB
Python
Raw Normal View History

2021-02-16 00:26:48 +00:00
''' incoming activities '''
import json
from urllib.parse import urldefrag
from django.http import HttpResponse
from django.http import HttpResponseBadRequest, HttpResponseNotFound
2021-02-17 02:07:57 +00:00
from django.utils.decorators import method_decorator
2021-02-16 00:26:48 +00:00
from django.views import View
2021-02-17 02:07:57 +00:00
from django.views.decorators.csrf import csrf_exempt
2021-02-16 00:26:48 +00:00
import requests
from bookwyrm import activitypub, models
2021-02-16 02:47:08 +00:00
from bookwyrm.tasks import app
2021-02-16 00:26:48 +00:00
from bookwyrm.signatures import Signature
2021-02-17 02:07:57 +00:00
@method_decorator(csrf_exempt, name='dispatch')
2021-02-16 00:26:48 +00:00
# pylint: disable=no-self-use
class Inbox(View):
''' requests sent by outside servers'''
def post(self, request, username=None):
''' only works as POST request '''
# first let's do some basic checks to see if this is legible
if username:
try:
models.User.objects.get(localname=username)
except models.User.DoesNotExist:
return HttpResponseNotFound()
# is it valid json? does it at least vaguely resemble an activity?
try:
2021-02-16 01:23:17 +00:00
activity_json = json.loads(request.body)
except json.decoder.JSONDecodeError:
2021-02-16 00:26:48 +00:00
return HttpResponseBadRequest()
# verify the signature
if not has_valid_signature(request, activity_json):
if activity_json['type'] == 'Delete':
# Pretend that unauth'd deletes succeed. Auth may be failing
# because the resource or owner of the resource might have
# been deleted.
return HttpResponse()
return HttpResponse(status=401)
2021-02-16 02:47:08 +00:00
# just some quick smell tests before we try to parse the json
if not 'object' in activity_json or \
not 'type' in activity_json or \
not activity_json['type'] in activitypub.activity_objects:
2021-02-16 00:26:48 +00:00
return HttpResponseNotFound()
2021-02-16 03:41:22 +00:00
activity_task.delay(activity_json)
2021-02-16 00:26:48 +00:00
return HttpResponse()
2021-02-16 02:47:08 +00:00
@app.task
def activity_task(activity_json):
''' do something with this json we think is legit '''
# lets see if the activitypub module can make sense of this json
try:
activity = activitypub.parse(activity_json)
except activitypub.ActivitySerializerError:
2021-02-17 02:07:57 +00:00
return
2021-02-16 02:47:08 +00:00
# cool that worked, now we should do the action described by the type
# (create, update, delete, etc)
activity.action()
2021-02-16 00:26:48 +00:00
def has_valid_signature(request, activity):
''' verify incoming signature '''
try:
signature = Signature.parse(request)
key_actor = urldefrag(signature.key_id).url
if key_actor != activity.get('actor'):
raise ValueError("Wrong actor created signature.")
remote_user = activitypub.resolve_remote_id(
key_actor, model=models.User)
2021-02-16 00:26:48 +00:00
if not remote_user:
return False
try:
signature.verify(remote_user.key_pair.public_key, request)
except ValueError:
old_key = remote_user.key_pair.public_key
remote_user = activitypub.resolve_remote_id(
remote_user.remote_id, model=models.User, refresh=True
2021-02-16 00:26:48 +00:00
)
if remote_user.key_pair.public_key == old_key:
raise # Key unchanged.
signature.verify(remote_user.key_pair.public_key, request)
except (ValueError, requests.exceptions.HTTPError):
return False
return True