Skip to content

Commit

Permalink
Protocol.receive: second part of making explicit opt in override impl…
Browse files Browse the repository at this point in the history
…icit opt out

for #1052, #974
  • Loading branch information
snarfed committed May 15, 2024
1 parent 71b2306 commit e4f9966
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 12 deletions.
7 changes: 5 additions & 2 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,20 +236,23 @@ def get_by_id(cls, id, allow_opt_out=False):

@classmethod
@ndb.transactional()
def get_or_create(cls, id, propagate=False, **kwargs):
def get_or_create(cls, id, propagate=False, allow_opt_out=False, **kwargs):
"""Loads and returns a :class:`User`. Creates it if necessary.
Args:
propagate (bool): whether to create copies of this user in push-based
protocols, eg ATProto and Nostr.
allow_opt_out (bool): whether to allow and create the user if they're
currently opted out
Returns:
User: existing or new user, or None if the user is opted out
"""
assert cls != User
user = cls.get_by_id(id, allow_opt_out=True)
if user:
if user.status:
if user.status and not allow_opt_out:
return None
user.existing = True

Expand Down
20 changes: 11 additions & 9 deletions protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -764,16 +764,18 @@ def receive(from_cls, obj, authed_as=None, internal=False):
# update copy ids to originals
obj.resolve_ids()

# refresh profile for follows of bot users to re-check whether they're
# opted out
if obj.type == 'follow':
if Protocol.for_bridgy_subdomain(as1.get_object(obj.as1).get('id')):
logger.info(f'Follow of bot user, reloading {actor}')
from_cls.load(actor, remote=True)
if (obj.type == 'follow'
and Protocol.for_bridgy_subdomain(as1.get_object(obj.as1).get('id'))):
# refresh profile for follows of bot users to re-check whether
# they're opted out
logger.info(f'Follow of bot user, reloading {actor}')
from_cls.load(actor, remote=True)
from_user = from_cls.get_or_create(id=actor, allow_opt_out=True)
else:
# load actor user
from_user = from_cls.get_or_create(id=actor)

# load actor user
from_user = from_cls.get_or_create(id=actor)
if not from_user:
if not from_user or from_user.manual_opt_out:
error(f'Actor {actor} is opted out or blocked', status=204)

# write Object to datastore
Expand Down
6 changes: 5 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,10 +378,14 @@ def test_is_enabled_enabled_protocols_overrides_bio_opt_out(self):
self.assertEqual('opt-out', user.status)

user.enabled_protocols = ['web']
user.put()
self.assertTrue(user.is_enabled(Web))
self.assertIsNone(user.status)

# manual opt out should still take precedence thoough
user.manual_opt_out = True
self.assertFalse(user.is_enabled(Web))
self.assertEqual('opt-out', user.status)

def test_is_enabled_enabled_protocols_overrides_non_public_profile_opt_out(self):
self.store_object(id='did:plc:user', raw=DID_DOC)
user = self.make_user('did:plc:user', cls=ATProto,
Expand Down
29 changes: 29 additions & 0 deletions tests/test_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,35 @@ def test_follow_bot_user_copy_id_refreshes_profile(self):
self.assertTrue(user.is_enabled(Fake))
self.assertEqual(['eefake:user'], ExplicitEnableFake.fetched)

def test_follow_bot_user_overrides_opt_out(self):
# bot user
self.make_user('fa.brid.gy', cls=Web,
copies=[Target(uri='eefake:bot', protocol='eefake')])

# profile that's opted out
actor = {
'id': 'eefake:user',
'summary': '#nobridge',
}
user = self.make_user('eefake:user', cls=ExplicitEnableFake, obj_as1=actor)
self.assertFalse(user.is_enabled(Fake))
ExplicitEnableFake.fetchable = {'eefake:user': actor}

# follow should override opt out
with self.assertRaises(NoContent):
ExplicitEnableFake.receive_as1({
'objectType': 'activity',
'verb': 'follow',
'id': 'eefake:follow',
'actor': 'eefake:user',
'object': 'eefake:bot',
})

user = user.key.get()
self.assertIsNone(user.status)
self.assertTrue(user.is_enabled(Fake))
self.assertEqual(['eefake:user'], ExplicitEnableFake.fetched)

def test_dm_no_yes_sets_enabled_protocols(self):
# bot user
self.make_user('fa.brid.gy', cls=Web)
Expand Down

0 comments on commit e4f9966

Please sign in to comment.