From fa2a9a1afe779783e2c198012fc72714977bf683 Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Fri, 3 Mar 2023 15:12:51 -0800 Subject: [PATCH] activitypub: store Create inner objects in their own Objects --- activitypub.py | 49 +++++++++++++++++++-------------------- tests/test_activitypub.py | 18 +++++++++++++- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/activitypub.py b/activitypub.py index 08014a8..d2b06b6 100644 --- a/activitypub.py +++ b/activitypub.py @@ -135,9 +135,7 @@ def inbox(domain=None): elif obj.type not in SUPPORTED_TYPES: error(f'Sorry, {obj.type} activities are not supported yet.', status=501) - inner_obj = obj.as1.get('object') or {} - if isinstance(inner_obj, str): - inner_obj = {'id': inner_obj} + inner_obj = as1.get_object(obj.as1) inner_obj_id = inner_obj.get('id') # load user @@ -149,6 +147,20 @@ def inbox(domain=None): verify_signature(user) + # check that this activity is public. only do this check for creates, not + # like, follow, or other activity types, since Mastodon doesn't currently + # mark those as explicitly public. Use as2's is_public instead of as1's + # because as1's interprets unlisted as true. + if obj.type in ('post', 'create') and not as2.is_public(obj.as2): + logger.info('Dropping non-public activity') + return 'OK' + + # store inner object + if obj.type in ('post', 'create', 'update') and inner_obj.keys() > set(['id']): + to_update = Object.get_by_id(inner_obj_id) or Object(id=inner_obj_id) + to_update.populate(as2=obj.as2['object'], source_protocol='activitypub') + to_update.put() + # handle activity! if obj.type == 'stop-following': # granary doesn't yet handle three-actor undo follows, eg Eve undoes @@ -179,10 +191,6 @@ def inbox(domain=None): if not inner_obj_id: error("Couldn't find id of object to update") - to_update = Object.get_by_id(inner_obj_id) or Object(id=inner_obj_id) - to_update.populate(as2=obj.as2.get('object'), source_protocol='activitypub') - to_update.put() - obj.status = 'complete' obj.put() return 'OK' @@ -224,25 +232,16 @@ def inbox(domain=None): common.send_webmentions(as2.to_as1(activity), obj, proxy=True) # deliver original posts and reposts to followers - if obj.type in ('share', 'create', 'post'): - # check that this activity is public. only do this check for Creates, - # not Like, Follow, or other activity types, since Mastodon doesn't - # currently mark those as explicitly public. - if not as1.is_public(obj.as1): - logger.info('Dropping non-public activity') - return 'OK' + if obj.type in ('share', 'create', 'post') and actor and actor_id: + logger.info(f'Delivering to followers of {actor_id}') + for f in Follower.query(Follower.dest == actor_id, + projection=[Follower.src]): + if f.src not in obj.domains: + obj.domains.append(f.src) + if obj.domains and 'feed' not in obj.labels: + obj.labels.append('feed') - if actor and actor_id: - logger.info(f'Delivering to followers of {actor_id}') - for f in Follower.query(Follower.dest == actor_id, - projection=[Follower.src]): - if f.src not in obj.domains: - obj.domains.append(f.src) - if obj.domains and 'feed' not in obj.labels: - obj.labels.append('feed') - - if (obj.as1.get('objectType') == 'activity' - and 'activity' not in obj.labels): + if obj.as1.get('objectType') == 'activity' and 'activity' not in obj.labels: obj.labels.append('activity') obj.put() diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py index 6761587..04c953c 100644 --- a/tests/test_activitypub.py +++ b/tests/test_activitypub.py @@ -303,6 +303,10 @@ class ActivityPubTest(testutil.TestCase): 'labels': ['notification', 'activity'], }, *mocks) + self.assert_object(REPLY_OBJECT['id'], + source_protocol='activitypub', + as2=REPLY_OBJECT, + type='comment') def _test_inbox_reply(self, reply, expected_props, mock_head, mock_get, mock_post): mock_head.return_value = requests_response(url='http://or.ig/post') @@ -380,6 +384,10 @@ class ActivityPubTest(testutil.TestCase): type='post', labels=['activity', 'feed'], object_ids=[NOTE_OBJECT['id']]) + self.assert_object(NOTE_OBJECT['id'], + source_protocol='activitypub', + as2=NOTE_OBJECT, + type='note') def test_repost_of_federated_post(self, mock_head, mock_get, mock_post): mock_head.return_value = requests_response(url='https://foo.com/orig') @@ -480,7 +488,7 @@ class ActivityPubTest(testutil.TestCase): self.assertEqual(200, got.status_code, got.get_data(as_text=True)) obj = Object.get_by_id(not_public['id']) - self.assertEqual(['activity'], obj.labels) + self.assertEqual([], obj.labels) self.assertEqual([], obj.domains) self.assertIsNone(Object.get_by_id(not_public['object']['id'])) @@ -506,6 +514,14 @@ class ActivityPubTest(testutil.TestCase): *mocks, ) + # redirect unwrap + expected_as2 = copy.deepcopy(MENTION_OBJECT) + expected_as2['tag'][1]['href'] = 'https://tar.get/' + self.assert_object(MENTION_OBJECT['id'], + source_protocol='activitypub', + as2=expected_as2, + type='note') + def _test_inbox_mention(self, mention, expected_props, mock_head, mock_get, mock_post): mock_get.return_value = requests_response( '')