Skip to content

Commit

Permalink
bug fix for deleting bridged fediverse actors: translate object as ac…
Browse files Browse the repository at this point in the history
…tor id

...not as object id. for #783, 81319a7
  • Loading branch information
snarfed committed Jun 14, 2024
1 parent 49445c9 commit da5d434
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 48 deletions.
1 change: 1 addition & 0 deletions protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ def translate(elem, field, fn):
else translate_object_id)

inner_is_actor = (as1.object_type(inner_obj) in as1.ACTOR_TYPES
or as1.get_owner(outer_obj) == inner_obj.get('id')
or type in ('follow', 'stop-following'))
translate(inner_obj, 'id',
translate_user_id if inner_is_actor else translate_object_id)
Expand Down
132 changes: 84 additions & 48 deletions tests/test_integrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import app
from atproto import ATProto, Cursor
from atproto_firehose import handle, new_commits, Op
from models import Object, Target
from models import Follower, Object, Target
from web import Web

from .testutil import ATPROTO_KEY, TestCase
Expand All @@ -45,32 +45,54 @@ def setUp(self):
super().setUp()
self.storage = DatastoreStorage()

def make_ap_user_with_atp(self, ap_id, did):
Repo.create(self.storage, did, signing_key=ATPROTO_KEY)
def make_ap_user(self, ap_id, did=None):
user = self.make_user(id=ap_id, cls=ActivityPub,
enabled_protocols=['atproto'],
copies=[Target(uri=did, protocol='atproto')],
obj_as2=add_key({
'type': 'Person',
'id': ap_id,
'name': 'My Name',
'image': 'http://pic',
'inbox': f'{ap_id}/inbox',
}))
if did:
self.make_atproto_copy(user, did)

bob_did_doc = copy.deepcopy(test_atproto.DID_DOC)
bob_did_doc['service'][0]['serviceEndpoint'] = ATProto.PDS_URL
bob_did_doc.update({
'id': did,
# 'alsoKnownAs': ['at://...ap.brid.gy'],
})
self.store_object(id=did, raw=bob_did_doc)
return user

def make_atproto_user(self, did, enabled_protocols=['activitypub']):
self.store_object(id=did, raw=DID_DOC)
user = self.make_user(id=did, cls=ATProto,
obj_bsky=test_atproto.ACTOR_PROFILE_BSKY,
enabled_protocols=enabled_protocols)
return user

def make_web_user(self, domain, did, enabled_protocols=['activitypub']):
ap_subdomain = (domain.removesuffix('.brid.gy')
if domain.endswith('.brid.gy')
else None)

self.store_object(id=f'at://{did}/app.bsky.actor.profile/self',
bsky=bluesky.from_as1(user.obj.as1))
user = self.make_user(id=domain, cls=Web, ap_subdomain=ap_subdomain,
enabled_protocols=enabled_protocols)
if did:
self.make_atproto_copy(user, did)

return user

def make_atproto_copy(self, user, did):
user.enabled_protocols = ['atproto']
user.copies = [Target(uri=did, protocol='atproto')]
user.put()

Repo.create(self.storage, did, signing_key=ATPROTO_KEY)

did_doc = copy.deepcopy(test_atproto.DID_DOC)
did_doc['service'][0]['serviceEndpoint'] = ATProto.PDS_URL
did_doc['id'] = did
self.store_object(id=did, raw=did_doc)
if user.obj.as1:
self.store_object(id=f'at://{did}/app.bsky.actor.profile/self',
bsky=bluesky.from_as1(user.obj.as1))


# TODO: port to firehose
@skip
Expand All @@ -84,20 +106,8 @@ def test_atproto_notify_reply_to_activitypub(self, mock_get, mock_post):
https://github.com/snarfed/bridgy-fed/issues/720
"""
# setup
self.store_object(id='did:plc:alice', raw=DID_DOC)
alice = self.make_user(id='did:plc:alice', cls=ATProto)

Repo.create(self.storage, 'did:plc:bob', signing_key=ATPROTO_KEY)
bob = self.make_user(
id='http://inst/bob',
cls=ActivityPub,
copies=[Target(uri='did:plc:bob', protocol='atproto')],
enabled_protocols=['atproto'],
obj_as2={
'id': 'http://inst/bob',
'inbox': 'http://inst/bob/inbox',
})
alice = self.make_atproto_user('did:plc:alice')
bob = self.make_atproto_user('http://inst/bob', 'did:plc:alice')

self.store_object(id='http://inst/post', source_protocol='activitypub',
our_as1={
Expand Down Expand Up @@ -182,8 +192,7 @@ def test_atproto_follow_to_web(self, mock_get, mock_post):
Web user bob.com
"""
# setup
self.store_object(id='did:plc:alice', raw=DID_DOC)
alice = self.make_user(id='did:plc:alice', cls=ATProto)
alice = self.make_atproto_user('did:plc:alice')

