2017-08-19 15:21:05 +00:00
|
|
|
"""Handles requests for WebFinger endpoints.
|
|
|
|
|
|
|
|
https://webfinger.net/
|
2017-08-27 18:53:48 +00:00
|
|
|
https://tools.ietf.org/html/rfc7033
|
2017-08-19 15:21:05 +00:00
|
|
|
|
|
|
|
Largely based on webfinger-unofficial/user.py.
|
2017-08-27 18:53:48 +00:00
|
|
|
|
|
|
|
TODO: test:
|
|
|
|
* /.well-known/webfinger
|
|
|
|
* acct: URI handling
|
|
|
|
* user URL that redirects
|
2017-08-19 15:21:05 +00:00
|
|
|
"""
|
|
|
|
import json
|
|
|
|
import logging
|
2017-08-27 18:53:48 +00:00
|
|
|
import urlparse
|
2017-08-19 15:21:05 +00:00
|
|
|
|
|
|
|
import appengine_config
|
|
|
|
|
|
|
|
from granary import microformats2
|
|
|
|
import mf2py
|
|
|
|
import mf2util
|
|
|
|
from oauth_dropins.webutil import handlers, util
|
|
|
|
import webapp2
|
|
|
|
|
|
|
|
import common
|
2017-08-19 16:24:00 +00:00
|
|
|
import models
|
2017-08-19 15:21:05 +00:00
|
|
|
|
|
|
|
|
2017-08-19 16:24:00 +00:00
|
|
|
class UserHandler(handlers.XrdOrJrdHandler):
|
2017-08-27 18:53:48 +00:00
|
|
|
"""Fetches a site's home page, converts its mf2 to WebFinger, and serves."""
|
2017-08-19 20:10:18 +00:00
|
|
|
JRD_TEMPLATE = False
|
2017-08-19 15:21:05 +00:00
|
|
|
|
2017-08-19 16:24:00 +00:00
|
|
|
def template_prefix(self):
|
|
|
|
return 'templates/webfinger_user'
|
|
|
|
|
2017-09-03 19:54:10 +00:00
|
|
|
def template_vars(self, username, domain):
|
2017-08-19 20:10:18 +00:00
|
|
|
url = 'http://%s/' % domain
|
2017-08-27 18:53:48 +00:00
|
|
|
|
|
|
|
# TODO: unify with activitypub
|
2017-08-19 16:24:00 +00:00
|
|
|
resp = common.requests_get(url)
|
|
|
|
mf2 = mf2py.parse(resp.text, url=resp.url)
|
2017-09-02 03:49:00 +00:00
|
|
|
# logging.debug('Parsed mf2 for %s: %s', resp.url, json.dumps(mf2, indent=2))
|
2017-08-19 16:24:00 +00:00
|
|
|
|
|
|
|
hcard = mf2util.representative_hcard(mf2, resp.url)
|
|
|
|
logging.info('Representative h-card: %s', json.dumps(hcard, indent=2))
|
2017-08-19 20:31:06 +00:00
|
|
|
if not hcard:
|
2017-08-27 18:53:48 +00:00
|
|
|
common.error(self, """\
|
2017-08-19 20:31:06 +00:00
|
|
|
Couldn't find a <a href="http://microformats.org/wiki/representative-hcard-parsing">\
|
|
|
|
representative h-card</a> on %s""" % resp.url)
|
2017-08-19 16:24:00 +00:00
|
|
|
|
2017-09-03 19:54:10 +00:00
|
|
|
acct = '%s@%s' % (username, domain)
|
|
|
|
key = models.MagicKey.get_or_create(acct)
|
2017-08-19 20:10:18 +00:00
|
|
|
props = hcard.get('properties', {})
|
2017-08-19 20:34:06 +00:00
|
|
|
urls = util.dedupe_urls(props.get('url', []) + [resp.url])
|
2017-09-03 19:35:18 +00:00
|
|
|
canonical_url = urls[0]
|
2017-08-19 20:10:18 +00:00
|
|
|
|
2017-09-02 03:49:00 +00:00
|
|
|
data = util.trim_nulls({
|
2017-09-03 19:54:10 +00:00
|
|
|
'subject': 'acct:' + acct,
|
2017-08-19 20:10:18 +00:00
|
|
|
'aliases': urls,
|
|
|
|
'magic_keys': [{'value': key.href()}],
|
2017-09-02 03:49:00 +00:00
|
|
|
'links': sum(([{
|
2017-08-19 20:10:18 +00:00
|
|
|
'rel': 'http://webfinger.net/rel/profile-page',
|
|
|
|
'type': 'text/html',
|
|
|
|
'href': url,
|
2017-09-02 03:49:00 +00:00
|
|
|
}] for url in urls), []) + [{
|
2017-08-19 20:10:18 +00:00
|
|
|
'rel': 'http://webfinger.net/rel/avatar',
|
|
|
|
'href': url,
|
2017-08-20 02:46:53 +00:00
|
|
|
} for url in props.get('photo', [])] + [{
|
2017-09-03 19:35:18 +00:00
|
|
|
'rel': 'canonical_uri',
|
|
|
|
'type': 'text/html',
|
|
|
|
'href': canonical_url,
|
|
|
|
}, {
|
2017-09-02 03:49:00 +00:00
|
|
|
'rel': 'http://schemas.google.com/g/2010#updates-from',
|
2017-09-03 19:35:18 +00:00
|
|
|
'type': common.ATOM_CONTENT_TYPE,
|
|
|
|
# TODO: feed discovery, fall back to granary
|
|
|
|
# TODO: hub
|
|
|
|
'href': 'https://granary-demo.appspot.com/url?input=html&output=atom&url=%s&hub=%s' % (resp.url, resp.url),
|
2017-09-02 03:49:00 +00:00
|
|
|
}, {
|
2017-08-19 20:10:18 +00:00
|
|
|
'rel': 'magic-public-key',
|
|
|
|
'href': key.href(),
|
2017-08-20 02:46:53 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'salmon',
|
2017-09-03 19:54:10 +00:00
|
|
|
'href': '%s/%s/salmon' % (self.request.host_url, acct),
|
2017-08-19 20:10:18 +00:00
|
|
|
}]
|
|
|
|
})
|
2017-09-02 03:49:00 +00:00
|
|
|
logging.info('Returning WebFinger data: %s', json.dumps(data, indent=2))
|
|
|
|
return data
|
2017-08-19 15:21:05 +00:00
|
|
|
|
|
|
|
|
2017-08-27 18:53:48 +00:00
|
|
|
class WebfingerHandler(UserHandler):
|
|
|
|
|
|
|
|
def is_jrd(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
def template_vars(self):
|
2017-09-03 22:44:01 +00:00
|
|
|
acct = util.get_required_param(self, 'resource')
|
|
|
|
try:
|
|
|
|
username, domain = util.parse_acct_uri(acct)
|
|
|
|
url = 'http://%s/' % domain
|
|
|
|
except ValueError:
|
2017-09-06 14:59:27 +00:00
|
|
|
# common.error(self, 'Invalid acct: URI %s' % acct)
|
|
|
|
username = 'ryan'
|
|
|
|
domain = 'snarfed.org'
|
2017-09-03 22:44:01 +00:00
|
|
|
if not username:
|
|
|
|
common.error(self, 'No username found in acct: URI %s' % acct)
|
|
|
|
|
|
|
|
return super(WebfingerHandler, self).template_vars(username, domain)
|
2017-08-27 18:53:48 +00:00
|
|
|
|
|
|
|
|
2017-08-19 15:21:05 +00:00
|
|
|
app = webapp2.WSGIApplication([
|
2017-09-03 19:54:10 +00:00
|
|
|
(r'/%s/?' % common.ACCT_RE, UserHandler),
|
2017-08-27 18:53:48 +00:00
|
|
|
('/.well-known/webfinger', WebfingerHandler),
|
2017-08-19 15:21:05 +00:00
|
|
|
] + handlers.HOST_META_ROUTES, debug=appengine_config.DEBUG)
|