@ -11,6 +11,7 @@ from .follow import follow, unfollow
|
||||
from .follow import accept_follow_request, delete_follow_request
|
||||
from .goal import Goal
|
||||
from .import_data import Import, ImportStatus
|
||||
from .inbox import Inbox
|
||||
from .interaction import Favorite, Unfavorite, Boost, Unboost
|
||||
from .invite import ManageInvites, Invite
|
||||
from .landing import About, Home, Discover
|
||||
|
@ -1,5 +1,6 @@
|
||||
''' views for actions you can take in the application '''
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import IntegrityError
|
||||
from django.http import HttpResponseBadRequest
|
||||
from django.shortcuts import redirect
|
||||
from django.views.decorators.http import require_POST
|
||||
@ -17,13 +18,14 @@ def follow(request):
|
||||
except models.User.DoesNotExist:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
rel, _ = models.UserFollowRequest.objects.get_or_create(
|
||||
user_subject=request.user,
|
||||
user_object=to_follow,
|
||||
)
|
||||
try:
|
||||
models.UserFollowRequest.objects.create(
|
||||
user_subject=request.user,
|
||||
user_object=to_follow,
|
||||
)
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
if to_follow.local and not to_follow.manually_approves_followers:
|
||||
rel.accept()
|
||||
return redirect(to_follow.local_path)
|
||||
|
||||
|
||||
@ -40,9 +42,7 @@ def unfollow(request):
|
||||
models.UserFollows.objects.get(
|
||||
user_subject=request.user,
|
||||
user_object=to_unfollow
|
||||
)
|
||||
|
||||
to_unfollow.followers.remove(request.user)
|
||||
).delete()
|
||||
return redirect(to_unfollow.local_path)
|
||||
|
||||
|
||||
|
@ -192,7 +192,7 @@ def handle_remote_webfinger(query):
|
||||
if link.get('rel') == 'self':
|
||||
try:
|
||||
user = activitypub.resolve_remote_id(
|
||||
models.User, link['href']
|
||||
link['href'], model=models.User
|
||||
)
|
||||
except KeyError:
|
||||
return None
|
||||
|
95
bookwyrm/views/inbox.py
Normal file
95
bookwyrm/views/inbox.py
Normal file
@ -0,0 +1,95 @@
|
||||
''' incoming activities '''
|
||||
import json
|
||||
from urllib.parse import urldefrag
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
import requests
|
||||
|
||||
from bookwyrm import activitypub, models
|
||||
from bookwyrm.tasks import app
|
||||
from bookwyrm.signatures import Signature
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
# 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:
|
||||
activity_json = json.loads(request.body)
|
||||
except json.decoder.JSONDecodeError:
|
||||
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)
|
||||
|
||||
# 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:
|
||||
return HttpResponseNotFound()
|
||||
|
||||
activity_task.delay(activity_json)
|
||||
return HttpResponse()
|
||||
|
||||
|
||||
@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:
|
||||
return
|
||||
|
||||
# cool that worked, now we should do the action described by the type
|
||||
# (create, update, delete, etc)
|
||||
activity.action()
|
||||
|
||||
|
||||
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)
|
||||
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
|
||||
)
|
||||
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
|
@ -1,6 +1,5 @@
|
||||
''' tagging views'''
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseNotFound
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
@ -16,12 +15,11 @@ class Tag(View):
|
||||
''' tag page '''
|
||||
def get(self, request, tag_id):
|
||||
''' see books related to a tag '''
|
||||
tag_obj = models.Tag.objects.filter(identifier=tag_id).first()
|
||||
if not tag_obj:
|
||||
return HttpResponseNotFound()
|
||||
tag_obj = get_object_or_404(models.Tag, identifier=tag_id)
|
||||
|
||||
if is_api_request(request):
|
||||
return ActivitypubResponse(tag_obj.to_activity(**request.GET))
|
||||
return ActivitypubResponse(
|
||||
tag_obj.to_activity(**request.GET))
|
||||
|
||||
books = models.Edition.objects.filter(
|
||||
usertag__tag__identifier=tag_id
|
||||
|
Reference in New Issue
Block a user