diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py
index 064e94a..f2c1fa4 100644
--- a/tests/test_activitypub.py
+++ b/tests/test_activitypub.py
@@ -323,7 +323,7 @@ class ActivityPubTest(TestCase):
'type': 'Person',
'id': 'http://bf/fake/user.com/ap',
'preferredUsername': 'user.com',
- 'url': 'http://localhost/r/fake://user.com',
+ 'url': 'http://localhost/r/user.com',
'summary': '',
'inbox': 'http://bf/fake/user.com/ap/inbox',
'outbox': 'http://bf/fake/user.com/ap/outbox',
@@ -1456,16 +1456,16 @@ class ActivityPubUtilsTest(TestCase):
'actor': {
'id': 'baj',
'preferredUsername': 'site',
- 'url': 'http://localhost/r/fake://site',
+ 'url': 'http://localhost/r/site',
},
'attributedTo': [{
'id': 'bar',
'preferredUsername': 'site',
- 'url': 'http://localhost/r/fake://site',
+ 'url': 'http://localhost/r/site',
}, {
'id': 'baz',
'preferredUsername': 'site',
- 'url': 'http://localhost/r/fake://site',
+ 'url': 'http://localhost/r/site',
}],
'to': [as2.PUBLIC_AUDIENCE],
}, activitypub.postprocess_as2({
diff --git a/tests/test_common.py b/tests/test_common.py
index 45f8abe..b4d25be 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -14,6 +14,7 @@ import common
from flask_app import app
from models import Object, User
import protocol
+from web import Web
class CommonTest(TestCase):
@@ -47,8 +48,10 @@ class CommonTest(TestCase):
common.pretty_link('http://foo'))
+ # current user's homepage gets converted to BF user page
+ g.user = Web(id='user.com')
self.assertEqual(
- '
user.com',
+ '
user.com',
common.pretty_link('https://user.com/'))
def test_redirect_wrap_empty(self):
diff --git a/tests/test_models.py b/tests/test_models.py
index 5d3dc36..d48c45a 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -219,10 +219,10 @@ class FollowerTest(TestCase):
def test_from_to_same_type_fails(self):
with self.assertRaises(AssertionError):
- Follower(from_=Web.key_for('foo'), to=Web.key_for('bar')).put()
+ Follower(from_=Web.key_for('foo.com'), to=Web.key_for('bar.com')).put()
with self.assertRaises(AssertionError):
- Follower.get_or_create(from_=Web(id='foo'), to=Web(id='bar'))
+ Follower.get_or_create(from_=Web(id='foo.com'), to=Web(id='bar.com'))
def test_get_or_create(self):
follower = Follower.get_or_create(from_=g.user, to=self.other_user)
diff --git a/tests/test_protocol.py b/tests/test_protocol.py
index 468610f..c02a189 100644
--- a/tests/test_protocol.py
+++ b/tests/test_protocol.py
@@ -115,7 +115,7 @@ class ProtocolTest(TestCase):
(None, None),
('', None),
('foo://bar', None),
- ('fake://foo', Fake),
+ ('fake:foo', Fake),
# TODO
# ('at://foo', ATProto),
('https://ap.brid.gy/foo/bar', ActivityPub),
diff --git a/tests/test_web.py b/tests/test_web.py
index 42dcf77..bc8f583 100644
--- a/tests/test_web.py
+++ b/tests/test_web.py
@@ -1707,8 +1707,8 @@ class WebProtocolTest(TestCase):
for id in 'user.com', 'http://user.com', 'https://user.com/':
self.assertEqual(Web(id='user.com').key, Web.key_for(id))
- for bad in None, '', 'foo', 'https://foo/', 'foo bar':
- with self.assertRaises(AssertionError):
+ for bad in None, '', 'foo', 'https://foo/', 'foo bar', 'user.json':
+ with self.subTest(bad=bad), self.assertRaises(ValueError):
Web.key_for(bad)
def test_owns_id(self, *_):
diff --git a/tests/test_webfinger.py b/tests/test_webfinger.py
index b416c1d..03abbed 100644
--- a/tests/test_webfinger.py
+++ b/tests/test_webfinger.py
@@ -82,32 +82,32 @@ WEBFINGER_NO_HCARD = {
}],
}
WEBFINGER_FAKE = {
- 'subject': 'acct:user@fake',
- 'aliases': ['fake://user/'],
+ 'subject': 'acct:fake:user@fake',
+ 'aliases': ['fake:user'],
'links': [{
'rel': 'canonical_uri',
'type': 'text/html',
- 'href': 'fake://user/',
+ 'href': 'fake:user',
}, {
'rel': 'self',
'type': 'application/activity+json',
- 'href': 'http://bf/fake/user/ap',
+ 'href': 'http://bf/fake/fake:user/ap',
}, {
'rel': 'inbox',
'type': 'application/activity+json',
- 'href': 'http://bf/fake/user/ap/inbox',
+ 'href': 'http://bf/fake/fake:user/ap/inbox',
}, {
'rel': 'sharedInbox',
'type': 'application/activity+json',
'href': 'http://localhost/ap/sharedInbox',
}, {
'rel': 'http://ostatus.org/schema/1.0/subscribe',
- 'template': 'http://localhost/fa/user?url={uri}',
+ 'template': 'http://localhost/fa/fake:user?url={uri}',
}],
}
WEBFINGER_FAKE_FED_BRID_GY = copy.deepcopy(WEBFINGER_FAKE)
WEBFINGER_FAKE_FED_BRID_GY['links'][3]['href'] = 'https://fed.brid.gy/ap/sharedInbox'
-WEBFINGER_FAKE_FED_BRID_GY['links'][4]['template'] = 'https://fed.brid.gy/fa/user?url={uri}'
+WEBFINGER_FAKE_FED_BRID_GY['links'][4]['template'] = 'https://fed.brid.gy/fa/fake:user?url={uri}'
class HostMetaTest(TestCase):
@@ -162,33 +162,37 @@ class WebfingerTest(TestCase):
self.assert_equals(WEBFINGER, got.json)
def test_user_infer_protocol_from_resource_subdomain(self):
- got = self.client.get('/.well-known/webfinger?resource=acct:user@fake.brid.gy',
- headers={'Accept': 'application/json'})
+ got = self.client.get(
+ '/.well-known/webfinger?resource=acct:fake:user@fake.brid.gy',
+ headers={'Accept': 'application/json'})
self.assertEqual(200, got.status_code)
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
self.assert_equals(WEBFINGER_FAKE, got.json)
def test_user_infer_protocol_from_request_subdomain(self):
- self.make_user('user', cls=Fake)
- got = self.client.get('/.well-known/webfinger?resource=acct:user@user',
- base_url='https://fake.brid.gy/',
- headers={'Accept': 'application/json'})
+ self.make_user('fake:user', cls=Fake)
+ got = self.client.get(
+ '/.well-known/webfinger?resource=acct:user@fake:user',
+ base_url='https://fake.brid.gy/',
+ headers={'Accept': 'application/json'})
self.assertEqual(200, got.status_code)
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
self.assert_equals(WEBFINGER_FAKE_FED_BRID_GY, got.json)
def test_user_infer_protocol_resource_overrides_request(self):
- got = self.client.get('/.well-known/webfinger?resource=acct:user@fake.brid.gy',
- base_url='https://ap.brid.gy/',
- headers={'Accept': 'application/json'})
+ got = self.client.get(
+ '/.well-known/webfinger?resource=acct:fake:user@fake.brid.gy',
+ base_url='https://ap.brid.gy/',
+ headers={'Accept': 'application/json'})
self.assertEqual(200, got.status_code)
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
self.assert_equals(WEBFINGER_FAKE_FED_BRID_GY, got.json)
def test_urlencoded(self):
"""https://github.com/snarfed/bridgy-fed/issues/535"""
- got = self.client.get('/.well-known/webfinger?resource=acct%3Auser.com%40user.com',
- headers={'Accept': 'application/json'})
+ got = self.client.get(
+ '/.well-known/webfinger?resource=acct%3Auser.com%40user.com',
+ 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(WEBFINGER, got.json)
@@ -216,7 +220,6 @@ class WebfingerTest(TestCase):
# Mastodon requires this as of 3.3.0
# https://github.com/snarfed/bridgy-fed/issues/73
'acct:user.com@fed.brid.gy',
- 'acct:user.com@bridgy-federated.appspot.com',
'acct:user.com@localhost',
):
with self.subTest(resource=resource):
@@ -237,14 +240,31 @@ class WebfingerTest(TestCase):
}, got.json)
def test_missing_user(self):
- for bad in 'nope.com', 'nope.com@nope.com':
- got = self.client.get(f'/.well-known/webfinger?resource=acct:{bad}')
- self.assertEqual(404, got.status_code)
+ got = self.client.get(f'/.well-known/webfinger?resource=acct:nope.com@nope.com')
+ self.assertEqual(404, got.status_code)
+
+ got = self.client.get(f'/.well-known/webfinger?resource=acct:nope.com')
+ self.assertEqual(400, got.status_code)
+
+ def test_indirect_user_not_on_bridgy_fed_subdomain(self):
+ self.user.direct = False
+ self.user.put()
+ got = self.client.get(f'/.well-known/webfinger?resource=acct:user.com@user.com')
+ self.assertEqual(404, got.status_code)
+
+ def test_bad_id(self):
+ got = self.client.get(f'/.well-known/webfinger?resource=acct:nope@fa.brid.gy')
+ self.assertEqual(400, got.status_code, got.get_data(as_text=True))
+
+ got = self.client.get(f'/.well-known/webfinger?resource=acct:nope@nope',
+ base_url='https://fa.brid.gy/')
+ self.assertEqual(400, got.status_code, got.get_data(as_text=True))
def test_bad_tld(self):
self.make_user('user.json')
- got = self.client.get(f'/.well-known/webfinger?resource=acct:user.json@user.json')
- self.assertIn("like a domain but", html.unescape(got.get_data(as_text=True)))
+ got = self.client.get(f'/.well-known/webfinger?resource=acct:user.json@user.json',
+ base_url='https://web.brid.gy/')
+ self.assertEqual(400, got.status_code, got.get_data(as_text=True))
@patch('requests.get')
def test_fetch_create_user(self, mock_get):
diff --git a/tests/testutil.py b/tests/testutil.py
index 969f2c9..0463625 100644
--- a/tests/testutil.py
+++ b/tests/testutil.py
@@ -48,7 +48,7 @@ class Fake(User, protocol.Protocol):
fetched = []
def web_url(self):
- return f'fake://{self.key.id()}'
+ return self.key.id()
def ap_address(self):
return f'@{self.key.id()}@fake'
@@ -58,7 +58,10 @@ class Fake(User, protocol.Protocol):
@classmethod
def owns_id(cls, id):
- return id.startswith('fake://')
+ if id.startswith('nope'):
+ return False
+
+ return id.startswith('fake:') or id in cls.objects
@classmethod
def send(cls, obj, url, log_data=True):
@@ -89,7 +92,7 @@ class Fake(User, protocol.Protocol):
# expensive to generate
requests.post(f'http://{ndb_client.host}/reset')
with ndb_client.context():
- global_user = Fake.get_or_create('user')
+ global_user = Fake.get_or_create('fake:user')
# import other modules that register Flask handlers *after* Fake is defined
diff --git a/web.py b/web.py
index c6a980a..434fb11 100644
--- a/web.py
+++ b/web.py
@@ -39,6 +39,7 @@ CHAR_AFTER_SPACE = chr(ord(' ') + 1)
WWW_DOMAINS = frozenset((
'www.jvt.me',
))
+NON_TLDS = frozenset(('html', 'json', 'php', 'xml'))
class Web(User, Protocol):
@@ -211,8 +212,12 @@ class Web(User, Protocol):
Args:
id: str
+
+ Raises:
+ ValueError
"""
- assert id
+ if not id:
+ raise ValueError()
if util.is_web(id):
parsed = urlparse(id)
@@ -220,13 +225,16 @@ class Web(User, Protocol):
id = parsed.netloc
if re.match(common.DOMAIN_RE, id):
+ tld = id.split('.')[-1]
+ if tld in NON_TLDS:
+ raise ValueError(f"{id} looks like a domain but {tld} isn't a TLD")
return cls(id=id).key
- assert False, f'{id} is not a domain or usable home page URL'
+ raise ValueError(f'{id} is not a domain or usable home page URL')
@classmethod
def owns_id(cls, id):
- """Returns None if id is an http(s) URL, False otherwise.
+ """Returns None if id is a domain or http(s) URL, False otherwise.
All web pages are http(s) URLs, but not all http(s) URLs are web pages.
"""
@@ -238,8 +246,8 @@ class Web(User, Protocol):
if key:
user = key.get()
return True if user and user.has_redirects else None
- except AssertionError:
- pass
+ except ValueError as e:
+ logger.info(e)
return None if util.is_web(id) else False
diff --git a/webfinger.py b/webfinger.py
index 41970fe..9c26525 100644
--- a/webfinger.py
+++ b/webfinger.py
@@ -20,8 +20,6 @@ from models import User
from protocol import Protocol
from web import Web
-NON_TLDS = frozenset(('html', 'json', 'php', 'xml'))
-
SUBSCRIBE_LINK_REL = 'http://ostatus.org/schema/1.0/subscribe'
logger = logging.getLogger(__name__)
@@ -55,8 +53,8 @@ class Webfinger(flask_util.XrdOrJrd):
cls = None
try:
user, id = util.parse_acct_uri(resource)
- if id in common.DOMAINS:
- cls = Protocol.for_domain(id)
+ cls = Protocol.for_domain(id, fed=Web)
+ if cls:
id = user
allow_indirect=True
except ValueError:
@@ -66,12 +64,8 @@ class Webfinger(flask_util.XrdOrJrd):
cls = Protocol.for_request(fed=Web)
logger.info(f'Protocol {cls.__name__}, user id {id}')
-
- # if id is a domain, validate
- if re.match(common.DOMAIN_RE, id):
- tld = id.split('.')[-1]
- if tld in NON_TLDS:
- error(f"{id} looks like a domain but {tld} isn't a TLD", status=404)
+ if cls.owns_id(id) is False:
+ error(f'{id} is not a valid {cls.__name__} id')
# only allow indirect users if this id is "on" a brid.gy subdomain,
# eg user.com@bsky.brid.gy but not user.com@user.com
@@ -79,6 +73,8 @@ class Webfinger(flask_util.XrdOrJrd):
g.user = cls.get_or_create(id)
else:
g.user = cls.get_by_id(id)
+ if g.user and not g.user.direct:
+ error(f"{g.user.key} hasn't signed up yet", status=404)
if not g.user:
error(f'No {cls.LABEL} user found for {id}', status=404)