kopia lustrzana https://github.com/snarfed/bridgy-fed
rodzic
5f82b98e51
commit
6333b7126a
|
@ -1,11 +1,83 @@
|
|||
# coding=utf-8
|
||||
"""Unit tests for webfinger.py."""
|
||||
import copy
|
||||
import html
|
||||
from unittest.mock import patch
|
||||
import urllib.parse
|
||||
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
|
||||
import common
|
||||
from models import User
|
||||
from . import testutil
|
||||
from .test_webmention import ACTOR_HTML
|
||||
|
||||
WEBFINGER = {
|
||||
'subject': 'acct:user.com@user.com',
|
||||
'aliases': [
|
||||
'https://user.com/about-me',
|
||||
'https://user.com/',
|
||||
],
|
||||
'links': [{
|
||||
'rel': 'http://webfinger.net/rel/profile-page',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/about-me',
|
||||
}, {
|
||||
'rel': 'http://webfinger.net/rel/profile-page',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/',
|
||||
}, {
|
||||
'rel': 'http://webfinger.net/rel/avatar',
|
||||
'href': 'https://user.com/me.jpg',
|
||||
}, {
|
||||
'rel': 'canonical_uri',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/about-me',
|
||||
}, {
|
||||
'rel': 'self',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com',
|
||||
}, {
|
||||
'rel': 'inbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com/inbox'
|
||||
}, {
|
||||
'rel': 'sharedInbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/inbox',
|
||||
}, {
|
||||
'rel': 'http://ostatus.org/schema/1.0/subscribe',
|
||||
'template': 'http://localhost/user/user.com?url={uri}',
|
||||
}],
|
||||
}
|
||||
WEBFINGER_NO_HCARD = {
|
||||
'subject': 'acct:user.com@user.com',
|
||||
'aliases': ['https://user.com/'],
|
||||
'links': [{
|
||||
'rel': 'http://webfinger.net/rel/profile-page',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/',
|
||||
}, {
|
||||
'rel': 'canonical_uri',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/',
|
||||
}, {
|
||||
'rel': 'self',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com',
|
||||
}, {
|
||||
'rel': 'inbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com/inbox',
|
||||
}, {
|
||||
'rel': 'sharedInbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/inbox',
|
||||
}, {
|
||||
'rel': 'http://ostatus.org/schema/1.0/subscribe',
|
||||
'template': 'http://localhost/user/user.com?url={uri}',
|
||||
}],
|
||||
}
|
||||
|
||||
|
||||
class HostMetaTest(testutil.TestCase):
|
||||
|
@ -46,50 +118,12 @@ class WebfingerTest(testutil.TestCase):
|
|||
}
|
||||
self.user = self.make_user('user.com', has_hcard=True, actor_as2=self.actor_as2)
|
||||
self.user.put()
|
||||
self.expected_webfinger = {
|
||||
'subject': 'acct:user.com@user.com',
|
||||
'aliases': [
|
||||
'https://user.com/about-me',
|
||||
'https://user.com/',
|
||||
],
|
||||
'links': [{
|
||||
'rel': 'http://webfinger.net/rel/profile-page',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/about-me',
|
||||
}, {
|
||||
'rel': 'http://webfinger.net/rel/profile-page',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/',
|
||||
}, {
|
||||
'rel': 'http://webfinger.net/rel/avatar',
|
||||
'href': 'https://user.com/me.jpg',
|
||||
}, {
|
||||
'rel': 'canonical_uri',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/about-me',
|
||||
}, {
|
||||
'rel': 'self',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com',
|
||||
}, {
|
||||
'rel': 'inbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com/inbox'
|
||||
}, {
|
||||
'rel': 'sharedInbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/inbox',
|
||||
}, {
|
||||
'rel': 'http://ostatus.org/schema/1.0/subscribe',
|
||||
'template': 'http://localhost/user/user.com?url={uri}',
|
||||
}],
|
||||
}
|
||||
|
||||
def test_user(self):
|
||||
got = self.client.get('/acct:user.com', headers={'Accept': 'application/json'})
|
||||
self.assertEqual(200, got.status_code)
|
||||
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
|
||||
self.assert_equals(self.expected_webfinger, got.json)
|
||||
self.assert_equals(WEBFINGER, got.json)
|
||||
|
||||
def test_user_no_hcard(self):
|
||||
self.user.has_hcard = False
|
||||
|
@ -98,34 +132,7 @@ class WebfingerTest(testutil.TestCase):
|
|||
|
||||
got = self.client.get('/acct:user.com')
|
||||
self.assertEqual(200, got.status_code)
|
||||
self.assert_equals({
|
||||
'subject': 'acct:user.com@user.com',
|
||||
'aliases': ['https://user.com/'],
|
||||
'links': [{
|
||||
'rel': 'http://webfinger.net/rel/profile-page',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/'
|
||||
}, {
|
||||
'rel': 'canonical_uri',
|
||||
'type': 'text/html',
|
||||
'href': 'https://user.com/'
|
||||
}, {
|
||||
'rel': 'self',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com'
|
||||
}, {
|
||||
'rel': 'inbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/user.com/inbox'
|
||||
}, {
|
||||
'rel': 'sharedInbox',
|
||||
'type': 'application/activity+json',
|
||||
'href': 'http://localhost/inbox'
|
||||
}, {
|
||||
'rel': 'http://ostatus.org/schema/1.0/subscribe',
|
||||
'template': 'http://localhost/user/user.com?url={uri}',
|
||||
}]
|
||||
}, got.json)
|
||||
self.assert_equals(WEBFINGER_NO_HCARD, got.json)
|
||||
|
||||
def test_user_bad_tld(self):
|
||||
got = self.client.get('/acct:foo.json')
|
||||
|
@ -133,7 +140,7 @@ class WebfingerTest(testutil.TestCase):
|
|||
self.assertIn("doesn't look like a domain",
|
||||
html.unescape(got.get_data(as_text=True)))
|
||||
|
||||
def test_user_not_found(self):
|
||||
def test_missing_user(self):
|
||||
got = self.client.get('/acct:nope.com', headers={'Accept': 'application/json'})
|
||||
self.assertEqual(404, got.status_code)
|
||||
|
||||
|
@ -147,7 +154,7 @@ class WebfingerTest(testutil.TestCase):
|
|||
got = self.client.get(url, headers={'Accept': 'application/json'})
|
||||
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
||||
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
|
||||
self.assert_equals(self.expected_webfinger, got.json)
|
||||
self.assert_equals(WEBFINGER, got.json)
|
||||
|
||||
def test_webfinger_custom_username(self):
|
||||
self.user.actor_as2 = {
|
||||
|
@ -160,22 +167,15 @@ class WebfingerTest(testutil.TestCase):
|
|||
}
|
||||
self.user.put()
|
||||
|
||||
self.expected_webfinger.update({
|
||||
'subject': 'acct:customuser@user.com',
|
||||
'aliases': [
|
||||
'https://user.com/about-me',
|
||||
'acct:notthisuser@boop.org',
|
||||
'acct:customuser@user.com',
|
||||
'https://user.com/',
|
||||
],
|
||||
})
|
||||
|
||||
for resource in (
|
||||
'customuser@user.com',
|
||||
'acct:customuser@user.com',
|
||||
'user.com',
|
||||
'user.com@user.com',
|
||||
'http://user.com/',
|
||||
'https://user.com/',
|
||||
'acct:user.com@user.com',
|
||||
'acct:@user.com@user.com',
|
||||
# Mastodon requires this as of 3.3.0
|
||||
# https://github.com/snarfed/bridgy-fed/issues/73
|
||||
'acct:user.com@fed.brid.gy',
|
||||
|
@ -188,7 +188,34 @@ class WebfingerTest(testutil.TestCase):
|
|||
got = self.client.get(url, headers={'Accept': 'application/json'})
|
||||
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
||||
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
|
||||
self.assert_equals(self.expected_webfinger, got.json)
|
||||
self.assert_equals({
|
||||
**WEBFINGER,
|
||||
'subject': 'acct:customuser@user.com',
|
||||
'aliases': [
|
||||
'https://user.com/about-me',
|
||||
'acct:notthisuser@boop.org',
|
||||
'acct:customuser@user.com',
|
||||
'https://user.com/',
|
||||
],
|
||||
}, got.json)
|
||||
|
||||
def test_webfinger_missing_user(self):
|
||||
got = self.client.get('/acct:nope.com', headers={'Accept': 'application/json'})
|
||||
self.assertEqual(404, got.status_code)
|
||||
|
||||
@patch('requests.get')
|
||||
def test_webfinger_external_user_fetch(self, mock_get):
|
||||
self.user.key.delete()
|
||||
mock_get.return_value = requests_response(ACTOR_HTML)
|
||||
|
||||
expected = copy.deepcopy(WEBFINGER_NO_HCARD)
|
||||
expected['subject'] = 'acct:user.com@localhost'
|
||||
expected['links'][2]['href'] = 'http://localhost/r/https://user.com/'
|
||||
|
||||
got = self.client.get('/.well-known/webfinger?resource=acct:user.com@fed.brid.gy',
|
||||
headers={'Accept': 'application/json'})
|
||||
self.assertEqual(200, got.status_code)
|
||||
self.assertEqual(expected, got.json)
|
||||
|
||||
def test_webfinger_fed_brid_gy(self):
|
||||
got = self.client.get('/.well-known/webfinger?resource=http://localhost/')
|
||||
|
|
35
webfinger.py
35
webfinger.py
|
@ -17,6 +17,7 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
|
|||
from flask_app import app, cache
|
||||
import common
|
||||
from models import User
|
||||
from webmention import Webmention
|
||||
|
||||
NON_TLDS = frozenset(('html', 'json', 'php', 'xml'))
|
||||
|
||||
|
@ -32,28 +33,44 @@ class Actor(flask_util.XrdOrJrd):
|
|||
def template_prefix(self):
|
||||
return 'webfinger_user'
|
||||
|
||||
def template_vars(self, domain=None):
|
||||
def template_vars(self, domain=None, external=False):
|
||||
"""
|
||||
Args:
|
||||
domain: str, user domain
|
||||
external: bool, whether this may be an external user, ie without a
|
||||
stored :class:`User`
|
||||
"""
|
||||
logger.debug(f'Headers: {list(request.headers.items())}')
|
||||
|
||||
if domain.split('.')[-1] in NON_TLDS:
|
||||
error(f"{domain} doesn't look like a domain", status=404)
|
||||
|
||||
g.user = User.get_by_id(domain)
|
||||
if not g.user:
|
||||
error(f'No user for {domain}', status=404)
|
||||
if g.user:
|
||||
actor = g.user.to_as1() or {}
|
||||
homepage = g.user.homepage
|
||||
handle = g.user.address()
|
||||
actor_id = g.user.actor_id()
|
||||
elif external:
|
||||
g.external_user = homepage = f'https://{domain}/'
|
||||
obj = Webmention.load(g.external_user)
|
||||
actor = obj.as1
|
||||
handle = f'@{domain}@{request.host}'
|
||||
actor_id = common.redirect_wrap(homepage)
|
||||
else:
|
||||
error(f'No user or web site found for {domain}', status=404)
|
||||
|
||||
logger.info(f'Generating WebFinger data for {domain}')
|
||||
actor = g.user.to_as1() or {}
|
||||
logger.info(f'AS1 actor: {actor}')
|
||||
urls = util.dedupe_urls(util.get_list(actor, 'urls') +
|
||||
util.get_list(actor, 'url') +
|
||||
[g.user.homepage])
|
||||
[homepage])
|
||||
logger.info(f'URLs: {urls}')
|
||||
canonical_url = urls[0]
|
||||
|
||||
# generate webfinger content
|
||||
data = util.trim_nulls({
|
||||
'subject': 'acct:' + g.user.address().lstrip('@'),
|
||||
'subject': 'acct:' + handle.lstrip('@'),
|
||||
'aliases': urls,
|
||||
'links':
|
||||
[{
|
||||
|
@ -80,7 +97,7 @@ class Actor(flask_util.XrdOrJrd):
|
|||
# WARNING: in python 2 sometimes request.host_url lost port,
|
||||
# http://localhost:8080 would become just http://localhost. no
|
||||
# clue how or why. pay attention here if that happens again.
|
||||
'href': g.user.actor_id(),
|
||||
'href': actor_id,
|
||||
}, {
|
||||
# AP reads this and sharedInbox from the AS2 actor, not
|
||||
# webfinger, so strictly speaking, it's probably not needed here.
|
||||
|
@ -124,14 +141,16 @@ class Webfinger(Actor):
|
|||
if resource in ('', '/', f'acct:{host}', f'acct:@{host}'):
|
||||
error('Expected other domain, not fed.brid.gy')
|
||||
|
||||
external = False
|
||||
try:
|
||||
user, domain = util.parse_acct_uri(resource)
|
||||
if domain in common.DOMAINS:
|
||||
domain = user
|
||||
external = True
|
||||
except ValueError:
|
||||
domain = urllib.parse.urlparse(resource).netloc or resource
|
||||
|
||||
return super().template_vars(domain=domain)
|
||||
return super().template_vars(domain=domain, external=external)
|
||||
|
||||
|
||||
class HostMeta(flask_util.XrdOrJrd):
|
||||
|
|
Ładowanie…
Reference in New Issue