diff --git a/activitypub.py b/activitypub.py index 2a0b75d..e94dcc0 100644 --- a/activitypub.py +++ b/activitypub.py @@ -527,8 +527,8 @@ def signed_request(fn, url, data=None, headers=None, **kwargs): def postprocess_as2(activity, orig_obj=None, wrap=True): """Prepare an AS2 object to be served or sent via ActivityPub. - ``g.user`` is required. Populates it into the ``actor.id`` and ``publicKey`` - fields. + ``g.user`` is required (in ``postprocess_as2_actor``). It's populated it + into the ``actor.id`` and ``publicKey`` fields. Args: activity (dict): AS2 object or activity diff --git a/convert.py b/convert.py index 4379196..4a154a3 100644 --- a/convert.py +++ b/convert.py @@ -58,13 +58,6 @@ def convert(dest, _, src=None): logger.info(f'Converting from {src_cls.LABEL} to {dest}: {id}') - # require g.user for AP since postprocess_as2 currently needs it. ugh - if dest_cls == ActivityPub: - domain = util.domain_from_link(id, minimize=False) - g.user = Web.get_by_id(domain) - if not g.user: - error(f'No web user found for {domain}') - # load, and maybe fetch. if it's a post/update, redirect to inner object. obj = src_cls.load(id) if not obj: @@ -86,6 +79,22 @@ def convert(dest, _, src=None): if obj.deleted or type == 'delete': return '', 410 + # load g.user for AP since postprocess_as2 currently needs it. ugh. + if dest_cls == ActivityPub: + actor_id = as1.get_owner(obj.as1) + if not actor_id and src_cls == Web: + actor_id = util.domain_from_link(id, minimize=False) + if not actor_id: + error(f"Couldn't determine actor id for {obj.as1}") + + user_key = src_cls.key_for(actor_id) + if not user_key: + error(f"Couldn't determine {src_cls.LABEL} key for {actor_id}") + + g.user = user_key.get() + if not g.user: + error(f'No {src_cls.LABEL} user found for {actor_id}') + # convert and serve return dest_cls.convert(obj), {'Content-Type': dest_cls.CONTENT_TYPE} diff --git a/tests/test_convert.py b/tests/test_convert.py index bd5f047..dd845af 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -13,6 +13,7 @@ from oauth_dropins.webutil.util import json_loads, parse_mf2 from . import testutil from .testutil import Fake, OtherFake +from activitypub import ActivityPub from common import CONTENT_TYPE_HTML COMMENT_AS2 = { @@ -95,6 +96,24 @@ class ConvertTest(testutil.TestCase): 'foo': 'bar', }, json_loads(resp.get_data())) + def test_fake_to_activitypub(self): + self.make_user('fake:alice', cls=Fake) + self.store_object(id='fake:post', our_as1={ + 'actor': 'fake:alice', + 'foo': 'bar', + }) + resp = self.client.get('/convert/ap/fake:post', + base_url='https://fa.brid.gy/') + self.assertEqual(200, resp.status_code) + self.assertEqual(ActivityPub.CONTENT_TYPE, resp.content_type) + self.assertEqual({ + '@context': 'https://www.w3.org/ns/activitystreams', + 'id': 'https://fa.brid.gy/convert/ap/fake:post', + 'actor': 'https://fa.brid.gy/ap/fake:alice', + 'foo': 'bar', + 'to': ['https://www.w3.org/ns/activitystreams#Public'], + }, json_loads(resp.get_data())) + def test_activitypub_to_web_object(self): url = 'https://user.com/bar?baz=baj&biff' Object(id=url, our_as1=COMMENT).put() @@ -274,7 +293,10 @@ A ☕ reply self.assertEqual(200, resp.status_code) self.assert_equals(COMMENT_AS2, resp.json, ignore=['to']) - def test_web_to_activitypub_no_user(self): + @patch('requests.get') + def test_web_to_activitypub_no_user(self, mock_get): + mock_get.return_value = requests_response(HTML) # protocol inference + resp = self.client.get(f'/convert/ap/http://nope.com/post', base_url='https://web.brid.gy/') self.assertEqual(400, resp.status_code) diff --git a/web.py b/web.py index 59e58e3..9744cf1 100644 --- a/web.py +++ b/web.py @@ -245,14 +245,13 @@ class Web(User, Protocol): If id is a domain, uses it as is. If it's a home page URL or fed.brid.gy or web.brid.gy AP actor URL, extracts the domain and uses that. - Otherwise, raises AssertionError. + Otherwise, returns None. Args: - id: str + id (str) - Raises: - ValueError - AssertionError + Returns: + ndb.Key or None: """ if not id: return None