Ryan Barrett 2023-06-15 10:52:11 -07:00
rodzic b8be57bae7
commit 60a4a2bb9f
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
9 zmienionych plików z 82 dodań i 52 usunięć

Wyświetl plik

@ -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({

Wyświetl plik

@ -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(
'<a class="h-card u-author" href="/fa/user.com"><img src="" class="profile"> user.com</a>',
'<a class="h-card u-author" href="/web/user.com"><img src="" class="profile"> user.com</a>',
common.pretty_link('https://user.com/'))
def test_redirect_wrap_empty(self):

Wyświetl plik

@ -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)

Wyświetl plik

@ -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),

Wyświetl plik

@ -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, *_):

Wyświetl plik

@ -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):

Wyświetl plik

@ -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

18
web.py
Wyświetl plik

@ -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

Wyświetl plik

@ -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)