From 46c2224a4f07f0dfa9f65094caf3ba186374c569 Mon Sep 17 00:00:00 2001 From: Danielle McLean Date: Fri, 11 May 2018 13:23:47 +1000 Subject: [PATCH] Replace the previous WebFinger implementation with a fairly simple forwarder to Bridgy Fed, so that Bridgy Fed will work eventually --- users/models.py | 6 ++- wellknowns/views/webfinger.py | 81 +++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/users/models.py b/users/models.py index f6fd500..607a41c 100644 --- a/users/models.py +++ b/users/models.py @@ -84,7 +84,11 @@ class User(ModelMeta, AbstractUser): return '{0} {1}'.format(self.first_name, self.last_name) def get_absolute_url(self): - return self.url + return self.absolute_url + + @property + def absolute_url(self): + return self.full_url @property def full_url(self): diff --git a/wellknowns/views/webfinger.py b/wellknowns/views/webfinger.py index ba3f0f2..05684a9 100644 --- a/wellknowns/views/webfinger.py +++ b/wellknowns/views/webfinger.py @@ -1,50 +1,59 @@ -from django.http import HttpResponseBadRequest, HttpResponseNotFound -from django.http import JsonResponse +from django.http import HttpResponseBadRequest, HttpResponseRedirect +from urllib.parse import urlencode, urlparse + from users.models import User -from django.shortcuts import get_object_or_404 -from urllib.parse import urlparse, urljoin -from lemoncurry.utils import origin AVATAR = 'http://webfinger.net/rel/avatar' PROFILE_PAGE = 'http://webfinger.net/rel/profile-page' +BRIDGY_FED = 'https://fed.brid.gy/.well-known/webfinger' + + +def https_resource_matching(resource): + """ + Takes a `urllib.parse.urlparse` tuple representing a WebFinger resource and + translates ``mailto:`` and ``xmpp:`` resources to an equivalent ``https:`` + resource, if a user with matching email or XMPP address exists locally. + Will throw `User.DoesNotExist` if no such user exists. + """ + if resource.scheme == 'mailto': + query = {'email': resource.path} + else: + query = {'xmpp': resource.path} + return User.objects.get(**query).absolute_url def webfinger(request): + """ + A thin wrapper around Bridgy Fed's implementation of WebFinger. + + In most cases, this view simply redirects to the same endpoint at Bridgy. + However, Bridgy does not support the ``mailto:`` and ``xmpp:`` resource + schemes - quite reasonably, since there's no possible way to discover the + ``acct:`` they go with! - so resources with those schemes are translated + locally into an ``https:`` URL representing the same person, and *then* + redirected to Bridgy. + + Additionally, WebFinger requests with a missing or malformed resource will + be rejected immediately rather than passed on to Bridgy. + + Note that the translation step will only be applied if there exists a + :model:`users.User` with matching email or XMPP address. Otherwise, the + original resource will be preserved in the redirect - and likely fail to + find anything at Bridgy's end either. + """ if 'resource' not in request.GET: return HttpResponseBadRequest('resource parameter missing') + resource = request.GET['resource'] try: - resource = urlparse(request.GET['resource']) + res = urlparse(resource) except ValueError: return HttpResponseBadRequest('resource parameter malformed') - if resource.scheme in ('mailto', 'acct', 'xmpp'): - user = get_object_or_404(User, email=resource.path) - elif resource.scheme in ('http', 'https'): - user = get_object_or_404(User, pk=1) - else: - return HttpResponseNotFound('resource not found on this server') + if res.scheme in ('mailto', 'xmpp'): + try: + resource = https_resource_matching(res) + except User.DoesNotExist: + pass - base = origin(request) - - def link(rel, href, type): - return {'rel': rel, 'href': urljoin(base, href), 'type': type} - - key_links = tuple(link( - rel='pgpkey', - href=key.file.url, - type='application/pgp-keys', - ) for key in user.keys.all()) - - info = { - 'subject': 'acct:' + user.email, - 'aliases': ( - urljoin(base, user.url), - 'mailto:' + user.email, - 'xmpp:' + user.xmpp, - ), - 'links': ( - link(rel=AVATAR, href=user.avatar.url, type='image/png'), - link(rel=PROFILE_PAGE, href=user.url, type='text/html'), - ) + key_links, - } - return JsonResponse(info, content_type='application/jrd+json') + query = urlencode({'resource': resource}) + return HttpResponseRedirect(BRIDGY_FED + '?' + query)