Do not assume that account data is of the correct form. (#8454)

This fixes a bug where `m.ignored_user_list` was assumed to be a dict,
leading to odd behavior for users who set it to something else.
This commit is contained in:
Patrick Cloke 2020-10-05 09:28:05 -04:00 committed by GitHub
parent e3debf9682
commit c5251c6fbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 34 additions and 21 deletions

1
changelog.d/8454.bugfix Normal file
View file

@ -0,0 +1 @@
Fix a longstanding bug where invalid ignored users in account data could break clients.

View file

@ -155,3 +155,8 @@ class EventContentFields:
class RoomEncryptionAlgorithms: class RoomEncryptionAlgorithms:
MEGOLM_V1_AES_SHA2 = "m.megolm.v1.aes-sha2" MEGOLM_V1_AES_SHA2 = "m.megolm.v1.aes-sha2"
DEFAULT = MEGOLM_V1_AES_SHA2 DEFAULT = MEGOLM_V1_AES_SHA2
class AccountDataTypes:
DIRECT = "m.direct"
IGNORED_USER_LIST = "m.ignored_user_list"

View file

@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple, Union
from unpaddedbase64 import encode_base64 from unpaddedbase64 import encode_base64
from synapse import types from synapse import types
from synapse.api.constants import MAX_DEPTH, EventTypes, Membership from synapse.api.constants import MAX_DEPTH, AccountDataTypes, EventTypes, Membership
from synapse.api.errors import ( from synapse.api.errors import (
AuthError, AuthError,
Codes, Codes,
@ -247,7 +247,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
user_account_data, _ = await self.store.get_account_data_for_user(user_id) user_account_data, _ = await self.store.get_account_data_for_user(user_id)
# Copy direct message state if applicable # Copy direct message state if applicable
direct_rooms = user_account_data.get("m.direct", {}) direct_rooms = user_account_data.get(AccountDataTypes.DIRECT, {})
# Check which key this room is under # Check which key this room is under
if isinstance(direct_rooms, dict): if isinstance(direct_rooms, dict):
@ -258,7 +258,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
# Save back to user's m.direct account data # Save back to user's m.direct account data
await self.store.add_account_data_for_user( await self.store.add_account_data_for_user(
user_id, "m.direct", direct_rooms user_id, AccountDataTypes.DIRECT, direct_rooms
) )
break break

View file

@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, Any, Dict, FrozenSet, List, Optional, Set, Tup
import attr import attr
from prometheus_client import Counter from prometheus_client import Counter
from synapse.api.constants import EventTypes, Membership from synapse.api.constants import AccountDataTypes, EventTypes, Membership
from synapse.api.filtering import FilterCollection from synapse.api.filtering import FilterCollection
from synapse.events import EventBase from synapse.events import EventBase
from synapse.logging.context import current_context from synapse.logging.context import current_context
@ -1378,13 +1378,16 @@ class SyncHandler:
return set(), set(), set(), set() return set(), set(), set(), set()
ignored_account_data = await self.store.get_global_account_data_by_type_for_user( ignored_account_data = await self.store.get_global_account_data_by_type_for_user(
"m.ignored_user_list", user_id=user_id AccountDataTypes.IGNORED_USER_LIST, user_id=user_id
) )
# If there is ignored users account data and it matches the proper type,
# then use it.
ignored_users = frozenset() # type: FrozenSet[str]
if ignored_account_data: if ignored_account_data:
ignored_users = ignored_account_data.get("ignored_users", {}).keys() ignored_users_data = ignored_account_data.get("ignored_users", {})
else: if isinstance(ignored_users_data, dict):
ignored_users = frozenset() ignored_users = frozenset(ignored_users_data.keys())
if since_token: if since_token:
room_changes = await self._get_rooms_changed( room_changes = await self._get_rooms_changed(
@ -1478,7 +1481,7 @@ class SyncHandler:
return False return False
async def _get_rooms_changed( async def _get_rooms_changed(
self, sync_result_builder: "SyncResultBuilder", ignored_users: Set[str] self, sync_result_builder: "SyncResultBuilder", ignored_users: FrozenSet[str]
) -> _RoomChanges: ) -> _RoomChanges:
"""Gets the the changes that have happened since the last sync. """Gets the the changes that have happened since the last sync.
""" """
@ -1690,7 +1693,7 @@ class SyncHandler:
return _RoomChanges(room_entries, invited, newly_joined_rooms, newly_left_rooms) return _RoomChanges(room_entries, invited, newly_joined_rooms, newly_left_rooms)
async def _get_all_rooms( async def _get_all_rooms(
self, sync_result_builder: "SyncResultBuilder", ignored_users: Set[str] self, sync_result_builder: "SyncResultBuilder", ignored_users: FrozenSet[str]
) -> _RoomChanges: ) -> _RoomChanges:
"""Returns entries for all rooms for the user. """Returns entries for all rooms for the user.
@ -1764,7 +1767,7 @@ class SyncHandler:
async def _generate_room_entry( async def _generate_room_entry(
self, self,
sync_result_builder: "SyncResultBuilder", sync_result_builder: "SyncResultBuilder",
ignored_users: Set[str], ignored_users: FrozenSet[str],
room_builder: "RoomSyncResultBuilder", room_builder: "RoomSyncResultBuilder",
ephemeral: List[JsonDict], ephemeral: List[JsonDict],
tags: Optional[Dict[str, Dict[str, Any]]], tags: Optional[Dict[str, Dict[str, Any]]],

View file

@ -18,6 +18,7 @@ import abc
import logging import logging
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
from synapse.api.constants import AccountDataTypes
from synapse.storage._base import SQLBaseStore, db_to_json from synapse.storage._base import SQLBaseStore, db_to_json
from synapse.storage.database import DatabasePool from synapse.storage.database import DatabasePool
from synapse.storage.util.id_generators import StreamIdGenerator from synapse.storage.util.id_generators import StreamIdGenerator
@ -291,14 +292,18 @@ class AccountDataWorkerStore(SQLBaseStore, metaclass=abc.ABCMeta):
self, ignored_user_id: str, ignorer_user_id: str, cache_context: _CacheContext self, ignored_user_id: str, ignorer_user_id: str, cache_context: _CacheContext
) -> bool: ) -> bool:
ignored_account_data = await self.get_global_account_data_by_type_for_user( ignored_account_data = await self.get_global_account_data_by_type_for_user(
"m.ignored_user_list", AccountDataTypes.IGNORED_USER_LIST,
ignorer_user_id, ignorer_user_id,
on_invalidate=cache_context.invalidate, on_invalidate=cache_context.invalidate,
) )
if not ignored_account_data: if not ignored_account_data:
return False return False
try:
return ignored_user_id in ignored_account_data.get("ignored_users", {}) return ignored_user_id in ignored_account_data.get("ignored_users", {})
except TypeError:
# The type of the ignored_users field is invalid.
return False
class AccountDataStore(AccountDataWorkerStore): class AccountDataStore(AccountDataWorkerStore):

View file

@ -16,7 +16,7 @@
import logging import logging
import operator import operator
from synapse.api.constants import EventTypes, Membership from synapse.api.constants import AccountDataTypes, EventTypes, Membership
from synapse.events.utils import prune_event from synapse.events.utils import prune_event
from synapse.storage import Storage from synapse.storage import Storage
from synapse.storage.state import StateFilter from synapse.storage.state import StateFilter
@ -77,15 +77,14 @@ async def filter_events_for_client(
) )
ignore_dict_content = await storage.main.get_global_account_data_by_type_for_user( ignore_dict_content = await storage.main.get_global_account_data_by_type_for_user(
"m.ignored_user_list", user_id AccountDataTypes.IGNORED_USER_LIST, user_id
) )
# FIXME: This will explode if people upload something incorrect. ignore_list = frozenset()
ignore_list = frozenset( if ignore_dict_content:
ignore_dict_content.get("ignored_users", {}).keys() ignored_users_dict = ignore_dict_content.get("ignored_users", {})
if ignore_dict_content if isinstance(ignored_users_dict, dict):
else [] ignore_list = frozenset(ignored_users_dict.keys())
)
erased_senders = await storage.main.are_users_erased((e.sender for e in events)) erased_senders = await storage.main.are_users_erased((e.sender for e in events))