2017-08-19 15:21:05 +00:00
|
|
|
# coding=utf-8
|
|
|
|
"""Unit tests for webfinger.py.
|
|
|
|
|
2021-03-12 22:30:24 +00:00
|
|
|
to test:
|
|
|
|
* user URL that redirects
|
|
|
|
* error handling
|
2017-08-19 15:21:05 +00:00
|
|
|
"""
|
2021-08-06 17:29:25 +00:00
|
|
|
import html
|
2019-12-26 06:20:57 +00:00
|
|
|
from unittest import mock
|
|
|
|
import urllib.parse
|
2017-08-19 15:21:05 +00:00
|
|
|
|
2017-08-23 15:14:51 +00:00
|
|
|
from oauth_dropins.webutil import util
|
2017-08-24 14:41:46 +00:00
|
|
|
from oauth_dropins.webutil.testutil import requests_response
|
2019-12-25 07:26:58 +00:00
|
|
|
from oauth_dropins.webutil.util import json_loads
|
2017-08-19 15:21:05 +00:00
|
|
|
import requests
|
|
|
|
|
2017-08-19 20:10:18 +00:00
|
|
|
import common
|
|
|
|
import models
|
2019-12-26 06:20:57 +00:00
|
|
|
from . import testutil
|
2017-08-19 15:21:05 +00:00
|
|
|
|
|
|
|
|
2020-12-30 18:26:48 +00:00
|
|
|
class WebfingerTest(testutil.TestCase):
|
2017-08-19 16:24:00 +00:00
|
|
|
|
2017-09-03 22:44:01 +00:00
|
|
|
def setUp(self):
|
2021-08-18 14:59:52 +00:00
|
|
|
super().setUp()
|
2021-07-11 20:39:19 +00:00
|
|
|
|
2017-09-03 22:44:01 +00:00
|
|
|
self.html = """
|
2018-10-12 02:19:56 +00:00
|
|
|
<body class="h-card">
|
|
|
|
<a class="u-url" rel="me" href="/about-me">
|
2017-08-19 16:24:00 +00:00
|
|
|
<img class="u-photo" src="/me.jpg" />
|
|
|
|
Mrs. ☕ Foo
|
|
|
|
</a>
|
|
|
|
</body>
|
2017-09-03 22:44:01 +00:00
|
|
|
"""
|
2022-11-16 06:00:28 +00:00
|
|
|
self.key = models.User.get_or_create('foo.com')
|
2017-09-03 22:44:01 +00:00
|
|
|
self.expected_webfinger = {
|
2021-03-12 22:30:24 +00:00
|
|
|
'subject': 'acct:foo.com@foo.com',
|
2017-08-19 16:24:00 +00:00
|
|
|
'aliases': [
|
2017-08-19 20:10:18 +00:00
|
|
|
'https://foo.com/about-me',
|
2017-08-19 20:34:06 +00:00
|
|
|
'https://foo.com/',
|
2017-08-19 16:24:00 +00:00
|
|
|
],
|
2017-09-03 22:44:01 +00:00
|
|
|
'magic_keys': [{'value': self.key.href()}],
|
2017-08-19 16:24:00 +00:00
|
|
|
'links': [{
|
|
|
|
'rel': 'http://webfinger.net/rel/profile-page',
|
|
|
|
'type': 'text/html',
|
2017-08-19 20:34:06 +00:00
|
|
|
'href': 'https://foo.com/about-me'
|
2017-08-19 16:24:00 +00:00
|
|
|
}, {
|
2017-08-19 20:10:18 +00:00
|
|
|
'rel': 'http://webfinger.net/rel/profile-page',
|
|
|
|
'type': 'text/html',
|
2017-08-19 20:34:06 +00:00
|
|
|
'href': 'https://foo.com/'
|
2017-08-20 02:46:53 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'http://webfinger.net/rel/avatar',
|
|
|
|
'href': 'https://foo.com/me.jpg'
|
2017-09-03 19:35:18 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'canonical_uri',
|
|
|
|
'type': 'text/html',
|
|
|
|
'href': 'https://foo.com/about-me'
|
2017-09-28 14:25:21 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'self',
|
|
|
|
'type': 'application/activity+json',
|
|
|
|
'href': 'http://localhost/foo.com'
|
|
|
|
}, {
|
|
|
|
'rel': 'inbox',
|
|
|
|
'type': 'application/activity+json',
|
|
|
|
'href': 'http://localhost/foo.com/inbox'
|
2022-11-16 18:09:24 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'sharedInbox',
|
|
|
|
'type': 'application/activity+json',
|
|
|
|
'href': 'http://localhost/inbox'
|
2017-09-03 19:35:18 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'http://schemas.google.com/g/2010#updates-from',
|
|
|
|
'type': 'application/atom+xml',
|
2019-12-26 06:20:57 +00:00
|
|
|
'href': 'https://granary.io/url?input=html&output=atom&url=https%3A%2F%2Ffoo.com%2F&hub=https%3A%2F%2Ffoo.com%2F',
|
2017-09-13 14:48:32 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'hub',
|
|
|
|
'href': 'https://bridgy-fed.superfeedr.com/'
|
2017-08-19 16:24:00 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'magic-public-key',
|
2017-09-03 22:44:01 +00:00
|
|
|
'href': self.key.href(),
|
2017-08-20 02:46:53 +00:00
|
|
|
}, {
|
|
|
|
'rel': 'salmon',
|
2017-09-12 14:31:50 +00:00
|
|
|
'href': 'http://localhost/foo.com/salmon'
|
2017-08-19 16:24:00 +00:00
|
|
|
}]
|
2017-09-03 22:44:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
def test_host_meta_handler_xrd(self):
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/.well-known/host-meta')
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code)
|
2019-12-26 06:20:57 +00:00
|
|
|
self.assertEqual('application/xrd+xml; charset=utf-8',
|
2017-09-03 22:44:01 +00:00
|
|
|
got.headers['Content-Type'])
|
2021-07-11 23:30:14 +00:00
|
|
|
body = got.get_data(as_text=True)
|
|
|
|
self.assertTrue(body.startswith('<?xml'), body)
|
2017-09-03 22:44:01 +00:00
|
|
|
|
|
|
|
def test_host_meta_handler_xrds(self):
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/.well-known/host-meta.xrds')
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code)
|
2021-07-11 23:50:44 +00:00
|
|
|
self.assertEqual('application/xrds+xml', got.headers['Content-Type'])
|
2021-07-11 23:30:14 +00:00
|
|
|
body = got.get_data(as_text=True)
|
|
|
|
self.assertTrue(body.startswith('<XRDS'), body)
|
2017-09-03 22:44:01 +00:00
|
|
|
|
|
|
|
def test_host_meta_handler_jrd(self):
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/.well-known/host-meta.json')
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code)
|
2021-07-11 23:30:14 +00:00
|
|
|
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
|
|
|
|
body = got.get_data(as_text=True)
|
|
|
|
self.assertTrue(body.startswith('{'), body)
|
2017-09-03 22:44:01 +00:00
|
|
|
|
|
|
|
@mock.patch('requests.get')
|
|
|
|
def test_user_handler(self, mock_get):
|
2021-07-11 20:39:19 +00:00
|
|
|
mock_get.return_value = requests_response(self.html, url='https://foo.com/')
|
2017-09-03 22:44:01 +00:00
|
|
|
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/acct:foo.com', headers={'Accept': 'application/json'})
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code)
|
2021-07-11 23:30:14 +00:00
|
|
|
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
|
2022-11-16 18:43:34 +00:00
|
|
|
self.assert_req(mock_get, 'https://foo.com/')
|
2017-09-03 22:44:01 +00:00
|
|
|
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(self.expected_webfinger, got.json)
|
2017-08-19 20:31:06 +00:00
|
|
|
|
2017-08-19 20:36:52 +00:00
|
|
|
# check that magic key is persistent
|
2021-08-18 14:59:52 +00:00
|
|
|
again = self.client.get('/acct:foo.com',
|
2021-07-11 23:30:14 +00:00
|
|
|
headers={'Accept': 'application/json'}).json
|
2019-12-26 06:20:57 +00:00
|
|
|
self.assertEqual(self.key.href(), again['magic_keys'][0]['value'])
|
2017-08-20 02:46:53 +00:00
|
|
|
|
|
|
|
links = {l['rel']: l['href'] for l in again['links']}
|
2019-12-26 06:20:57 +00:00
|
|
|
self.assertEqual(self.key.href(), links['magic-public-key'])
|
2017-08-19 20:36:52 +00:00
|
|
|
|
2017-09-13 14:05:30 +00:00
|
|
|
@mock.patch('requests.get')
|
|
|
|
def test_user_handler_with_atom_feed(self, mock_get):
|
|
|
|
html = """\
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<link rel="feed" href="/dont-use">
|
|
|
|
<link rel="alternate" type="application/rss+xml" href="/dont-use-either">
|
|
|
|
<link rel="alternate" type="application/atom+xml" href="/use-this">
|
|
|
|
</head>
|
|
|
|
""" + self.html
|
|
|
|
mock_get.return_value = requests_response(html, url = 'https://foo.com/')
|
|
|
|
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/acct:foo.com', headers={'Accept': 'application/json'})
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code)
|
2017-09-28 14:25:21 +00:00
|
|
|
self.assertIn({
|
2017-09-13 14:48:32 +00:00
|
|
|
'rel': 'http://schemas.google.com/g/2010#updates-from',
|
|
|
|
'type': 'application/atom+xml',
|
|
|
|
'href': 'https://foo.com/use-this',
|
2021-07-11 20:39:19 +00:00
|
|
|
}, got.json['links'])
|
2017-09-13 14:48:32 +00:00
|
|
|
|
|
|
|
@mock.patch('requests.get')
|
|
|
|
def test_user_handler_with_push_header(self, mock_get):
|
|
|
|
mock_get.return_value = requests_response(
|
|
|
|
self.html, url = 'https://foo.com/', headers={
|
|
|
|
'Link': 'badly formatted, '
|
|
|
|
"<xyz>; rel='foo',"
|
|
|
|
'<http://a.custom.hub/>; rel="hub"',
|
|
|
|
})
|
|
|
|
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/acct:foo.com', headers={'Accept': 'application/json'})
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code)
|
2017-09-28 14:25:21 +00:00
|
|
|
self.assertIn({
|
2017-09-13 14:48:32 +00:00
|
|
|
'rel': 'hub',
|
|
|
|
'href': 'http://a.custom.hub/',
|
2021-07-11 20:39:19 +00:00
|
|
|
}, got.json['links'])
|
2017-09-13 14:05:30 +00:00
|
|
|
|
2017-08-19 20:31:06 +00:00
|
|
|
@mock.patch('requests.get')
|
|
|
|
def test_user_handler_no_hcard(self, mock_get):
|
2017-08-24 14:41:46 +00:00
|
|
|
mock_get.return_value = requests_response("""
|
2017-08-19 20:31:06 +00:00
|
|
|
<body>
|
|
|
|
<div class="h-entry">
|
|
|
|
<p class="e-content">foo bar</p>
|
|
|
|
</div>
|
|
|
|
</body>
|
2017-08-24 14:41:46 +00:00
|
|
|
""")
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/acct:foo.com')
|
2022-11-16 18:43:34 +00:00
|
|
|
self.assert_req(mock_get, 'https://foo.com/')
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(400, got.status_code)
|
|
|
|
self.assertIn('representative h-card', got.get_data(as_text=True))
|
2017-09-03 22:44:01 +00:00
|
|
|
|
2019-04-19 14:59:44 +00:00
|
|
|
def test_user_handler_bad_tld(self):
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get('/acct:foo.json')
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(404, got.status_code)
|
2021-08-06 17:29:25 +00:00
|
|
|
self.assertIn("doesn't look like a domain",
|
|
|
|
html.unescape(got.get_data(as_text=True)))
|
2019-04-19 14:59:44 +00:00
|
|
|
|
2017-09-03 22:44:01 +00:00
|
|
|
@mock.patch('requests.get')
|
|
|
|
def test_webfinger_handler(self, mock_get):
|
2017-09-12 14:31:50 +00:00
|
|
|
mock_get.return_value = requests_response(self.html, url='https://foo.com/')
|
|
|
|
|
2017-10-26 15:07:29 +00:00
|
|
|
for resource in ('foo.com@foo.com', 'acct:foo.com@foo.com', 'xyz@foo.com',
|
2022-11-17 15:56:00 +00:00
|
|
|
'foo.com', 'http://foo.com/', 'https://foo.com/',
|
|
|
|
'http://localhost/foo.com'):
|
2019-12-26 06:20:57 +00:00
|
|
|
url = '/.well-known/webfinger?%s' % urllib.parse.urlencode(
|
2017-09-12 14:31:50 +00:00
|
|
|
{'resource': resource})
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get(url, headers={'Accept': 'application/json'})
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
2021-07-11 23:30:14 +00:00
|
|
|
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(self.expected_webfinger, got.json)
|
2018-04-12 13:25:39 +00:00
|
|
|
|
|
|
|
@mock.patch('requests.get')
|
|
|
|
def test_webfinger_handler_custom_username(self, mock_get):
|
|
|
|
self.html = """
|
|
|
|
<body class="h-card">
|
|
|
|
<a class="u-url" rel="me" href="/about-me">
|
|
|
|
<img class="u-photo" src="/me.jpg" />
|
|
|
|
Mrs. ☕ Foo
|
|
|
|
</a>
|
|
|
|
<a class="u-url" href="acct:notthisuser@boop.org"></a>
|
|
|
|
<a class="u-url" href="acct:customuser@foo.com"></a>
|
|
|
|
</body>
|
|
|
|
"""
|
2020-12-30 18:26:48 +00:00
|
|
|
self.expected_webfinger.update({
|
|
|
|
'subject': 'acct:customuser@foo.com',
|
|
|
|
'aliases': [
|
|
|
|
'https://foo.com/about-me',
|
|
|
|
'acct:notthisuser@boop.org',
|
|
|
|
'acct:customuser@foo.com',
|
|
|
|
'https://foo.com/',
|
|
|
|
],
|
|
|
|
})
|
2018-04-12 13:25:39 +00:00
|
|
|
mock_get.return_value = requests_response(self.html, url='https://foo.com/')
|
|
|
|
|
2020-12-30 18:26:48 +00:00
|
|
|
for resource in (
|
|
|
|
'customuser@foo.com',
|
|
|
|
'acct:customuser@foo.com',
|
|
|
|
'foo.com',
|
|
|
|
'http://foo.com/',
|
|
|
|
'https://foo.com/',
|
|
|
|
# Mastodon requires this as of 3.3.0
|
|
|
|
# https://github.com/snarfed/bridgy-fed/issues/73
|
|
|
|
# 'acct:foo.com@fed.brid.gy',
|
|
|
|
'acct:foo.com@fed.brid.gy',
|
|
|
|
'acct:foo.com@bridgy-federated.appspot.com',
|
|
|
|
'acct:foo.com@localhost',
|
|
|
|
):
|
2019-12-26 06:20:57 +00:00
|
|
|
url = '/.well-known/webfinger?%s' % urllib.parse.urlencode(
|
2018-04-12 13:25:39 +00:00
|
|
|
{'resource': resource})
|
2021-08-18 14:59:52 +00:00
|
|
|
got = self.client.get(url, headers={'Accept': 'application/json'})
|
2021-07-11 20:39:19 +00:00
|
|
|
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
2021-07-11 23:30:14 +00:00
|
|
|
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
|
|
|
|
self.assertEqual(self.expected_webfinger, got.json)
|