ActivityPub: accept non-public DMs to protocol bot users

for #880
pull/968/head
Ryan Barrett 2024-04-21 08:36:03 -07:00
rodzic 1686a2ba91
commit f357ea1698
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
4 zmienionych plików z 43 dodań i 23 usunięć

Wyświetl plik

@ -57,7 +57,9 @@ WEB_OPT_OUT_DOMAINS = None
FEDI_URL_RE = re.compile(r'https://[^/]+/(@|users/)([^/@]+)(@[^/@]+)?(/(?:statuses/)?[0-9]+)?')
_BOT_ACTOR_IDS = None
# can't use translate_user_id because Web.owns_id checks valid_domain, which
# doesn't allow our protocol subdomains
BOT_ACTOR_IDS = [f'https://{domain}/{domain}' for domain in PROTOCOL_DOMAINS]
def instance_actor():
@ -68,15 +70,6 @@ def instance_actor():
return _INSTANCE_ACTOR
def bot_actor_ids():
global _BOT_ACTOR_IDS
if _BOT_ACTOR_IDS is None:
from activitypub import ActivityPub
_BOT_ACTOR_IDS = [translate_user_id(id=domain, from_=Web, to=ActivityPub)
for domain in PROTOCOL_DOMAINS]
return _BOT_ACTOR_IDS
class ActivityPub(User, Protocol):
"""ActivityPub protocol class.
@ -939,7 +932,12 @@ def inbox(protocol=None, id=None):
# those as explicitly public. Use as2's is_public instead of as1's because
# as1's interprets unlisted as true.
# TODO: move this to Protocol
if type == 'Create' and not as2.is_public(activity, unlisted=False):
object = as1.get_object(activity)
to_cc = set(as1.get_ids(object, 'to') + as1.get_ids(activity, 'cc') +
as1.get_ids(object, 'to') + as1.get_ids(object, 'cc'))
if (type == 'Create' and not as2.is_public(activity, unlisted=False)
# DM to one of our protocol bot users
and not (len(to_cc) == 1 and to_cc.pop() in BOT_ACTOR_IDS)):
logger.info('Dropping non-public activity')
return 'OK'

Wyświetl plik

@ -42,7 +42,9 @@ PROTOCOL_DOMAINS = (
'atproto.brid.gy',
'bluesky.brid.gy',
'bsky.brid.gy',
'eefake.brid.gy',
'fa.brid.gy',
'other.brid.gy',
'nostr.brid.gy',
'web.brid.gy',
)

Wyświetl plik

@ -805,18 +805,18 @@ class Protocol:
return 'OK', 200
elif obj.type == 'post':
to_cc = (util.get_list(inner_obj_as1, 'to')
+ util.get_list(inner_obj_as1, 'cc'))
if len(to_cc) == 1 and to_cc[0] in PROTOCOL_DOMAINS:
content = inner_obj_as1.get('content').strip().lower()
logger.info(f'DM to bot user {to_cc}: {content}')
to_cc = (as1.get_ids(inner_obj_as1, 'to')
+ as1.get_ids(inner_obj_as1, 'cc'))
content = inner_obj_as1.get('content', '').strip().lower()
logger.info(f'got DM to {to_cc}: {content}')
if len(to_cc) == 1:
proto = Protocol.for_bridgy_subdomain(to_cc[0])
assert proto
if content in ('yes', 'ok'):
from_user.enable_protocol(proto)
elif content == 'no':
from_user.disable_protocol(proto)
return 'OK', 200
if proto:
if content in ('yes', 'ok'):
from_user.enable_protocol(proto)
elif content == 'no':
from_user.disable_protocol(proto)
return 'OK', 200
# fetch actor if necessary
if actor and actor.keys() == set(['id']):

Wyświetl plik

@ -20,7 +20,7 @@ from werkzeug.exceptions import BadGateway, BadRequest
# import first so that Fake is defined before URL routes are registered
from . import testutil
from .testutil import Fake, TestCase
from .testutil import ExplicitEnableFake, Fake, TestCase
import activitypub
from activitypub import (
@ -847,6 +847,26 @@ class ActivityPubTest(TestCase):
self.assertIsNone(Object.get_by_id(not_public['id']))
self.assertIsNone(Object.get_by_id(not_public['object']['id']))
def test_inbox_dm_yes_to_bot_user_enables_protocol(self, *mocks):
user = self.make_user(ACTOR['id'], cls=ActivityPub)
self.assertFalse(ActivityPub.is_enabled_to(ExplicitEnableFake, user))
got = self.post('/ap/sharedInbox', json={
'type': 'Create',
'id': 'https://mas.to/dm#create',
'to': ['https://eefake.brid.gy/eefake.brid.gy'],
'object': {
'type': 'Note',
'id': 'https://mas.to/dm',
'attributedTo': ACTOR['id'],
'to': ['https://eefake.brid.gy/eefake.brid.gy'],
'content': 'yes',
},
})
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
user = user.key.get()
self.assertTrue(ActivityPub.is_enabled_to(ExplicitEnableFake, user))
def test_inbox_actor_blocklisted(self, mock_head, mock_get, mock_post):
got = self.post('/ap/sharedInbox', json={
'type': 'Delete',