switch webfinger and AP actors to mostly protocol subdomains

except Web, it still mostly serves on fed.brid.gy for backcompat, don't want to change existing Web users' AP actor ids.
pull/653/head
Ryan Barrett 2023-09-26 16:43:48 -07:00
rodzic ef44dae317
commit ad0a942034
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
9 zmienionych plików z 81 dodań i 51 usunięć

Wyświetl plik

@ -703,13 +703,24 @@ def postprocess_as2_actor(actor, wrap=True):
return actor
# source protocol in subdomain.
# WARNING: the user page handler in pages.py overrides this for fediverse
# addresses with leading @ character. be careful when changing this route!
@app.get(f'/ap/<handle_or_id>', defaults={'protocol': None})
# source protocol in path; primarily for localhost testing
@app.get(f'/ap/<any({",".join(PROTOCOLS)}):protocol>/<handle_or_id>')
# special case Web users without /ap/web/ prefix, for backward compatibility
@app.get(f'/<regex("{DOMAIN_RE}"):handle_or_id>', defaults={'protocol': 'web'})
@flask_util.cached(cache, CACHE_TIME)
def actor(protocol, handle_or_id):
"""Serves a user's AS2 actor from the datastore."""
cls = PROTOCOLS[protocol]
if protocol:
cls = PROTOCOLS[protocol]
else:
cls = Protocol.for_request(fed=None)
if not cls:
error(f"Couldn't determine protocol")
if cls.owns_id(handle_or_id) is False:
if cls.owns_handle(handle_or_id) is False:
@ -739,7 +750,7 @@ def actor(protocol, handle_or_id):
'following': g.user.ap_actor('following'),
'followers': g.user.ap_actor('followers'),
'endpoints': {
'sharedInbox': host_url('/ap/sharedInbox'),
'sharedInbox': cls.subdomain_url('/ap/sharedInbox'),
},
# add this if we ever change the Web actor ids to be /web/[id]
# 'alsoKnownAs': [host_url(id)],
@ -764,6 +775,8 @@ def actor(protocol, handle_or_id):
}
# note that this path overlaps with the /ap/<handle_or_id> actor route above,
# but doesn't collide because this is POST and that one is GET.
@app.post('/ap/sharedInbox')
@app.post(f'/ap/<any({",".join(PROTOCOLS)}):protocol>/<regex("{DOMAIN_RE}"):domain>/inbox')
# special case Web users without /ap/web/ prefix, for backward compatibility

Wyświetl plik

@ -403,7 +403,7 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
def ap_actor(self, rest=None):
"""Returns this user's ActivityPub/AS2 actor id.
Eg ``https://fed.brid.gy/ap/atproto/foo.com`.
Eg ``https://atproto.brid.gy/ap/foo.com`.
May be overridden by subclasses.
@ -414,7 +414,7 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
str
"""
# must match the URL route for activitypub.actor()
url = common.host_url(f'/ap/{self.ABBREV}/{self.key.id()}')
url = self.subdomain_url(f'/ap/{self.key.id()}')
if rest:
url += f'/{rest.lstrip("/")}'
return url

Wyświetl plik

