From 51c2773594278dbfef33eab8a2ba238ec7f688c4 Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Sun, 4 Jun 2023 20:58:21 -0700 Subject: [PATCH] AP users: /r/: switch external user to indirect user for #512 --- activitypub.py | 8 +------- flask_app.py | 3 +-- redirect.py | 11 +++++++---- tests/test_activitypub.py | 19 ++++++++++--------- tests/test_redirect.py | 34 +++++++++++++++++++++++----------- tests/test_web.py | 1 - tests/test_webfinger.py | 2 +- 7 files changed, 43 insertions(+), 35 deletions(-) diff --git a/activitypub.py b/activitypub.py index 28acf73..cf08998 100644 --- a/activitypub.py +++ b/activitypub.py @@ -328,7 +328,7 @@ def postprocess_as2(activity, target=None, wrap=True): if not activity or isinstance(activity, str): return activity - assert bool(g.user) ^ bool(g.external_user) # should have one but not both + assert g.user type = activity.get('type') # actor objects @@ -396,14 +396,10 @@ def postprocess_as2(activity, target=None, wrap=True): obj['id'] = util.get_first(obj, 'url') or target_id elif g.user and g.user.is_web_url(id): obj['id'] = g.user.ap_actor() - elif g.external_user: - obj['id'] = redirect_wrap(g.external_user) # for Accepts if g.user and g.user.is_web_url(obj.get('object')): obj['object'] = g.user.ap_actor() - elif g.external_user and g.external_user == obj.get('object'): - obj['object'] = redirect_wrap(g.external_user) # id is required for most things. default to url if it's not set. if not activity.get('id'): @@ -506,8 +502,6 @@ def postprocess_as2_actor(actor, wrap=True): id = actor.get('id') if g.user and (not id or g.user.is_web_url(id)): actor['id'] = g.user.ap_actor() - elif g.external_user and (not id or id == g.external_user): - actor['id'] = redirect_wrap(g.external_user) actor.update({ 'url': urls if len(urls) > 1 else urls[0], diff --git a/flask_app.py b/flask_app.py index 1ae98f3..5baadce 100644 --- a/flask_app.py +++ b/flask_app.py @@ -39,9 +39,8 @@ def init_globals(): """Set request globals. * g.user: Current internal user we're operating on behalf of. - * g.external_user: Current external user we're operating on behalf of. """ - g.user = g.external_user = None + g.user = None # don't redirect API requests with blank path elements app.url_map.merge_slashes = False diff --git a/redirect.py b/redirect.py index 9da6a6e..35540b6 100644 --- a/redirect.py +++ b/redirect.py @@ -83,10 +83,7 @@ def redir(to): logger.info(f'Found web user for domain {domain}') break else: - if accept_as2: - g.external_user = urllib.parse.urljoin(to, '/') - logging.info(f'No web user for {g.external_user}') - else: + if not accept_as2: return f'No web user found for any of {domains}', 404 if accept_as2: @@ -94,6 +91,12 @@ def redir(to): obj = Web.load(to, check_backlink=False) if not obj or obj.deleted: return f'Object not found: {to}', 404 + + g.user = Web.get_or_create(util.domain_from_link(to), direct=False) + if g.user.is_web_url(to): + g.user.actor_as2 = as2.from_as1(obj.as1) + g.user.put() + ret, _ = ActivityPub.serve(obj) logger.info(f'Returning: {json_dumps(ret, indent=2)}') return ret, { diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py index 4a2205f..48cabdb 100644 --- a/tests/test_activitypub.py +++ b/tests/test_activitypub.py @@ -65,6 +65,15 @@ ACTOR_BASE = { 'publicKeyPem': 'populated in setUp()', }, } +ACTOR_BASE_FULL = { + **ACTOR_BASE, + 'name': 'Ms. ☕ Baz', + 'attachment': [{ + 'name': 'Web site', + 'type': 'PropertyValue', + 'value': 'user.com', + }], +} REPLY_OBJECT = { '@context': 'https://www.w3.org/ns/activitystreams', 'type': 'Note', @@ -347,15 +356,7 @@ class ActivityPubTest(TestCase): got = self.client.get('/user.com') self.assertEqual(200, got.status_code) - self.assert_equals({ - **ACTOR_BASE, - 'name': 'Ms. ☕ Baz', - 'attachment': [{ - 'name': 'Web site', - 'type': 'PropertyValue', - 'value': 'user.com', - }], - }, got.json, ignore=['publicKeyPem']) + self.assert_equals(ACTOR_BASE_FULL, got.json, ignore=['publicKeyPem']) def test_actor_new_user_fetch_no_mf2(self, _, mock_get, __): self.user.key.delete() diff --git a/tests/test_redirect.py b/tests/test_redirect.py index c8c319a..5cb8a43 100644 --- a/tests/test_redirect.py +++ b/tests/test_redirect.py @@ -13,6 +13,9 @@ from . import testutil from common import redirect_unwrap from flask_app import app, cache from models import Object, User +from web import Web + +from .test_activitypub import ACTOR_BASE_FULL from .test_web import ( ACTOR_AS2, ACTOR_AS2_FULL, @@ -27,9 +30,6 @@ REPOST_AS2 = { } del REPOST_AS2['cc'] -EXTERNAL_REPOST_AS2 = copy.deepcopy(REPOST_AS2) -EXTERNAL_REPOST_AS2['actor']['id'] = 'http://localhost/r/https://user.com/' - class RedirectTest(testutil.TestCase): @@ -71,14 +71,16 @@ class RedirectTest(testutil.TestCase): def test_as2_creates_user(self): with self.request_context: - Object(id='https://user.com/repost', as2=EXTERNAL_REPOST_AS2).put() + Object(id='https://user.com/repost', as2=REPOST_AS2).put() self.user.key.delete() resp = self.client.get('/r/https://user.com/repost', headers={'Accept': as2.CONTENT_TYPE}) self.assertEqual(200, resp.status_code, resp.get_data(as_text=True)) - self.assert_equals(EXTERNAL_REPOST_AS2, resp.json) + self.assert_equals(REPOST_AS2, resp.json) + + self.assert_user(Web, 'user.com', direct=False) @patch('requests.get') def test_as2_fetch_post(self, mock_get): @@ -107,16 +109,26 @@ class RedirectTest(testutil.TestCase): resp = self.client.get('/r/https://user.com/', headers={'Accept': as2.CONTENT_TYPE}) self.assertEqual(200, resp.status_code, resp.get_data(as_text=True)) - self.assert_equals({ - **ACTOR_AS2, - 'id': 'http://localhost/r/https://user.com/', - 'summary': '', + + expected = copy.deepcopy(ACTOR_BASE_FULL) + del expected['endpoints'] + del expected['followers'] + del expected['following'] + del expected['inbox'] + del expected['outbox'] + self.assert_equals(expected, resp.json, ignore=['publicKeyPem']) + + self.assert_user(Web, 'user.com', direct=False, actor_as2={ + '@context': 'https://www.w3.org/ns/activitystreams', + 'type': 'Person', + 'url': 'https://user.com/', + 'name': 'Ms. ☕ Baz', 'attachment': [{ - 'name': 'Ms. ☕ Baz', 'type': 'PropertyValue', + 'name': 'Ms. ☕ Baz', 'value': 'user.com', }], - }, resp.json) + }) def test_accept_header_cache_key(self): app.config['CACHE_TYPE'] = 'SimpleCache' diff --git a/tests/test_web.py b/tests/test_web.py index 4dc882f..6a4cb3c 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -69,7 +69,6 @@ ACTOR_AS2_FULL = { 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', ], - 'preferredUsername': 'user.com', 'attachment': [{ 'name': 'Web site', 'type': 'PropertyValue', diff --git a/tests/test_webfinger.py b/tests/test_webfinger.py index cd2b82c..a94b4dc 100644 --- a/tests/test_webfinger.py +++ b/tests/test_webfinger.py @@ -207,7 +207,7 @@ class WebfingerTest(testutil.TestCase): self.assertEqual(404, got.status_code) @patch('requests.get') - def test_webfinger_external_user_fetch_create_user(self, mock_get): + def test_webfinger_fetch_create_user(self, mock_get): self.user.key.delete() mock_get.return_value = requests_response(ACTOR_HTML)