Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Consistently exclude from user_directory, round 2 #10960

Merged
merged 12 commits into from
Oct 4, 2021
133 changes: 133 additions & 0 deletions tests/handlers/test_user_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Tuple
from unittest.mock import Mock, patch
from urllib.parse import quote

Expand All @@ -29,6 +30,7 @@

from tests import unittest
from tests.storage.test_user_directory import GetUserDirectoryTables
from tests.test_utils.event_injection import inject_member_event
from tests.unittest import override_config


Expand Down Expand Up @@ -79,6 +81,137 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
self.event_creation_handler = self.hs.get_event_creation_handler()
self.user_dir_helper = GetUserDirectoryTables(self.store)

def test_normal_user_pair(self) -> None:
"""Sanity check that the room-sharing tables are updated correctly."""
alice = self.register_user("alice", "pass")
alice_token = self.login(alice, "pass")
bob = self.register_user("bob", "pass")
bob_token = self.login(bob, "pass")

public = self.helper.create_room_as(
alice,
is_public=True,
extra_content={"visibility": "public"},
tok=alice_token,
)
private = self.helper.create_room_as(alice, is_public=False, tok=alice_token)
self.helper.invite(private, alice, bob, tok=alice_token)
self.helper.join(public, bob, tok=bob_token)
self.helper.join(private, bob, tok=bob_token)

# Alice also makes a second public room but no-one else joins
public2 = self.helper.create_room_as(
alice,
is_public=True,
extra_content={"visibility": "public"},
tok=alice_token,
)

users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
in_public = self.get_success(self.user_dir_helper.get_users_in_public_rooms())
in_private = self.get_success(
self.user_dir_helper.get_users_who_share_private_rooms()
)

self.assertEqual(users, {alice, bob})
self.assertEqual(
set(in_public), {(alice, public), (bob, public), (alice, public2)}
)
self.assertEqual(
self.user_dir_helper._compress_shared(in_private),
{(alice, bob, private), (bob, alice, private)},
)

# The next three tests (test_population_excludes_*) all setup
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# The next three tests (test_population_excludes_*) all setup
# The next three tests (test_population_excludes_*) all set up

# - A normal user included in the user dir
# - A public and private room created by that user
# - A user excluded from the room dir, belonging to both rooms

# They match similar logic in storage/test_user_directory. But that tests
# rebuilding the directory; this tests updating it incrementally.

def test_excludes_support_user(self) -> None:
alice = self.register_user("alice", "pass")
alice_token = self.login(alice, "pass")
support = "@support1:test"
self.get_success(
self.store.register_user(
user_id=support, password_hash=None, user_type=UserTypes.SUPPORT
)
)

public, private = self._create_rooms_and_inject_memberships(
alice, alice_token, support
)
self._check_only_one_user_in_directory(alice, public)

def test_excludes_deactivated_user(self) -> None:
admin = self.register_user("admin", "pass", admin=True)
admin_token = self.login(admin, "pass")
user = self.register_user("naughty", "pass")

# Deactivate the user.
channel = self.make_request(
"PUT",
f"/_synapse/admin/v2/users/{user}",
access_token=admin_token,
content={"deactivated": True},
)
self.assertEqual(channel.code, 200)
self.assertEqual(channel.json_body["deactivated"], True)

# Join the deactivated user to rooms owned by the admin.
# Is this something that could actually happen outside of a test?
public, private = self._create_rooms_and_inject_memberships(
admin, admin_token, user
)
self._check_only_one_user_in_directory(admin, public)

def test_excludes_appservices_user(self) -> None:
# Register an AS user.
user = self.register_user("user", "pass")
token = self.login(user, "pass")
as_user = self.register_appservice_user("as_user_potato", self.appservice.token)

# Join the AS user to rooms owned by the normal user.
public, private = self._create_rooms_and_inject_memberships(
user, token, as_user
)
self._check_only_one_user_in_directory(user, public)

def _create_rooms_and_inject_memberships(
self, creator: str, token: str, joiner: str
) -> Tuple[str, str]:
"""Create a public and private room as a normal user.
Then get the `joiner` into those rooms.
"""
# TODO: Duplicates the same-named method in UserDirectoryInitialPopulationTest.
public_room = self.helper.create_room_as(
creator,
is_public=True,
# See https://github.com/matrix-org/synapse/issues/10951
extra_content={"visibility": "public"},
tok=token,
)
private_room = self.helper.create_room_as(creator, is_public=False, tok=token)

# HACK: get the user into these rooms
self.get_success(inject_member_event(self.hs, public_room, joiner, "join"))
self.get_success(inject_member_event(self.hs, private_room, joiner, "join"))

return public_room, private_room

def _check_only_one_user_in_directory(self, user: str, public: str) -> None:
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
in_public = self.get_success(self.user_dir_helper.get_users_in_public_rooms())
in_private = self.get_success(
self.user_dir_helper.get_users_who_share_private_rooms()
)

