2 changed files with 51 additions and 38 deletions
@ -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') |
||||
|
||||
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') |
||||
if res.scheme in ('mailto', 'xmpp'): |
||||
try: |
||||
resource = https_resource_matching(res) |
||||
except User.DoesNotExist: |
||||
pass |
||||
|
||||
query = urlencode({'resource': resource}) |
||||
return HttpResponseRedirect(BRIDGY_FED + '?' + query) |
||||
|
Loading…
Reference in new issue