@ -91,7 +91,13 @@ def web_user_redirects(**kwargs):
@app.get(f'/<any({",".join(PROTOCOLS)}):protocol>/<id>')
# WARNING: this overrides the /ap/... actor URL route in activitypub.py, *only*
# for handles with leading @ character. be careful when changing this route!
@app.get(f'/ap/@<id>', defaults={'protocol': 'ap'})
def user(protocol, id):
if protocol == 'ap' and not id.startswith('@'):
id = '@' + id
load_user(protocol, id)
query = Object.query(OR(Object.users == g.user.key,

Wyświetl plik

@ -56,7 +56,7 @@ ACTOR_BASE = {
'following': 'http://localhost/user.com/following',
'followers': 'http://localhost/user.com/followers',
'endpoints': {
'sharedInbox': 'http://localhost/ap/sharedInbox',
'sharedInbox': 'https://web.brid.gy/ap/sharedInbox',
},
'publicKey': {
'id': 'http://localhost/user.com#key',
@ -74,20 +74,23 @@ ACTOR_BASE_FULL = {
}],
}
ACTOR_FAKE = {
'@context': ['https://w3id.org/security/v1'],
'@context': [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1',
],
'type': 'Person',
'id': 'http://localhost/ap/fa/fake:user',
'id': 'https://fa.brid.gy/ap/fake:user',
'url': 'https://fa.brid.gy/r/fake:user',
'inbox': 'https://fa.brid.gy/ap/fake:user/inbox',
'outbox': 'https://fa.brid.gy/ap/fake:user/outbox',
'following': 'https://fa.brid.gy/ap/fake:user/following',
'followers': 'https://fa.brid.gy/ap/fake:user/followers',
'endpoints': {'sharedInbox': 'https://fa.brid.gy/ap/sharedInbox'},
'preferredUsername': 'fake:user',
'url': 'http://localhost/r/fake:user',
'summary': '',
'inbox': 'http://localhost/ap/fa/fake:user/inbox',
'outbox': 'http://localhost/ap/fa/fake:user/outbox',
'following': 'http://localhost/ap/fa/fake:user/following',
'followers': 'http://localhost/ap/fa/fake:user/followers',
'endpoints': {'sharedInbox': 'http://localhost/ap/sharedInbox'},
'publicKey': {
'id': 'http://localhost/fake#key',
'owner': 'http://localhost/fake',
'id': 'https://fa.brid.gy/fake#key',
'owner': 'https://fa.brid.gy/fake',
'publicKeyPem': 'populated in setUp()',
},
}
@ -298,9 +301,8 @@ class ActivityPubTest(TestCase):
self.swentel_key = ndb.Key(ActivityPub, 'https://mas.to/users/swentel')
self.masto_actor_key = ndb.Key(ActivityPub, 'https://mas.to/actor')
ACTOR_BASE['publicKey']['publicKeyPem'] = \
ACTOR_FAKE['publicKey']['publicKeyPem'] = \
self.user.public_pem().decode()
for obj in ACTOR_BASE, ACTOR_FAKE:
obj['publicKey']['publicKeyPem'] = self.user.public_pem().decode()
self.key_id_obj = Object(id='http://my/key/id', as2={
**ACTOR,
@ -336,17 +338,19 @@ class ActivityPubTest(TestCase):
return self.client.post(path, data=body, headers=self.sign(path, body))
def test_actor_fake(self, *_):
self.make_user('fake:user', cls=Fake, obj_as2={
'type': 'Person',
'id': 'fake:user',
})
got = self.client.get('/ap/fake/fake:user')
self.make_user('fake:user', cls=Fake)
got = self.client.get('/ap/fake:user', base_url='https://fa.brid.gy/')
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
type = got.headers['Content-Type']
self.assertTrue(type.startswith(as2.CONTENT_TYPE), type)
self.assertEqual(ACTOR_FAKE, got.json)
def test_actor_fake_protocol_subdomain(self, *_):
self.make_user('fake:user', cls=Fake)
got = self.client.get('/ap/fake:user', base_url='https://fa.brid.gy/')
self.assertEqual(200, got.status_code)
self.assertEqual(ACTOR_FAKE, got.json)
def test_actor_web(self, *_):
"""Web users are special cased to drop the /web/ prefix."""
got = self.client.get('/user.com')
@ -393,7 +397,7 @@ class ActivityPubTest(TestCase):
def test_actor_handle_existing_user(self, _, __, ___):
self.make_user('fake:user', cls=Fake, obj_as2=ACTOR)
got = self.client.get('/ap/fake/fake:handle:user')
got = self.client.get('/ap/fake:handle:user', base_url='https://fa.brid.gy/')
self.assertEqual(200, got.status_code)
self.assert_equals({
**ACTOR,
@ -402,7 +406,7 @@ class ActivityPubTest(TestCase):
def test_actor_handle_new_user(self, _, __, ___):
Fake.fetchable['fake:user'] = as2.to_as1(ACTOR)
got = self.client.get('/ap/fake/fake:handle:user')
got = self.client.get('/ap/fake:handle:user', base_url='https://fa.brid.gy/')
self.assertEqual(200, got.status_code)
self.assert_equals({
**ACTOR,
@ -557,6 +561,9 @@ class ActivityPubTest(TestCase):
def test_shared_inbox_create_obj(self, *mocks):
self._test_inbox_create_obj('/inbox', *mocks)
def test_ap_sharedInbox_create_obj(self, *mocks):
self._test_inbox_create_obj('/ap/sharedInbox', *mocks)
def _test_inbox_create_obj(self, path, mock_head, mock_get, mock_post):
swentel = self.make_user('https://mas.to/users/swentel', cls=ActivityPub)
Follower.get_or_create(to=swentel, from_=self.user)
@ -1593,21 +1600,21 @@ class ActivityPubUtilsTest(TestCase):
'id': 'baj',
'preferredUsername': 'site',
'url': 'http://localhost/r/site',
'inbox': 'http://localhost/ap/fa/site/inbox',
'outbox': 'http://localhost/ap/fa/site/outbox',
'inbox': 'https://fa.brid.gy/ap/site/inbox',
'outbox': 'https://fa.brid.gy/ap/site/outbox',
},
'attributedTo': [{
'id': 'bar',
'preferredUsername': 'site',
'url': 'http://localhost/r/site',
'inbox': 'http://localhost/ap/fa/site/inbox',
'outbox': 'http://localhost/ap/fa/site/outbox',
'inbox': 'https://fa.brid.gy/ap/site/inbox',
'outbox': 'https://fa.brid.gy/ap/site/outbox',
}, {
'id': 'baz',
'preferredUsername': 'site',
'url': 'http://localhost/r/site',
'inbox': 'http://localhost/ap/fa/site/inbox',
'outbox': 'http://localhost/ap/fa/site/outbox',
'inbox': 'https://fa.brid.gy/ap/site/inbox',
'outbox': 'https://fa.brid.gy/ap/site/outbox',
}],
'to': [as2.PUBLIC_AUDIENCE],
}, postprocess_as2({

Wyświetl plik

@ -140,11 +140,12 @@ class UserTest(TestCase):
self.assertEqual('fake:handle:user', user.handle_as('fake'))
self.assertEqual('@fake:handle:user@fa.brid.gy', user.handle_as('ap'))
def test_ap_actor(self):
@patch('requests.get', return_value=requests_response(DID_DOC))
def test_ap_actor(self, mock_get):
user = self.make_user('did:plc:abc', cls=ATProto)
self.assertEqual('http://localhost/ap/atproto/did:plc:abc',
self.assertEqual('https://atproto.brid.gy/ap/did:plc:abc',
user.ap_actor())
self.assertEqual('http://localhost/ap/atproto/did:plc:abc/foo',
self.assertEqual('https://atproto.brid.gy/ap/did:plc:abc/foo',
user.ap_actor(rest='foo'))
def test_load_multi(self):

Wyświetl plik

@ -32,7 +32,7 @@ class PagesTest(TestCase):
self.user = self.make_user('user.com')
def test_user(self):
got = self.client.get('/web/user.com')
got = self.client.get('/web/user.com', base_url='https://fed.brid.gy/')
self.assert_equals(200, got.status_code)
def test_user_fake(self):

Wyświetl plik

@ -1031,7 +1031,7 @@ class ProtocolReceiveTest(TestCase):
delivered=['fake:user:target'],
)
accept_id = 'http://localhost/ap/fa/fake:user/followers#accept-fake:follow'
accept_id = 'https://fa.brid.gy/ap/fake:user/followers#accept-fake:follow'
accept_as1 = {
'id': accept_id,
'objectType': 'activity',
@ -1233,10 +1233,10 @@ class ProtocolReceiveTest(TestCase):
Fake.receive_as1(follow_as1)
(bob_obj, bob_target), (eve_obj, eve_target) = Fake.sent
self.assertEqual('http://localhost/ap/fa/http://x.com/bob/followers#accept-http://x.com/follow',
self.assertEqual('https://fa.brid.gy/ap/http://x.com/bob/followers#accept-http://x.com/follow',
bob_obj.key.id())
self.assertEqual('http://x.com/alice:target', bob_target)
self.assertEqual('http://localhost/ap/fa/http://x.com/eve/followers#accept-http://x.com/follow',
self.assertEqual('https://fa.brid.gy/ap/http://x.com/eve/followers#accept-http://x.com/follow',
eve_obj.key.id())
self.assertEqual('http://x.com/alice:target', eve_target)

Wyświetl plik

@ -46,7 +46,7 @@ WEBFINGER = {
}, {
'rel': 'sharedInbox',
'type': 'application/activity+json',
'href': 'http://localhost/ap/sharedInbox',
'href': 'https://web.brid.gy/ap/sharedInbox',
}, {
'rel': 'http://ostatus.org/schema/1.0/subscribe',
'template': 'http://localhost/web/user.com?url={uri}',
@ -74,7 +74,7 @@ WEBFINGER_NO_HCARD = {
}, {
'rel': 'sharedInbox',
'type': 'application/activity+json',
'href': 'http://localhost/ap/sharedInbox',
'href': 'https://web.brid.gy/ap/sharedInbox',
}, {
'rel': 'http://ostatus.org/schema/1.0/subscribe',
'template': 'http://localhost/web/user.com?url={uri}',
@ -98,17 +98,18 @@ WEBFINGER_FAKE = {
}, {
'rel': 'sharedInbox',
'type': 'application/activity+json',
'href': 'http://localhost/ap/sharedInbox',
'href': 'https://web.brid.gy/ap/sharedInbox',
}, {
'rel': 'http://ostatus.org/schema/1.0/subscribe',
'template': 'http://localhost/fa/fake:user?url={uri}',
}],
}
WEBFINGER_FAKE_FED_BRID_GY = copy.deepcopy(WEBFINGER_FAKE)
for link in WEBFINGER_FAKE_FED_BRID_GY['links']:
WEBFINGER_FAKE_FA_BRID_GY = copy.deepcopy(WEBFINGER_FAKE)
for link in WEBFINGER_FAKE_FA_BRID_GY['links']:
if 'href' in link:
link['href'] = link['href'].replace('http://localhost', 'https://fed.brid.gy')
WEBFINGER_FAKE_FED_BRID_GY['links'][4]['template'] = 'https://fed.brid.gy/fa/fake:user?url={uri}'
link['href'] = link['href'].replace('http://localhost/ap/fa', 'https://fa.brid.gy/ap')
WEBFINGER_FAKE_FA_BRID_GY['links'][3]['href'] = 'https://fa.brid.gy/ap/sharedInbox'
WEBFINGER_FAKE_FA_BRID_GY['links'][4]['template'] = 'https://fed.brid.gy/fa/fake:user?url={uri}'
class HostMetaTest(TestCase):
@ -163,10 +164,11 @@ class WebfingerTest(TestCase):
def test_user_infer_protocol_from_resource_subdomain(self):
got = self.client.get(
'/.well-known/webfinger?resource=acct:fake:handle:user@fake.brid.gy',
base_url='https://fed.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)
self.assert_equals(WEBFINGER_FAKE_FA_BRID_GY, got.json)
def test_user_infer_protocol_from_request_subdomain(self):
self.make_user('fake:user', cls=Fake)
@ -176,7 +178,7 @@ class WebfingerTest(TestCase):
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)
self.assert_equals(WEBFINGER_FAKE_FA_BRID_GY, got.json)
def test_user_infer_protocol_resource_overrides_request(self):
got = self.client.get(
@ -185,14 +187,15 @@ class WebfingerTest(TestCase):
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)
self.assert_equals(WEBFINGER_FAKE_FA_BRID_GY, got.json)
def test_handle(self):
got = self.client.get(
'/.well-known/webfinger?resource=acct:fake:handle:user@fake.brid.gy',
base_url='https://fed.brid.gy/',
headers={'Accept': 'application/json'})
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
self.assert_equals(WEBFINGER_FAKE, got.json)
self.assert_equals(WEBFINGER_FAKE_FA_BRID_GY, got.json)
def test_urlencoded(self):
"""https://github.com/snarfed/bridgy-fed/issues/535"""

Wyświetl plik

@ -134,7 +134,7 @@ class Webfinger(flask_util.XrdOrJrd):
# https://www.w3.org/TR/activitypub/#sharedInbox
'rel': 'sharedInbox',
'type': as2.CONTENT_TYPE,
'href': common.host_url('/ap/sharedInbox'),
'href': cls.subdomain_url('/ap/sharedInbox'),
},
# remote follow