From c065c712f1e68a52b7b7e1a5ab040cf67cef0705 Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Thu, 25 Apr 2024 14:29:48 -0700 Subject: [PATCH] more id/handle translation tweaks for protocol bot users hopefully fixes Accept actor for responding to bot user follows in AP --- ids.py | 3 ++- protocol.py | 3 +-- tests/test_activitypub.py | 15 +++++++-------- tests/test_ids.py | 13 +++++++++++++ tests/test_integrations.py | 19 ++++++++++++++++++- tests/test_webfinger.py | 2 +- 6 files changed, 42 insertions(+), 13 deletions(-) diff --git a/ids.py b/ids.py index 1b50a1a..9bd6d9d 100644 --- a/ids.py +++ b/ids.py @@ -46,7 +46,8 @@ def web_ap_base_domain(user_domain): Returns: str: """ - if request.host in LOCAL_DOMAINS: + if (request.host in LOCAL_DOMAINS and + not (user_domain == PRIMARY_DOMAIN or user_domain in PROTOCOL_DOMAINS)): return request.host_url global _NON_WEB_SUBDOMAIN_SITES diff --git a/protocol.py b/protocol.py index 2053f6a..f87b437 100644 --- a/protocol.py +++ b/protocol.py @@ -618,8 +618,7 @@ class Protocol: def translate(elem, field, fn): elem[field] = as1.get_object(elem, field) - id = elem[field].get('id') - if id and util.domain_from_link(id) not in DOMAINS: + if id := elem[field].get('id'): from_cls = Protocol.for_id(id) # TODO: what if from_cls is None? relax translate_object_id, # make it a noop if we don't know enough about from/to? diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py index c77c8e6..5961aa3 100644 --- a/tests/test_activitypub.py +++ b/tests/test_activitypub.py @@ -507,7 +507,7 @@ class ActivityPubTest(TestCase): 'following', 'publicKey', 'publicKeyPem']) # skip _pre_put_hook since it doesn't allow internal domains - @patch.object(Web, '_pre_put_hook', new=lambda self: None) + # @patch.object(Web, '_pre_put_hook', new=lambda self: None) def test_instance_actor_fetch(self, *_): def reset_instance_actor(): activitypub._INSTANCE_ACTOR = testutil.global_user @@ -515,16 +515,15 @@ class ActivityPubTest(TestCase): actor_as2 = json_loads(util.read('fed.brid.gy.as2.json')) self.make_user(common.PRIMARY_DOMAIN, cls=Web, obj_as2=actor_as2, - obj_id='https://fed.brid.gy/') + obj_id='https://fed.brid.gy/', ap_subdomain='fed', + has_redirects=True) activitypub._INSTANCE_ACTOR = None - got = self.client.get(f'/{common.PRIMARY_DOMAIN}') + got = self.client.get(f'/fed.brid.gy', base_url='https://fed.brid.gy/') self.assertEqual(200, got.status_code) - self.assert_equals({ - **actor_as2, - 'id': 'http://localhost/fed.brid.gy', - }, got.json, ignore=['inbox', 'outbox', 'endpoints', 'followers', - 'following', 'publicKey', 'publicKeyPem']) + self.assert_equals(actor_as2, got.json, + ignore=['inbox', 'outbox', 'endpoints', 'followers', + 'following', 'publicKey', 'publicKeyPem']) def test_individual_inbox_no_user(self, mock_head, mock_get, mock_post): self.user.key.delete() diff --git a/tests/test_ids.py b/tests/test_ids.py index a03de2d..d917df3 100644 --- a/tests/test_ids.py +++ b/tests/test_ids.py @@ -11,6 +11,11 @@ from web import Web class IdsTest(TestCase): + def setUp(self): + super().setUp() + Web(id='bsky.brid.gy', ap_subdomain='bsky', has_redirects=True).put() + Web(id='fed.brid.gy', ap_subdomain='fed', has_redirects=True).put() + def test_translate_user_id(self): Web(id='user.com', copies=[Target(uri='did:plc:123', protocol='atproto')]).put() @@ -34,21 +39,25 @@ class IdsTest(TestCase): (ActivityPub, 'https://bsky.app/profile/user.com', ATProto, 'did:plc:123'), (ActivityPub, 'https://bsky.app/profile/did:plc:123', ATProto, 'did:plc:123'), + (ATProto, 'did:plc:456', ATProto, 'did:plc:456'), # copies (ATProto, 'did:plc:123', Web, 'user.com'), (ATProto, 'did:plc:456', ActivityPub, 'https://inst/user'), (ATProto, 'did:plc:789', Fake, 'fake:user'), + # no copies (ATProto, 'did:plc:x', Web, 'https://bsky.brid.gy/web/did:plc:x'), (ATProto, 'did:plc:x', ActivityPub, 'https://bsky.brid.gy/ap/did:plc:x'), (ATProto, 'did:plc:x', Fake, 'fake:u:did:plc:x'), (ATProto, 'https://bsky.app/profile/user.com', ATProto, 'did:plc:123'), (ATProto, 'https://bsky.app/profile/did:plc:123', ATProto, 'did:plc:123'), + (Fake, 'fake:user', ActivityPub, 'https://fa.brid.gy/ap/fake:user'), (Fake, 'fake:user', ATProto, 'did:plc:789'), (Fake, 'fake:user', Fake, 'fake:user'), (Fake, 'fake:user', Web, 'https://fa.brid.gy/web/fake:user'), + (Web, 'user.com', ActivityPub, 'http://localhost/user.com'), (Web, 'https://user.com/', ActivityPub, 'http://localhost/user.com'), (Web, 'user.com', ATProto, 'did:plc:123'), @@ -58,6 +67,10 @@ class IdsTest(TestCase): (Web, 'user.com', Fake, 'fake:u:user.com'), (Web, 'user.com', Web, 'user.com'), (Web, 'https://user.com/', Web, 'user.com'), + + # instance actor / protocol bot users + (Web, 'fed.brid.gy', ActivityPub, 'https://fed.brid.gy/fed.brid.gy'), + (Web, 'bsky.brid.gy', ActivityPub, 'https://bsky.brid.gy/bsky.brid.gy'), ]: with self.subTest(from_=from_.LABEL, to=to.LABEL): self.assertEqual(expected, translate_user_id( diff --git a/tests/test_integrations.py b/tests/test_integrations.py index b0ee594..3740eef 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -9,6 +9,8 @@ from granary import as2 from granary.tests.test_bluesky import ACTOR_PROFILE_BSKY, POST_BSKY from oauth_dropins.webutil.flask_util import NoContent from oauth_dropins.webutil.testutil import requests_response +from oauth_dropins.webutil import util +from oauth_dropins.webutil.util import json_dumps, json_loads from activitypub import ActivityPub import app @@ -361,7 +363,7 @@ class IntegrationTests(TestCase): @patch('requests.post', return_value=requests_response('OK')) # create DID @patch('requests.get') - def test_activitypub_follow_bsky_bot_user_enables_protocol(self, mock_get, _): + def test_activitypub_follow_bsky_bot_user_enables_protocol(self, mock_get, mock_post): """AP follow of @bsky.brid.gy@bsky.brid.gy bridges the account into BLuesky. ActivityPub user @alice@inst , https://inst/alice @@ -402,6 +404,21 @@ class IntegrationTests(TestCase): self.assertEqual(['app.bsky.actor.profile'], list(records.keys())) self.assertEqual(['self'], list(records['app.bsky.actor.profile'].keys())) + args, kwargs = mock_post.call_args_list[1] + self.assert_equals(('http://inst/inbox',), args) + self.assert_equals({ + 'type': 'Accept', + 'id': 'http://localhost/r/bsky.brid.gy/followers#accept-http://inst/follow', + 'actor': 'https://bsky.brid.gy/bsky.brid.gy', + 'object': { + 'actor': 'https://inst/alice', + 'id': 'http://inst/follow', + 'url': 'https://inst/alice#followed-bsky.brid.gy', + 'type': 'Follow', + 'object': 'https://bsky.brid.gy/bsky.brid.gy', + }, + }, json_loads(kwargs['data']), ignore=['to', '@context']) + util.d(mock_post.calls) @patch('requests.post') @patch('requests.get') diff --git a/tests/test_webfinger.py b/tests/test_webfinger.py index 9e9e37d..59e6b0f 100644 --- a/tests/test_webfinger.py +++ b/tests/test_webfinger.py @@ -366,7 +366,7 @@ class WebfingerTest(TestCase): self.assertEqual('acct:bsky.brid.gy@bsky.brid.gy', got.json['subject']) self.assertEqual(['https://bsky.brid.gy/'], got.json['aliases']) self.assertIn({ - 'href': 'http://localhost/bsky.brid.gy', + 'href': 'https://bsky.brid.gy/bsky.brid.gy', 'rel': 'self', 'type': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', }, got.json['links'])