Skip to content

Commit fe8bb62

Browse files
Add the ability to exclude remote users in user directory search results (#18300)
This change adds a new configuration `user_directory.exclude_remote_users`, which defaults to False. When set to True, remote users will not appear in user directory search results. ### Pull Request Checklist <!-- Please read https://element-hq.github.io/synapse/latest/development/contributing_guide.html before submitting your pull request --> * [x] Pull request is based on the develop branch * [x] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [x] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters)) --------- Co-authored-by: Andrew Morgan <[email protected]>
1 parent b8146d4 commit fe8bb62

File tree

6 files changed

+84
-4
lines changed

6 files changed

+84
-4
lines changed

changelog.d/18300.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add config option `user_directory.exclude_remote_users` which, when enabled, excludes remote users from user directory search results.

docs/usage/configuration/config_documentation.md

+2
Original file line numberDiff line numberDiff line change
@@ -4095,6 +4095,7 @@ This option has the following sub-options:
40954095
* `prefer_local_users`: Defines whether to prefer local users in search query results.
40964096
If set to true, local users are more likely to appear above remote users when searching the
40974097
user directory. Defaults to false.
4098+
* `exclude_remote_users`: If set to true, the search will only return local users. Defaults to false.
40984099
* `show_locked_users`: Defines whether to show locked users in search query results. Defaults to false.
40994100

41004101
Example configuration:
@@ -4103,6 +4104,7 @@ user_directory:
41034104
enabled: false
41044105
search_all_users: true
41054106
prefer_local_users: true
4107+
exclude_remote_users: false
41064108
show_locked_users: true
41074109
```
41084110
---

synapse/config/user_directory.py

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
3838
self.user_directory_search_all_users = user_directory_config.get(
3939
"search_all_users", False
4040
)
41+
self.user_directory_exclude_remote_users = user_directory_config.get(
42+
"exclude_remote_users", False
43+
)
4144
self.user_directory_search_prefer_local_users = user_directory_config.get(
4245
"prefer_local_users", False
4346
)

synapse/handlers/user_directory.py

+3
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def __init__(self, hs: "HomeServer"):
108108
self.is_mine_id = hs.is_mine_id
109109
self.update_user_directory = hs.config.worker.should_update_user_directory
110110
self.search_all_users = hs.config.userdirectory.user_directory_search_all_users
111+
self.exclude_remote_users = (
112+
hs.config.userdirectory.user_directory_exclude_remote_users
113+
)
111114
self.show_locked_users = hs.config.userdirectory.show_locked_users
112115
self._spam_checker_module_callbacks = hs.get_module_api_callbacks().spam_checker
113116
self._hs = hs

synapse/storage/databases/main/user_directory.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -1037,11 +1037,11 @@ async def search_user_dir(
10371037
}
10381038
"""
10391039

1040+
join_args: Tuple[str, ...] = (user_id,)
1041+
10401042
if self.hs.config.userdirectory.user_directory_search_all_users:
1041-
join_args = (user_id,)
10421043
where_clause = "user_id != ?"
10431044
else:
1044-
join_args = (user_id,)
10451045
where_clause = """
10461046
(
10471047
EXISTS (select 1 from users_in_public_rooms WHERE user_id = t.user_id)
@@ -1055,6 +1055,14 @@ async def search_user_dir(
10551055
if not show_locked_users:
10561056
where_clause += " AND (u.locked IS NULL OR u.locked = FALSE)"
10571057

1058+
# Adjust the JOIN type based on the exclude_remote_users flag (the users
1059+
# table only contains local users so an inner join is a good way to
1060+
# to exclude remote users)
1061+
if self.hs.config.userdirectory.user_directory_exclude_remote_users:
1062+
join_type = "JOIN"
1063+
else:
1064+
join_type = "LEFT JOIN"
1065+
10581066
# We allow manipulating the ranking algorithm by injecting statements
10591067
# based on config options.
10601068
additional_ordering_statements = []
@@ -1086,7 +1094,7 @@ async def search_user_dir(
10861094
SELECT d.user_id AS user_id, display_name, avatar_url
10871095
FROM matching_users as t
10881096
INNER JOIN user_directory AS d USING (user_id)
1089-
LEFT JOIN users AS u ON t.user_id = u.name
1097+
%(join_type)s users AS u ON t.user_id = u.name
10901098
WHERE
10911099
%(where_clause)s
10921100
ORDER BY
@@ -1115,6 +1123,7 @@ async def search_user_dir(
11151123
""" % {
11161124
"where_clause": where_clause,
11171125
"order_case_statements": " ".join(additional_ordering_statements),
1126+
"join_type": join_type,
11181127
}
11191128
args = (
11201129
(full_query,)
@@ -1142,7 +1151,7 @@ async def search_user_dir(
11421151
SELECT d.user_id AS user_id, display_name, avatar_url
11431152
FROM user_directory_search as t
11441153
INNER JOIN user_directory AS d USING (user_id)
1145-
LEFT JOIN users AS u ON t.user_id = u.name
1154+
%(join_type)s users AS u ON t.user_id = u.name
11461155
WHERE
11471156
%(where_clause)s
11481157
AND value MATCH ?
@@ -1155,6 +1164,7 @@ async def search_user_dir(
11551164
""" % {
11561165
"where_clause": where_clause,
11571166
"order_statements": " ".join(additional_ordering_statements),
1167+
"join_type": join_type,
11581168
}
11591169
args = join_args + (search_query,) + ordering_arguments + (limit + 1,)
11601170
else:

tests/handlers/test_user_directory.py

+61
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,67 @@ def test_prefer_local_users(self) -> None:
992992
[self.assertIn(user, local_users) for user in received_user_id_ordering[:3]]
993993
[self.assertIn(user, remote_users) for user in received_user_id_ordering[3:]]
994994

995+
@override_config(
996+
{
997+
"user_directory": {
998+
"enabled": True,
999+
"search_all_users": True,
1000+
"exclude_remote_users": True,
1001+
}
1002+
}
1003+
)
1004+
def test_exclude_remote_users(self) -> None:
1005+
"""Tests that only local users are returned when
1006+
user_directory.exclude_remote_users is True.
1007+
"""
1008+
1009+
# Create a room and few users to test the directory with
1010+
searching_user = self.register_user("searcher", "password")
1011+
searching_user_tok = self.login("searcher", "password")
1012+
1013+
room_id = self.helper.create_room_as(
1014+
searching_user,
1015+
room_version=RoomVersions.V1.identifier,
1016+
tok=searching_user_tok,
1017+
)
1018+
1019+
# Create a few local users and join them to the room
1020+
local_user_1 = self.register_user("user_xxxxx", "password")
1021+
local_user_2 = self.register_user("user_bbbbb", "password")
1022+
local_user_3 = self.register_user("user_zzzzz", "password")
1023+
1024+
self._add_user_to_room(room_id, RoomVersions.V1, local_user_1)
1025+
self._add_user_to_room(room_id, RoomVersions.V1, local_user_2)
1026+
self._add_user_to_room(room_id, RoomVersions.V1, local_user_3)
1027+
1028+
# Create a few "remote" users and join them to the room
1029+
remote_user_1 = "@user_aaaaa:remote_server"
1030+
remote_user_2 = "@user_yyyyy:remote_server"
1031+
remote_user_3 = "@user_ccccc:remote_server"
1032+
self._add_user_to_room(room_id, RoomVersions.V1, remote_user_1)
1033+
self._add_user_to_room(room_id, RoomVersions.V1, remote_user_2)
1034+
self._add_user_to_room(room_id, RoomVersions.V1, remote_user_3)
1035+
1036+
local_users = [local_user_1, local_user_2, local_user_3]
1037+
remote_users = [remote_user_1, remote_user_2, remote_user_3]
1038+
1039+
# The local searching user searches for the term "user", which other users have
1040+
# in their user id
1041+
results = self.get_success(
1042+
self.handler.search_users(searching_user, "user", 20)
1043+
)["results"]
1044+
received_user_ids = [result["user_id"] for result in results]
1045+
1046+
for user in local_users:
1047+
self.assertIn(
1048+
user, received_user_ids, f"Local user {user} not found in results"
1049+
)
1050+
1051+
for user in remote_users:
1052+
self.assertNotIn(
1053+
user, received_user_ids, f"Remote user {user} should not be in results"
1054+
)
1055+
9951056
def _add_user_to_room(
9961057
self,
9971058
room_id: str,

0 commit comments

Comments
 (0)