apply AP Updates to stored Objects in datastore

for #409
pull/424/head
Ryan Barrett 2023-02-11 22:23:01 -08:00
rodzic 7e2fbd1ed0
commit 3feb44e414
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
2 zmienionych plików z 46 dodań i 50 usunięć

Wyświetl plik

@ -83,9 +83,9 @@ def inbox(domain=None):
actor_id = actor.get('id') if isinstance(actor, dict) else actor
logger.info(f'Got {type} activity from {actor_id}: {json_dumps(activity, indent=2)}')
obj = activity.get('object') or {}
if isinstance(obj, str):
obj = {'id': obj}
obj_as2 = activity.get('object') or {}
if isinstance(obj_as2, str):
obj_as2 = {'id': obj_as2}
if type == 'Accept': # eg in response to a Follow
return '' # noop
@ -94,25 +94,29 @@ def inbox(domain=None):
# TODO: verify signature if there is one
if type == 'Undo' and obj.get('type') == 'Follow':
if type == 'Undo' and obj_as2.get('type') == 'Follow':
# skip actor fetch below; we don't need it to undo a follow
undo_follow(redirect_unwrap(activity))
return ''
elif type == 'Update':
if obj.get('type') == 'Person':
return '' # noop
else:
error(f'Sorry, {type} activities are not supported yet.', status=501)
elif type == 'Delete':
# we currently only actually delete followers for Deletes that are sent
# to the shared inbox, not individual users' inboxes, to help scaling
# background: https://github.com/snarfed/bridgy-fed/issues/284
if domain:
logger.info('Skipping Delete sent to individual user inbox')
return 'OK'
id = obj.get('id') if isinstance(obj, dict) else obj
if not isinstance(id, str):
elif type == 'Update':
id = obj_as2.get('id')
if not id:
error("Couldn't find id of object to update")
logger.info(f'updating Object {id}')
obj = Object.get_by_id(id) or Object(id=id)
obj.as2 = json_dumps(obj_as2)
obj_as1 = as2.to_as1(obj_as2)
obj.as1 = json_dumps(obj_as1)
obj.type = as1.object_type(obj_as1)
obj.source_protocol = 'activitypub'
obj.put()
return 'OK'
elif type == 'Delete':
id = obj_as2.get('id')
if not id:
error("Couldn't find id of object to delete")
obj = Object.get_by_id(id)
@ -138,7 +142,7 @@ def inbox(domain=None):
# fetch object if necessary so we can render it in feeds
if type in FETCH_OBJECT_TYPES and isinstance(activity.get('object'), str):
obj = activity['object'] = common.get_as2(activity['object'], user=user).json()
obj_as2 = activity['object'] = common.get_as2(activity['object'], user=user).json()
activity_unwrapped = redirect_unwrap(activity)
if type == 'Follow':
@ -155,7 +159,7 @@ def inbox(domain=None):
object_ids=as1.get_ids(activity_as1, 'object'))
# deliver original posts and reposts to followers
if ((type == 'Create' and not activity.get('inReplyTo') and not obj.get('inReplyTo'))
if ((type == 'Create' and not activity.get('inReplyTo') and not obj_as2.get('inReplyTo'))
or type == 'Announce'):
# check that this activity is public. only do this check for Creates,
# not Like, Follow, or other activity types, since Mastodon doesn't

Wyświetl plik

@ -543,9 +543,8 @@ class ActivityPubTest(testutil.TestCase):
labels=['notification', 'activity'],
object_ids=[LIKE['object']])
def test_inbox_follow_accept_with_id(self, mock_head, mock_get, mock_post):
self._test_inbox_follow_accept(FOLLOW_WRAPPED, ACCEPT,
mock_head, mock_get, mock_post)
def test_inbox_follow_accept_with_id(self, *mocks):
self._test_inbox_follow_accept(FOLLOW_WRAPPED, ACCEPT, *mocks)
follow = copy.deepcopy(FOLLOW_WITH_ACTOR)
follow['url'] = 'https://mastodon.social/users/swentel#followed-https://foo.com/'
@ -564,7 +563,7 @@ class ActivityPubTest(testutil.TestCase):
follower = Follower.query().get()
self.assertEqual(FOLLOW_WRAPPED_WITH_ACTOR, json_loads(follower.last_follow))
def test_inbox_follow_accept_with_object(self, mock_head, mock_get, mock_post):
def test_inbox_follow_accept_with_object(self, *mocks):
wrapped_user = {
'id': FOLLOW_WRAPPED['object'],
'url': FOLLOW_WRAPPED['object'],
@ -580,7 +579,7 @@ class ActivityPubTest(testutil.TestCase):
accept = copy.deepcopy(ACCEPT)
accept['actor'] = accept['object']['object'] = wrapped_user
self._test_inbox_follow_accept(follow, accept, mock_head, mock_get, mock_post)
self._test_inbox_follow_accept(follow, accept, *mocks)
follower = Follower.query().get()
follow.update({
@ -746,22 +745,7 @@ class ActivityPubTest(testutil.TestCase):
mock_post.assert_not_called()
self.assertEqual(0, Object.query().count())
def test_individual_inbox_delete_actor_noop(self, mock_head, mock_get, mock_post):
"""Deletes sent to individual users' inboxes do nothing."""
follower = Follower.get_or_create('foo.com', DELETE['actor'])
followee = Follower.get_or_create(DELETE['actor'], 'snarfed.org')
# other unrelated follower
other = Follower.get_or_create('foo.com', 'https://mas.to/users/other')
self.assertEqual(3, Follower.query().count())
got = self.client.post('/foo.com/inbox', json=DELETE)
self.assertEqual(200, got.status_code)
self.assertEqual('active', follower.key.get().status)
self.assertEqual('active', followee.key.get().status)
self.assertEqual('active', other.key.get().status)
def test_shared_inbox_delete_actor(self, mock_head, mock_get, mock_post):
"""Deletes sent to the shared inbox actually deactivate followers."""
def test_delete_actor(self, _, __, ___):
follower = Follower.get_or_create('foo.com', DELETE['actor'])
followee = Follower.get_or_create(DELETE['actor'], 'snarfed.org')
# other unrelated follower
@ -774,7 +758,7 @@ class ActivityPubTest(testutil.TestCase):
self.assertEqual('inactive', followee.key.get().status)
self.assertEqual('active', other.key.get().status)
def test_delete_note(self, mock_head, mock_get, mock_post):
def test_delete_note(self, _, __, ___):
key = Object(id='http://an/obj', as1='{}').put()
resp = self.client.post('/inbox', json={
@ -784,15 +768,23 @@ class ActivityPubTest(testutil.TestCase):
self.assertEqual(200, resp.status_code)
self.assertTrue(key.get().deleted)
def test_update_person_noop(self, _, __, ___):
"""Updates to Person objects do nothing."""
got = self.client.post('/inbox', json=UPDATE_PERSON)
self.assertEqual(200, got.status_code)
def test_update_note(self, _, __, ___):
key = Object(id='https://a/note', as1='{}').put()
def test_update_note_not_implemented(self, _, __, ___):
"""Updates to non-Person objects are not implemented."""
got = self.client.post('/inbox', json=UPDATE_NOTE)
self.assertEqual(501, got.status_code)
resp = self.client.post('/inbox', json=UPDATE_NOTE)
self.assertEqual(200, resp.status_code)
obj = UPDATE_NOTE['object']
self.assert_object('https://a/note', type='note', as2=obj,
as1=as2.to_as1(obj), source_protocol='activitypub')
def test_update_unknown(self, _, __, ___):
resp = self.client.post('/inbox', json=UPDATE_NOTE)
self.assertEqual(200, resp.status_code)
obj = UPDATE_NOTE['object']
self.assert_object('https://a/note', type='note', as2=obj,
as1=as2.to_as1(obj), source_protocol='activitypub')
def test_inbox_webmention_discovery_connection_fails(self, mock_head,
mock_get, mock_post):