store all incoming AP activities in Objects

pull/424/head
Ryan Barrett 2023-02-11 22:53:50 -08:00
rodzic 3feb44e414
commit bf97c1af4f
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
2 zmienionych plików z 73 dodań i 40 usunięć

Wyświetl plik

@ -87,6 +87,17 @@ def inbox(domain=None):
if isinstance(obj_as2, str):
obj_as2 = {'id': obj_as2}
id = activity.get('id')
if not id:
error('Activity has no id')
activity_as1 = as2.to_as1(activity)
as1_type = as1.object_type(activity_as1)
activity_obj = Object(
id=id, as2=json_dumps(activity), as1=json_dumps(activity_as1),
type=as1_type, source_protocol='activitypub', status='complete')
activity_obj.put()
if type == 'Accept': # eg in response to a Follow
return '' # noop
if type not in SUPPORTED_TYPES:
@ -100,12 +111,12 @@ def inbox(domain=None):
return ''
elif type == 'Update':
id = obj_as2.get('id')
if not id:
error("Couldn't find id of object to update")
obj_id = obj_as2.get('id')
if not obj_id:
error("Couldn't find obj_id of object to update")
logger.info(f'updating Object {id}')
obj = Object.get_by_id(id) or Object(id=id)
logger.info(f'updating Object {obj_id}')
obj = Object.get_by_id(obj_id) or Object(id=obj_id)
obj.as2 = json_dumps(obj_as2)
obj_as1 = as2.to_as1(obj_as2)
obj.as1 = json_dumps(obj_as1)
@ -115,21 +126,21 @@ def inbox(domain=None):
return 'OK'
elif type == 'Delete':
id = obj_as2.get('id')
if not id:
obj_id = obj_as2.get('id')
if not obj_id:
error("Couldn't find id of object to delete")
obj = Object.get_by_id(id)
obj = Object.get_by_id(obj_id)
if obj:
logger.info(f'Marking Object {id} deleted')
logger.info(f'Marking Object {obj_id} deleted')
obj.deleted = True
obj.put()
# assume this is an actor
# https://github.com/snarfed/bridgy-fed/issues/63
logger.info(f'Deactivating Followers with src or dest = {id}')
followers = Follower.query(OR(Follower.src == id,
Follower.dest == id)
logger.info(f'Deactivating Followers with src or dest = {obj_id}')
followers = Follower.query(OR(Follower.src == obj_id,
Follower.dest == obj_id)
).fetch()
for f in followers:
f.status = 'inactive'
@ -151,7 +162,6 @@ def inbox(domain=None):
# send webmentions to each target
activity_as2_str = json_dumps(activity_unwrapped)
activity_as1 = as2.to_as1(activity_unwrapped)
as1_type = as1.object_type(activity_as1)
activity_as1_str = json_dumps(activity_as1)
sent = common.send_webmentions(as2.to_as1(activity), proxy=True,
source_protocol='activitypub', type=as1_type,
@ -168,23 +178,23 @@ def inbox(domain=None):
logging.info('Dropping non-public activity')
return ''
source = util.get_first(activity, 'url') or activity.get('id')
domains = []
if actor:
actor_id = actor.get('id')
if actor_id:
logging.info(f'Finding followers of {actor_id}')
domains = [f.src for f in
Follower.query(Follower.dest == actor_id,
projection=[Follower.src]).fetch()]
activity_obj.domains = [
f.src for f in Follower.query(Follower.dest == actor_id,
projection=[Follower.src]).fetch()]
key = Object(id=source, source_protocol='activitypub', domains=domains,
status='complete', as2=activity_as2_str, as1=activity_as1_str,
type=as1_type, object_ids=as1.get_ids(activity_as1, 'object'),
labels=['feed', 'activity']).put()
logging.info(f'Wrote Object {key} with {len(domains)} follower domains')
activity_obj.as2 = activity_as2_str
activity_obj.as1 = activity_as1_str
activity_obj.type = as1_type
activity_obj.object_ids = as1.get_ids(activity_as1, 'object')
activity_obj.labels = ['feed', 'activity']
activity_obj.put()
logging.info(f'Wrote Object {id} for {len(activity_obj.domains)} followers')
return ''
return 'OK'
def accept_follow(follow, follow_unwrapped, user):

Wyświetl plik

@ -315,6 +315,12 @@ class ActivityPubTest(testutil.TestCase):
got = self.client.post('/nope.com/inbox', json=REPLY)
self.assertEqual(404, got.status_code)
def test_inbox_activity_without_id(self, *_):
note = copy.deepcopy(NOTE)
del note['id']
resp = self.client.post('/inbox', json=note)
self.assertEqual(400, resp.status_code)
def test_inbox_reply_object(self, *mocks):
self._test_inbox_reply(REPLY_OBJECT,
{'as2': REPLY_OBJECT,
@ -453,7 +459,12 @@ class ActivityPubTest(testutil.TestCase):
got = self.client.post('/foo.com/inbox', json=not_public)
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
self.assertEqual(0, Object.query().count())
obj = Object.get_by_id(not_public['id'])
self.assertEqual([], obj.labels)
self.assertEqual([], obj.domains)
self.assertIsNone(Object.get_by_id(not_public['object']['id']))
def test_inbox_mention_object(self, *mocks):
self._test_inbox_mention(
@ -732,18 +743,26 @@ class ActivityPubTest(testutil.TestCase):
def test_inbox_bad_object_url(self, mock_head, mock_get, mock_post):
# https://console.cloud.google.com/errors/detail/CMKn7tqbq-GIRA;time=P30D?project=bridgy-federated
mock_get.return_value = self.as2_resp(ACTOR) # source actor
id = 'https://mastodon.social/users/tmichellemoore#likes/56486252'
bad_url = 'http://localhost/r/Testing \u2013 Brid.gy \u2013 Post to Mastodon 3'
got = self.client.post('/foo.com/inbox', json={
'@context': 'https://www.w3.org/ns/activitystreams',
'id': 'https://mastodon.social/users/tmichellemoore#likes/56486252',
'id': id,
'type': 'Like',
'actor': ACTOR['id'],
'object': 'http://localhost/r/Testing \u2013 Brid.gy \u2013 Post to Mastodon 3',
'object': bad_url,
})
# bad object, should ignore activity
self.assertEqual(200, got.status_code)
mock_post.assert_not_called()
self.assertEqual(0, Object.query().count())
obj = Object.get_by_id(id)
self.assertEqual([], obj.labels)
self.assertEqual([], obj.domains)
self.assertIsNone(Object.get_by_id(bad_url))
def test_delete_actor(self, _, __, ___):
follower = Follower.get_or_create('foo.com', DELETE['actor'])
@ -761,30 +780,34 @@ class ActivityPubTest(testutil.TestCase):
def test_delete_note(self, _, __, ___):
key = Object(id='http://an/obj', as1='{}').put()
resp = self.client.post('/inbox', json={
delete = {
**DELETE,
'object': 'http://an/obj',
})
}
resp = self.client.post('/inbox', json=delete)
self.assertEqual(200, resp.status_code)
self.assertTrue(key.get().deleted)
self.assert_object(delete['id'], as2=delete, as1=as2.to_as1(delete),
type='delete', source_protocol='activitypub',
status='complete')
def test_update_note(self, _, __, ___):
key = Object(id='https://a/note', as1='{}').put()
def test_update_note(self, *_):
Object(id='https://a/note', as1='{}').put()
self._test_update()
def test_update_unknown(self, *_):
self._test_update()
def _test_update(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_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')
self.assert_object(UPDATE_NOTE['id'], source_protocol='activitypub',
type='update', status='complete', as2=UPDATE_NOTE,
as1=as2.to_as1(UPDATE_NOTE))
def test_inbox_webmention_discovery_connection_fails(self, mock_head,
mock_get, mock_post):