diff --git a/Pipfile b/Pipfile index 0981ea0..331f775 100644 --- a/Pipfile +++ b/Pipfile @@ -30,6 +30,7 @@ django-push = "*" pyyaml = "*" django-annoying = "*" django-shorturls = "*" +accept-types = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index bc1d7db..2dc8003 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3f9a75dcd0609cd38fc01d23b8f2e198e494f5ab7120e5bb8af08b909a16ea52" + "sha256": "c9835b77b96ade2202ff84521a2cda6a3c04679d448cc434a49f3ce07d6befcf" }, "host-environment-markers": { "implementation_name": "cpython", @@ -27,6 +27,12 @@ ] }, "default": { + "accept-types": { + "hashes": [ + "sha256:9ae86512bf3a3eaad6a2793617a34eb15b384593e6c28697bef9b15ac237017a" + ], + "version": "==0.3.0" + }, "beautifulsoup4": { "hashes": [ "sha256:7015e76bf32f1f574636c4288399a6de66ce08fb7b2457f628a8d70c0fbabb11", diff --git a/lemonauth/views/indie.py b/lemonauth/views/indie.py index d899a67..2dff96d 100644 --- a/lemonauth/views/indie.py +++ b/lemonauth/views/indie.py @@ -3,9 +3,11 @@ import mf2py from annoying.decorators import render_to from django.contrib.auth.decorators import login_required from django.http import HttpResponseForbidden, HttpResponseBadRequest +from django.http import JsonResponse from django.shortcuts import redirect from django.utils.decorators import method_decorator from django.views.generic import TemplateView +from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from lemoncurry import breadcrumbs, utils from urllib.parse import urlencode, urljoin, urlunparse, urlparse @@ -26,6 +28,7 @@ def canonical(url): return urlunparse((scheme, loc, path, params, q, fragment)) +@method_decorator(csrf_exempt, name='dispatch') class IndieView(TemplateView): template_name = 'lemonauth/indie.html' required_params = ('me', 'client_id', 'redirect_uri') @@ -68,6 +71,25 @@ class IndieView(TemplateView): return {'app': app, 'me': me, 'params': params, 'title': 'indieauth'} + def post(self, request): + post = request.POST.dict() + try: + code = IndieAuthCode.objects.get( + code=post['code'], + client_id=post['client_id'], + redirect_uri=post['redirect_uri'] + ) + except IndieAuthCode.DoesNotExist: + return HttpResponseForbidden( + 'invalid auth code {0}'.format(post['code']), + content_type='text/plain' + ) + code.delete() + return utils.choose_type(request, {'me': code.me}, { + 'application/json': JsonResponse, + 'application/x-www-form-urlencoded': utils.form_encoded_response, + }) + @login_required @require_POST diff --git a/lemoncurry/utils.py b/lemoncurry/utils.py index 59a3d73..eb96b29 100644 --- a/lemoncurry/utils.py +++ b/lemoncurry/utils.py @@ -1,7 +1,10 @@ import json +from accept_types import get_best_match from django.conf import settings +from django.http import HttpResponse from os.path import join from types import SimpleNamespace +from urllib.parse import urlencode cache = SimpleNamespace(package_json=None) @@ -20,3 +23,17 @@ def origin(request): def uri(request): return origin(request) + request.path + + +def choose_type(request, content, reps): + type = get_best_match(request.META.get('HTTP_ACCEPT'), reps.keys()) + if type: + return reps[type](content) + return HttpResponse(status=406) + + +def form_encoded_response(content): + return HttpResponse( + urlencode(content), + content_type='application/x-www-form-urlencoded' + )