self.assertEqual(users, {user})
self.assertEqual(set(in_public), {(user, public)})
self.assertEqual(in_private, [])

def test_handle_local_profile_change_with_support_user(self) -> None:
support_user_id = "@support:test"
self.get_success(
Expand Down
99 changes: 73 additions & 26 deletions tests/storage/test_user_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from synapse.storage import DataStore
from synapse.util import Clock

from tests.test_utils.event_injection import inject_member_event
from tests.unittest import HomeserverTestCase, override_config

ALICE = "@alice:a"
Expand Down Expand Up @@ -237,34 +238,77 @@ def test_initial(self) -> None:
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, {u1, u2, u3})

# The next three tests (test_population_excludes_*) all setup
DMRobertson marked this conversation as resolved.
Show resolved Hide resolved
# - A normal user included in the user dir
# - A public and private room created by that user
# - A user excluded from the room dir, belonging to both rooms

# They match similar logic in handlers/test_user_directory.py But that tests
# updating the directory; this tests rebuilding it from scratch.

def _create_rooms_and_inject_memberships(
self, creator: str, token: str, joiner: str
) -> Tuple[str, str]:
"""Create a public and private room as a normal user.
Then get the `joiner` into those rooms.
"""
public_room = self.helper.create_room_as(
creator,
is_public=True,
# See https://github.com/matrix-org/synapse/issues/10951
extra_content={"visibility": "public"},
tok=token,
)
private_room = self.helper.create_room_as(creator, is_public=False, tok=token)

# HACK: get the user into these rooms
self.get_success(inject_member_event(self.hs, public_room, joiner, "join"))
self.get_success(inject_member_event(self.hs, private_room, joiner, "join"))

return public_room, private_room

def _check_room_sharing_tables(
self, normal_user: str, public_room: str, private_room: str
) -> None:
# After rebuilding the directory, we should only see the normal user.
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, {normal_user})
in_public_rooms = self.get_success(
self.user_dir_helper.get_users_in_public_rooms()
)
self.assertEqual(set(in_public_rooms), {(normal_user, public_room)})
in_private_rooms = self.get_success(
self.user_dir_helper.get_users_who_share_private_rooms()
)
self.assertEqual(in_private_rooms, [])

def test_population_excludes_support_user(self) -> None:
# Create a normal and support user.
user = self.register_user("user", "pass")
token = self.login(user, "pass")
support = "@support1:test"
self.get_success(
self.store.register_user(
user_id=support, password_hash=None, user_type=UserTypes.SUPPORT
)
)
# Check the support user is not in the directory.
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, set())

# TODO add support user to a public and private room. Check that
# users_in_public_rooms and users_who_share_private_rooms is empty.
# Join the support user to rooms owned by the normal user.
public, private = self._create_rooms_and_inject_memberships(
user, token, support
)

# Rebuild the directory. It should still exclude the support user.
# Rebuild the directory.
self._purge_and_rebuild_user_dir()
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, set())

# Check the support user is not in the directory.
self._check_room_sharing_tables(user, public, private)

def test_population_excludes_deactivated_user(self) -> None:
user = self.register_user("naughty", "pass")
admin = self.register_user("admin", "pass", admin=True)
admin_token = self.login(admin, "pass")

# Directory contains both users to start with.
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, {admin, user})

# Deactivate the user.
channel = self.make_request(
"PUT",
Expand All @@ -275,29 +319,32 @@ def test_population_excludes_deactivated_user(self) -> None:
self.assertEqual(channel.code, 200)
self.assertEqual(channel.json_body["deactivated"], True)

# They should no longer be in the directory.
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, {admin})
# Join the deactivated user to rooms owned by the admin.
# Is this something that could actually happen outside of a test?
public, private = self._create_rooms_and_inject_memberships(
admin, admin_token, user
)

# Rebuild the user dir. The deactivated user should still be missing.
# Rebuild the user dir. The deactivated user should be missing.
self._purge_and_rebuild_user_dir()
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, {admin})
self._check_room_sharing_tables(admin, public, private)

def test_population_excludes_appservice_user(self) -> None:
# Register an AS user.
self.register_appservice_user("as_user_potato", self.appservice.token)
# TODO put this user in a public and private room with someone else
user = self.register_user("user", "pass")
token = self.login(user, "pass")
as_user = self.register_appservice_user("as_user_potato", self.appservice.token)

users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, set())
# TODO assert the room sharing tables are as expected
# Join the AS user to rooms owned by the normal user.
public, private = self._create_rooms_and_inject_memberships(
user, token, as_user
)

# Rebuild the directory.
self._purge_and_rebuild_user_dir()

# TODO assert the room sharing tables are as expected
users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
self.assertEqual(users, set())
# Check the AS user is not in the directory.
self._check_room_sharing_tables(user, public, private)


class UserDirectoryStoreTestCase(HomeserverTestCase):
Expand Down