kopia lustrzana https://github.com/snarfed/bridgy-fed
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
rodzic
ef44dae317
commit
ad0a942034
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
6
pages.py
6
pages.py
|
@ -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,
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"""
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue