From 53518a3e4de7068ca368192b61e40af530da09bf Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Wed, 9 Aug 2023 11:26:36 -0700 Subject: [PATCH] AP: handle Follow activities sent to shared inbox fixes https://console.cloud.google.com/errors/detail/CKCyj4Cml--vUw;time=P30D?project=bridgy-federated --- activitypub.py | 34 +++++++++++++++++++++++++--------- models.py | 1 + tests/test_activitypub.py | 20 ++++++++++++++++++-- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/activitypub.py b/activitypub.py index 84cdf69..e2060fb 100644 --- a/activitypub.py +++ b/activitypub.py @@ -735,15 +735,31 @@ def inbox(protocol=None, domain=None): actor_id = actor.get('id') logger.info(f'Got {type} from {actor_id}: {json_dumps(activity, indent=2)}') - # load user - if protocol and domain: - # receiving user - g.user = PROTOCOLS[protocol].get_or_create(domain, direct=False) - if not g.user.direct and actor_id: - # this is a deliberate interaction with an indirect receiving user; - # create a local AP User for the sending user - actor_obj = ActivityPub.load(actor_id) - ActivityPub.get_or_create(actor_id, direct=True, obj=actor_obj) + # load receiving user + obj_id = as1.get_object(redirect_unwrap(activity)).get('id') + receiving_proto = receiving_user_id = None + if protocol: + receiving_proto = PROTOCOLS[protocol] + elif type == 'Follow': + receiving_proto = Protocol.for_id(obj_id) + + if receiving_proto: + if domain: + assert receiving_proto is web.Web, 'https://github.com/snarfed/bridgy-fed/issues/611' + receiving_user_id = domain + else: + receiving_key = receiving_proto.key_for(obj_id) + if receiving_key: + receiving_user_id = receiving_key.id() + + if receiving_user_id: + g.user = receiving_proto.get_or_create(receiving_user_id, direct=False) + logger.info(f'Setting g.user to {g.user.key}') + if not g.user.direct and actor_id: + # this is a deliberate interaction with an indirect receiving user; + # create a local AP User for the sending user + actor_obj = ActivityPub.load(actor_id) + ActivityPub.get_or_create(actor_id, direct=True, obj=actor_obj) ActivityPub.verify_signature(activity) diff --git a/models.py b/models.py index 04781cf..1dcc8b7 100644 --- a/models.py +++ b/models.py @@ -232,6 +232,7 @@ class User(StringIdModel, metaclass=ProtocolUserMeta): def private_pem(self): """Returns: bytes""" + assert self.mod and self.public_exponent and self.private_exponent, str(self) rsa = RSA.construct((base64_to_long(str(self.mod)), base64_to_long(str(self.public_exponent)), base64_to_long(str(self.private_exponent)))) diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py index 7e22f81..57531bc 100644 --- a/tests/test_activitypub.py +++ b/tests/test_activitypub.py @@ -785,6 +785,21 @@ class ActivityPubTest(TestCase): type='follow', object_ids=[FOLLOW['object']]) + def test_inbox_follow_accept_shared_inbox(self, *mocks): + self._test_inbox_follow_accept(FOLLOW_WRAPPED, ACCEPT, 200, *mocks, + inbox_path='/ap/sharedInbox') + + url = 'https://mas.to/users/swentel#followed-https://user.com/' + self.assert_object('https://mas.to/6d1a', + users=[self.swentel_key], + notify=[self.user.key], + source_protocol='activitypub', + status='complete', + our_as1=as2.to_as1({**FOLLOW_WITH_ACTOR, 'url': url}), + delivered=['https://user.com/'], + type='follow', + object_ids=[FOLLOW['object']]) + def test_inbox_follow_accept_webmention_fails(self, mock_head, mock_get, mock_post): mock_post.side_effect = [ @@ -807,7 +822,8 @@ class ActivityPubTest(TestCase): object_ids=[FOLLOW['object']]) def _test_inbox_follow_accept(self, follow_as2, accept_as2, expected_status, - mock_head, mock_get, mock_post): + mock_head, mock_get, mock_post, + inbox_path='/user.com/inbox'): # this should makes us make the follower ActivityPub as direct=True self.user.direct = False self.user.put() @@ -821,7 +837,7 @@ class ActivityPubTest(TestCase): if not mock_post.return_value and not mock_post.side_effect: mock_post.return_value = requests_response() - got = self.post('/user.com/inbox', json=follow_as2) + got = self.post(inbox_path, json=follow_as2) self.assertEqual(expected_status, got.status_code) mock_get.assert_has_calls((