in progress checkpoint for webfinger, halfway done

mastodon
Ryan Barrett 2017-08-19 09:24:00 -07:00
rodzic 5499e06a6b
commit a0e6d0c5d0
5 zmienionych plików z 192 dodań i 9 usunięć

Wyświetl plik

@ -10,6 +10,8 @@ builtins:
- remote_api: on
libraries:
- name: jinja2
version: latest
- name: lxml
version: latest
- name: pycrypto
@ -57,10 +59,14 @@ handlers:
script: webmention.app
secure: always
- url: /[^/]+/?(inbox)?
- url: /[^@/]+/?(inbox)?
script: activitypub.app
secure: always
- url: /(acct:)?@[^/]+/?
script: webfinger.app
secure: always
skip_files:
- ^(.*/)?.*\.py[co]
- ^(.*/)?.*/RCS/.*

Wyświetl plik

@ -0,0 +1,47 @@
{
"subject": "{{ uri }}",
"aliases": ["{{ urls|join('\", \"') }}"],
{% if magic_public_key %}
"magic_keys": [{"value": "{{ magic_public_key }}"}],
{% endif %}
"links": [
{% for url in urls %}{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "{{ url }}"
},{% endfor %}
{% if magic_public_key %}
{
"rel": "magic-public-key",
"href": "{{ magic_public_key }}"
},
{% endif %}
{% for avatar in avatars %}{
"rel": "http://webfinger.net/rel/avatar",
"href": "{{ avatar }}"
}{% endfor %}
{% if poco_url %}
{
"rel": "http://portablecontacts.net/spec/1.0",
"href": "{{ poco_url }}"
},
{% endif %}
{% if activitystreams_url %}
{
"rel": "http://ns.opensocial.org/2008/opensocial/activitystreams",
"href": "{{ activitystreams_url }}"
},
{
"rel": "http://activitystrea.ms/spec/1.0",
"href": "{{ activitystreams_url }}"
}
{% endif %}
]
}

Wyświetl plik

@ -0,0 +1,37 @@
<?xml version='1.0' encoding='UTF-8'?>
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
<Subject>{{ uri }}</Subject>
<Alias>{{ urls[0] }}</Alias>
{% for url in urls %}
<Link rel='http://webfinger.net/rel/profile-page' type='text/html'
href='{{ url }}' />
{% endfor %}
{% if magic_public_key %}
<Property xmlns:mk="http://salmon-protocol.org/ns/magic-key"
type="http://salmon-protocol.org/ns/magic-key">
{{ magic_public_key }}
</Property>
<Link rel='magic-public-key'
href='{{ magic_public_key }}' />
{% endif %}
{% for avatar in avatars %}
<Link rel='http://webfinger.net/rel/avatar'
href='{{ avatar }}' />
{% endfor %}
{% if poco_url %}
<Link rel='http://portablecontacts.net/spec/1.0'
href='{{ poco_url }}' />
{% endif %}
{% if activitystreams_url %}
<Link rel='http://ns.opensocial.org/2008/opensocial/activitystreams'
href='{{ activitystreams_url }}' />
<Link rel='http://activitystrea.ms/spec/1.0'
href='{{ activitystreams_url }}' />
{% endif %}
</XRD>

Wyświetl plik

@ -5,18 +5,31 @@ TODO: test error handling
"""
import json
import unittest
import urllib
from google.appengine.datastore import datastore_stub_util
from google.appengine.ext import testbed
import mock
import requests
import webfinger
from webfinger import app
import common
class WebFingerTest(unittest.TestCase):
maxDiff = None
# TODO: unify with test_models
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
hrd_policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=.5)
self.testbed.init_datastore_v3_stub(consistency_policy=hrd_policy)
self.testbed.init_memcache_stub()
def tearDown(self):
self.testbed.deactivate()
def test_host_meta_handler_xrd(self):
got = app.get_response('/.well-known/host-meta')
self.assertEquals(200, got.status_int)
@ -37,3 +50,64 @@ class WebFingerTest(unittest.TestCase):
self.assertEquals('application/json; charset=utf-8',
got.headers['Content-Type'])
self.assertTrue(got.body.startswith('{'), got.body)
@mock.patch('requests.get')
def test_user_handler(self, mock_get):
html = u"""
<body>
<a class="h-card" rel="me" href="/about-me">
<img class="u-photo" src="/me.jpg" />
Mrs. Foo
</a>
</body>
"""
resp = requests.Response()
resp.status_code = 200
resp._text = html
resp._content = html.encode('utf-8')
resp.encoding = 'utf-8'
resp.url = 'https://foo.com/'
mock_get.return_value = resp
got = app.get_response('/@foo.com?format=json')
self.assertEquals(200, got.status_int)
self.assertEquals('application/json; charset=utf-8',
got.headers['Content-Type'])
print got.body
self.assertEquals({
'subject': 'acct:@foo.com',
'aliases': [
'https://foo.com/about-me',
'https://foo.com/',
],
'magic_keys': [{
'rel': 'magic-public-key',
'href': 'data:application/magic-public-key,RSA. ...',
}],
'links': [{
'rel': 'http://webfinger.net/rel/profile-page',
'type': 'text/html',
'href': 'https://foo.com/about-me'
}, {
'rel': 'http://webfinger.net/rel/avatar',
'href': 'https://foo.com/me.jpg'
}, {
'rel': 'salmon',
'href': 'http://localhost/salmon/23507'
}, {
'rel': 'magic-public-key',
'href': 'data:application/magic-public-key,RSA. ...'
# TODO
# }, {
# 'rel': 'http://schemas.google.com/g/2010#updates-from',
# 'type': 'application/atom+xml',
# 'href': 'https://mastodon.technology/users/snarfed.atom'
# }, {
# 'rel': 'self',
# 'type': 'application/activity+json',
# 'href': 'https://mastodon.technology/users/snarfed'
# }, {
# 'rel': 'http://ostatus.org/schema/1.0/subscribe',
# 'template': 'https://mastodon.technology/authorize_follow?acct={uri}'
}]
}, json.loads(got.body))

Wyświetl plik

@ -16,15 +16,34 @@ from oauth_dropins.webutil import handlers, util
import webapp2
import common
import models
class UserHandler(webapp2.RequestHandler):
"""TODO"""
class UserHandler(handlers.XrdOrJrdHandler):
"""Serves /@[DOMAIN], fetches its mf2, converts to WebFinger, and serves."""
def get(self, username, domain):
pass
def template_prefix(self):
return 'templates/webfinger_user'
def template_vars(self, domain):
# TODO: unify with activitypub
url = 'https://%s/' % domain
resp = common.requests_get(url)
mf2 = mf2py.parse(resp.text, url=resp.url)
logging.info('Parsed mf2 for %s: %s', resp.url, json.dumps(mf2, indent=2))
hcard = mf2util.representative_hcard(mf2, resp.url)
logging.info('Representative h-card: %s', json.dumps(hcard, indent=2))
key = models.MagicKey.get_or_create('@%s' % domain)
props = hcard['properties']
return {
'urls': props['url'],
'avatars': props['photo'],
'magic_public_key': key.href(),
}
app = webapp2.WSGIApplication([
(r'/(?:acct)?([^@/])@%s/?' % common.DOMAIN_RE, UserHandler),
(r'/(?:acct)?@%s/?' % common.DOMAIN_RE, UserHandler),
] + handlers.HOST_META_ROUTES, debug=appengine_config.DEBUG)