diff --git a/activitypub.py b/activitypub.py index 0d1f8d9..70a3f5b 100644 --- a/activitypub.py +++ b/activitypub.py @@ -493,10 +493,10 @@ def postprocess_as2(activity, orig_obj=None, wrap=True): Args: activity (dict): AS2 object or activity - orig_obj (dict): AS2 object, optional. The target of activity's inReplyTo or - Like/Announce/etc object, if any. - wrap (bool): whether to wrap id, url, object, actor, and attributedTo - + orig_obj (dict): AS2 object, optional. The target of activity's ``inReplyTo`` or + ``Like``/``Announce``/etc object, if any. + wrap (bool): whether to wrap ``id``, ``url``, ``object``, ``actor``, and + ``attributedTo`` """ if not activity or isinstance(activity, str): return activity diff --git a/protocol.py b/protocol.py index f25f93a..a0d029d 100644 --- a/protocol.py +++ b/protocol.py @@ -214,7 +214,9 @@ class Protocol: if cls == Protocol: return Protocol.for_id(id).key_for(id) - return cls(id=id).key + # load user so that we follow use_instead + existing = cls.get_by_id(id) + return existing.key if existing else cls(id=id).key @staticmethod def for_id(id): diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 6c8ac65..998c760 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -281,7 +281,7 @@ class ProtocolTest(TestCase): with self.assertRaises(AssertionError): Fake.load('nope', local=False, remote=False) - def test_owner_key(self): + def test_actor_key(self): user = Fake(id='fake:a') a_key = user.key @@ -300,6 +300,15 @@ class ProtocolTest(TestCase): self.assertEqual(a_key, Fake.actor_key(Object())) self.assertIsNone(Fake.actor_key(Object(), default_g_user=False)) + def test_key_for(self): + self.assertEqual(self.user.key, Protocol.key_for(self.user.key.id())) + + Fake(id='fake:other', use_instead=self.user.key).put() + self.assertEqual(self.user.key, Protocol.key_for('fake:other')) + + # no stored user + self.assertEqual(ndb.Key('Fake', 'fake:foo'), Protocol.key_for('fake:foo')) + def test_targets_checks_blocklisted_per_protocol(self): """_targets should call the target protocol's is_blocklisted().""" # non-ATProto account, ATProto target (PDS) is http://localhost, @@ -502,6 +511,27 @@ class ProtocolReceiveTest(TestCase): self.assertEqual([(obj, 'shared:target')], Fake.sent) + def test_create_post_use_instead(self): + self.make_user('fake:instead', cls=Fake, use_instead=g.user.key, obj_mf2={ + 'type': ['h-card'], + 'properties': { + # this is the key part to test; Object.as1 uses this as id + 'url': ['https://www.user.com/'], + }, + }) + self.make_followers() + + post_as1 = { + 'id': 'fake:post', + 'objectType': 'note', + 'author': 'fake:instead', + } + obj = self.store_object(id='fake:post', our_as1=post_as1) + + self.assertEqual('OK', Fake.receive_as1(post_as1)) + self.assertEqual(1, len(Fake.sent)) + self.assertEqual('shared:target', Fake.sent[0][1]) + def test_update_post(self): self.make_followers() diff --git a/tests/test_web.py b/tests/test_web.py index 03552d0..649fe74 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -1093,6 +1093,45 @@ class WebTest(TestCase): status='complete', ) + def test_create_post_use_instead_strip_www(self, mock_get, mock_post): + g.user.obj.mf2 = { + 'type': ['h-card'], + 'properties': { + # this is the key part to test; Object.as1 uses this as id + 'url': ['https://www.user.com/'], + 'name': ['Ms. ☕ Baz'], + }, + } + g.user.obj.put() + self.make_user('www.user.com', use_instead=g.user.key) + self.make_followers() + + note_html = NOTE_HTML.replace('https://user.com/', 'https://www.user.com/') + mock_get.side_effect = [ + requests_response(note_html, url='https://www.user.com/post'), + ACTOR, + ] + mock_post.return_value = requests_response('abc xyz') + + got = self.client.post('/queue/webmention', data={ + 'source': 'https://www.user.com/post', + 'target': 'https://fed.brid.gy/', + }) + self.assertEqual(200, got.status_code) + + mock_get.assert_has_calls(( + self.req('https://www.user.com/post'), + )) + + inboxes = ('https://inbox/', 'https://public/inbox', 'https://shared/inbox') + create_as2 = copy.deepcopy(CREATE_AS2) + create_as2['id'] = 'http://localhost/r/https://www.user.com/post#bridgy-fed-create' + create_as2['object'].update({ + 'id': 'http://localhost/r/https://www.user.com/post', + 'url': 'http://localhost/r/https://www.user.com/post', + }) + self.assert_deliveries(mock_post, inboxes, create_as2) + def test_create_post(self, mock_get, mock_post): mock_get.side_effect = [NOTE, ACTOR] mock_post.return_value = requests_response('abc xyz') @@ -1887,6 +1926,13 @@ class WebUtilTest(TestCase): with self.subTest(bad=bad): self.assertIsNone(Web.key_for(bad)) + # no stored user + self.assertEqual(Web(id='foo.com').key, Web.key_for('foo.com')) + + def test_key_for_use_instead(self, *_): + Web(id='www.user.com', use_instead=g.user.key).put() + self.assertEqual(g.user.key, Web.key_for('www.user.com')) + def test_handle(self, *_): self.assertEqual('user.com', g.user.handle) diff --git a/web.py b/web.py index 93599cb..235c535 100644 --- a/web.py +++ b/web.py @@ -257,7 +257,7 @@ class Web(User, Protocol): id = parsed.netloc if is_valid_domain(id): - return cls(id=id).key + return super().key_for(id) logger.info(f'{id} is not a domain or usable home page URL') return None