Repo.create(self.storage, 'did:plc:bob', signing_key=ATPROTO_KEY)
bob = self.make_user(id='bob.com', cls=Web,
Expand Down Expand Up @@ -338,10 +347,8 @@ def test_activitypub_like_of_atproto(self, mock_get, _):
ATProto user alice.com (did:plc:alice)
Like is https://inst/like
"""
self.store_object(id='did:plc:alice', raw=DID_DOC)
alice = self.make_user(id='did:plc:alice', cls=ATProto)

self.make_ap_user_with_atp('https://inst/bob', 'did:plc:bob')
self.make_atproto_user('did:plc:alice')
self.make_ap_user('https://inst/bob', 'did:plc:bob')

# existing Object with original post, *without* cid. we should generate.
Object(id='at://did:plc:alice/app.bsky.feed.post/123', bsky=POST_BSKY).put()
Expand Down Expand Up @@ -499,15 +506,7 @@ def test_atproto_follow_ap_bot_user_enables_protocol(self, mock_get, mock_post):
ATProto user alice.com, did:plc:alice
ActivityPub bot user @ap.brid.gy, did:plc:ap
"""
self.make_user(id='ap.brid.gy', cls=Web, ap_subdomain='ap',
enabled_protocols=['atproto'],
copies=[Target(uri='did:plc:ap', protocol='atproto')])
self.store_object(id='did:plc:ap', raw={
**DID_DOC,
'id': 'did:plc:ap',
'alsoKnownAs': ['at://ap.brid.gy'],
})
Repo.create(self.storage, 'did:plc:ap', signing_key=ATPROTO_KEY)
self.make_web_user('ap.brid.gy', did='did:plc:ap')

mock_get.side_effect = [
# ATProto listNotifications
Expand Down Expand Up @@ -546,6 +545,46 @@ def test_atproto_follow_ap_bot_user_enables_protocol(self, mock_get, mock_post):
self.assertTrue(user.is_enabled(ActivityPub))


@patch('requests.post')
@patch('requests.get')
def test_atproto_block_ap_bot_user_disables_protocol_deletes_actor(
self, mock_get, mock_post):
"""Bluesky user blocks ap.brid.gy: disables protocol, deletes their AP actor.
ATProto user alice.com, did:plc:alice
"""
self.make_web_user('ap.brid.gy', did='did:plc:ap')
alice = self.make_atproto_user('did:plc:alice')
Follower.get_or_create(to=alice, from_=self.make_ap_user('http://x/bob'))
Follower.get_or_create(to=alice, from_=self.make_ap_user('http://y/eve'))

block = {
'$type': 'app.bsky.graph.block',
'subject': 'did:plc:ap',
'createdAt': '2022-01-02T03:04:05.000Z'
}
new_commits.put(Op(repo='did:plc:alice', action='create', seq=123,
path='app.bsky.graph.block/123', record=block))
Cursor(id='bgs.local com.atproto.sync.subscribeRepos').put()

handle(limit=1)

self.assertEqual(2, mock_post.call_count)
args, kwargs = mock_post.call_args_list
self.assertEqual([('http://x/bob/inbox',), ('http://y/eve/inbox',)],
[args for args, _ in mock_post.call_args_list])

for _, kwargs in mock_post.call_args_list:
self.assert_equals({
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Delete',
'id': 'https://bsky.brid.gy/convert/ap/did:plc:alice#delete-copy-activitypub-2022-01-02T03:04:05+00:00',
'actor': 'https://bsky.brid.gy/ap/did:plc:alice',
'object': 'https://bsky.brid.gy/ap/did:plc:alice',
'to': ['https://www.w3.org/ns/activitystreams#Public'],
}, json_loads(kwargs['data']))


@patch('requests.post')
@patch('requests.get')
def test_atproto_mention_activitypub(self, mock_get, mock_post):
Expand All @@ -554,11 +593,8 @@ def test_atproto_mention_activitypub(self, mock_get, mock_post):
ATProto user alice.com, did:plc:alice
ActivityPub user bob@inst, https://inst/bob, bob.inst.ap.brid.gy, did:plc:bob
"""
self.store_object(id='did:plc:alice', raw=DID_DOC)
alice = self.make_user(id='did:plc:alice', cls=ATProto,
obj_bsky=test_atproto.ACTOR_PROFILE_BSKY,
enabled_protocols=['activitypub'])
bob = self.make_ap_user_with_atp('https://inst/bob', 'did:plc:bob')
alice = self.make_atproto_user('did:plc:alice')
bob = self.make_ap_user('https://inst/bob', 'did:plc:bob')

post = {
'$type': 'app.bsky.feed.post',
Expand Down
13 changes: 13 additions & 0 deletions tests/test_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,19 @@ def test_translate_ids_update_profile(self):
},
}))

def test_translate_ids_delete_actor(self):
self.assert_equals({
'objectType': 'activity',
'verb': 'delete',
'actor': 'other:u:fake:alice',
'object': 'other:u:fake:alice',
}, OtherFake.translate_ids({
'objectType': 'activity',
'verb': 'delete',
'actor': 'fake:alice',
'object': 'fake:alice',
}))

def test_translate_ids_copies(self):
self.store_object(id='fake:post',
copies=[Target(uri='other:post', protocol='other')])
Expand Down

0 comments on commit da5d434

Please sign in to comment.