-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Consistently exclude users from directory #10914
Changes from all commits
da02259
c9913cf
ad7ea64
acf94bf
556566f
969d0d1
ff89fb8
ff177a8
2e32e1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Fix a long-standing bug where rebuilding the user directory wouldn't exclude support and disabled users. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -132,12 +132,7 @@ async def handle_local_profile_change( | |
# FIXME(#3714): We should probably do this in the same worker as all | ||
# the other changes. | ||
|
||
# Support users are for diagnostics and should not appear in the user directory. | ||
is_support = await self.store.is_support_user(user_id) | ||
# When change profile information of deactivated user it should not appear in the user directory. | ||
is_deactivated = await self.store.get_user_deactivated_status(user_id) | ||
|
||
if not (is_support or is_deactivated): | ||
if not await self.store.is_excluded_from_user_dir(user_id): | ||
await self.store.update_profile_in_user_dir( | ||
user_id, profile.display_name, profile.avatar_url | ||
) | ||
|
@@ -229,8 +224,8 @@ async def _handle_deltas(self, deltas: List[Dict[str, Any]]) -> None: | |
else: | ||
logger.debug("Server is still in room: %r", room_id) | ||
|
||
is_support = await self.store.is_support_user(state_key) | ||
if not is_support: | ||
excluded = await self.store.is_excluded_from_user_dir(state_key) | ||
if not excluded: | ||
if change is MatchChange.no_change: | ||
# Handle any profile changes | ||
await self._handle_profile_change( | ||
|
@@ -357,12 +352,8 @@ async def _handle_new_user( | |
# First, if they're our user then we need to update for every user | ||
if self.is_mine_id(user_id): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think this is redundant? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we'll check to see if they're local within But. I think putting the localness check into I think it's going to be clearer if we have a function |
||
|
||
is_appservice = self.store.get_if_app_services_interested_in_user( | ||
user_id | ||
) | ||
|
||
# We don't care about appservice users. | ||
if not is_appservice: | ||
excluded = await self.store.is_excluded_from_user_dir(user_id) | ||
if not excluded: | ||
for other_user_id in other_users_in_room: | ||
if user_id == other_user_id: | ||
continue | ||
|
@@ -374,10 +365,8 @@ async def _handle_new_user( | |
if user_id == other_user_id: | ||
continue | ||
|
||
is_appservice = self.store.get_if_app_services_interested_in_user( | ||
other_user_id | ||
) | ||
if self.is_mine_id(other_user_id) and not is_appservice: | ||
excluded = await self.store.is_excluded_from_user_dir(other_user_id) | ||
if not excluded: | ||
to_insert.add((other_user_id, user_id)) | ||
|
||
if to_insert: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -238,15 +238,16 @@ def _get_next_batch( | |
|
||
# Update each user in the user directory. | ||
for user_id, profile in users_with_profile.items(): | ||
await self.update_profile_in_user_dir( | ||
user_id, profile.display_name, profile.avatar_url | ||
) | ||
if not await self.is_excluded_from_user_dir(user_id): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's probably fine, but for the record: what's the logic behind adding this here, and at line 353, where there was no check before? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consistency with Rebuilding the user directory from scratch (here) should have the same outcome as starting with an empty directory and building it by listening for membership events ( |
||
await self.update_profile_in_user_dir( | ||
user_id, profile.display_name, profile.avatar_url | ||
) | ||
|
||
to_insert = set() | ||
|
||
if is_public: | ||
for user_id in users_with_profile: | ||
if self.get_if_app_services_interested_in_user(user_id): | ||
if await self.is_excluded_from_user_dir(user_id): | ||
continue | ||
|
||
to_insert.add(user_id) | ||
|
@@ -259,7 +260,7 @@ def _get_next_batch( | |
if not self.hs.is_mine_id(user_id): | ||
continue | ||
|
||
if self.get_if_app_services_interested_in_user(user_id): | ||
if await self.is_excluded_from_user_dir(user_id): | ||
continue | ||
|
||
for other_user_id in users_with_profile: | ||
|
@@ -349,10 +350,11 @@ def _get_next_batch(txn: LoggingTransaction) -> Optional[List[str]]: | |
) | ||
|
||
for user_id in users_to_work_on: | ||
profile = await self.get_profileinfo(get_localpart_from_id(user_id)) | ||
await self.update_profile_in_user_dir( | ||
user_id, profile.display_name, profile.avatar_url | ||
) | ||
if not await self.is_excluded_from_user_dir(user_id): | ||
profile = await self.get_profileinfo(get_localpart_from_id(user_id)) | ||
await self.update_profile_in_user_dir( | ||
user_id, profile.display_name, profile.avatar_url | ||
) | ||
|
||
# We've finished processing a user. Delete it from the table. | ||
await self.db_pool.simple_delete_one( | ||
|
@@ -369,6 +371,22 @@ def _get_next_batch(txn: LoggingTransaction) -> Optional[List[str]]: | |
|
||
return len(users_to_work_on) | ||
|
||
async def is_excluded_from_user_dir(self, user: str) -> bool: | ||
"""Certain classes of local user are omitted from the user directory. | ||
Is this user one of them? | ||
""" | ||
if self.hs.is_mine_id(user): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please can we do an early return rather than a big nested if statement? |
||
# Support users are for diagnostics and should not appear in the user directory. | ||
if await self.is_support_user(user): | ||
return True | ||
# Deactivated users aren't contactable, so should not appear in the user directory. | ||
if await self.get_user_deactivated_status(user): | ||
return True | ||
# App service users aren't usually contactable, so exclude them. | ||
if self.get_if_app_services_interested_in_user(user): | ||
return True | ||
Comment on lines
+386
to
+387
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm unsure what to think about this. Both yes and no make sense here. No, on a lot of networks (IRC probably? Discord when using a guild-bot.), the users aren't contactable. I would guess that the 'No' cases outweigh the 'Yes' cases, so I think what you're doing is sensible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like most of the code currently excludes AS users from the directory, but there are enough bugs that they leak through. At least we're making it consistent here. There might be scope for a future change where we make this configurable per-appservice, but let's leave it for now.
Comment on lines
+386
to
+387
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wonder if we should do this test first, since it doesn't require a database hit and should be pretty quick. |
||
return False | ||
|
||
async def is_room_world_readable_or_publicly_joinable(self, room_id: str) -> bool: | ||
"""Check if the room is either world_readable or publically joinable""" | ||
|
||
|
@@ -527,7 +545,7 @@ async def get_user_in_directory(self, user_id: str) -> Optional[Dict[str, str]]: | |
desc="get_user_in_directory", | ||
) | ||
|
||
async def update_user_directory_stream_pos(self, stream_id: int) -> None: | ||
async def update_user_directory_stream_pos(self, stream_id: Optional[int]) -> None: | ||
await self.db_pool.simple_update_one( | ||
table="user_directory_stream_pos", | ||
keyvalues={}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the double-negative here a bit mind-bending. Perhaps we should instead have an
should_include_in_user_dir
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad it's not just me. I was erring towards preserving the original logic below as much as possible, but think it'll be better this way round.