Do not assume that the contents dictionary includes history_visibility. (#8945)

This commit is contained in:
Patrick Cloke 2020-12-16 08:46:37 -05:00 committed by GitHub
parent 01333681bc
commit be2db93b3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 58 additions and 28 deletions

1
changelog.d/8945.bugfix Normal file
View File

@ -0,0 +1 @@
Fix a bug where 500 errors would be returned if the `m.room_history_visibility` event had invalid content.

View File

@ -23,7 +23,7 @@ from twisted.web.server import Request
import synapse.types import synapse.types
from synapse import event_auth from synapse import event_auth
from synapse.api.auth_blocking import AuthBlocking from synapse.api.auth_blocking import AuthBlocking
from synapse.api.constants import EventTypes, Membership from synapse.api.constants import EventTypes, HistoryVisibility, Membership
from synapse.api.errors import ( from synapse.api.errors import (
AuthError, AuthError,
Codes, Codes,
@ -648,7 +648,8 @@ class Auth:
) )
if ( if (
visibility visibility
and visibility.content["history_visibility"] == "world_readable" and visibility.content.get("history_visibility")
== HistoryVisibility.WORLD_READABLE
): ):
return Membership.JOIN, None return Membership.JOIN, None
raise AuthError( raise AuthError(

View File

@ -160,3 +160,10 @@ class RoomEncryptionAlgorithms:
class AccountDataTypes: class AccountDataTypes:
DIRECT = "m.direct" DIRECT = "m.direct"
IGNORED_USER_LIST = "m.ignored_user_list" IGNORED_USER_LIST = "m.ignored_user_list"
class HistoryVisibility:
INVITED = "invited"
JOINED = "joined"
SHARED = "shared"
WORLD_READABLE = "world_readable"

View File

@ -27,6 +27,7 @@ from typing import TYPE_CHECKING, Any, Awaitable, Dict, List, Optional, Tuple
from synapse.api.constants import ( from synapse.api.constants import (
EventTypes, EventTypes,
HistoryVisibility,
JoinRules, JoinRules,
Membership, Membership,
RoomCreationPreset, RoomCreationPreset,
@ -81,21 +82,21 @@ class RoomCreationHandler(BaseHandler):
self._presets_dict = { self._presets_dict = {
RoomCreationPreset.PRIVATE_CHAT: { RoomCreationPreset.PRIVATE_CHAT: {
"join_rules": JoinRules.INVITE, "join_rules": JoinRules.INVITE,
"history_visibility": "shared", "history_visibility": HistoryVisibility.SHARED,
"original_invitees_have_ops": False, "original_invitees_have_ops": False,
"guest_can_join": True, "guest_can_join": True,
"power_level_content_override": {"invite": 0}, "power_level_content_override": {"invite": 0},
}, },
RoomCreationPreset.TRUSTED_PRIVATE_CHAT: { RoomCreationPreset.TRUSTED_PRIVATE_CHAT: {
"join_rules": JoinRules.INVITE, "join_rules": JoinRules.INVITE,
"history_visibility": "shared", "history_visibility": HistoryVisibility.SHARED,
"original_invitees_have_ops": True, "original_invitees_have_ops": True,
"guest_can_join": True, "guest_can_join": True,
"power_level_content_override": {"invite": 0}, "power_level_content_override": {"invite": 0},
}, },
RoomCreationPreset.PUBLIC_CHAT: { RoomCreationPreset.PUBLIC_CHAT: {
"join_rules": JoinRules.PUBLIC, "join_rules": JoinRules.PUBLIC,
"history_visibility": "shared", "history_visibility": HistoryVisibility.SHARED,
"original_invitees_have_ops": False, "original_invitees_have_ops": False,
"guest_can_join": False, "guest_can_join": False,
"power_level_content_override": {}, "power_level_content_override": {},

View File

@ -20,7 +20,7 @@ from typing import Any, Dict, Optional
import msgpack import msgpack
from unpaddedbase64 import decode_base64, encode_base64 from unpaddedbase64 import decode_base64, encode_base64
from synapse.api.constants import EventTypes, JoinRules from synapse.api.constants import EventTypes, HistoryVisibility, JoinRules
from synapse.api.errors import Codes, HttpResponseException from synapse.api.errors import Codes, HttpResponseException
from synapse.types import ThirdPartyInstanceID from synapse.types import ThirdPartyInstanceID
from synapse.util.caches.descriptors import cached from synapse.util.caches.descriptors import cached
@ -159,7 +159,8 @@ class RoomListHandler(BaseHandler):
"canonical_alias": room["canonical_alias"], "canonical_alias": room["canonical_alias"],
"num_joined_members": room["joined_members"], "num_joined_members": room["joined_members"],
"avatar_url": room["avatar"], "avatar_url": room["avatar"],
"world_readable": room["history_visibility"] == "world_readable", "world_readable": room["history_visibility"]
== HistoryVisibility.WORLD_READABLE,
"guest_can_join": room["guest_access"] == "can_join", "guest_can_join": room["guest_access"] == "can_join",
} }
@ -317,7 +318,7 @@ class RoomListHandler(BaseHandler):
visibility = None visibility = None
if visibility_event: if visibility_event:
visibility = visibility_event.content.get("history_visibility", None) visibility = visibility_event.content.get("history_visibility", None)
result["world_readable"] = visibility == "world_readable" result["world_readable"] = visibility == HistoryVisibility.WORLD_READABLE
guest_event = current_state.get((EventTypes.GuestAccess, "")) guest_event = current_state.get((EventTypes.GuestAccess, ""))
guest = None guest = None

View File

@ -16,7 +16,7 @@
import logging import logging
import synapse.metrics import synapse.metrics
from synapse.api.constants import EventTypes, JoinRules, Membership from synapse.api.constants import EventTypes, HistoryVisibility, JoinRules, Membership
from synapse.handlers.state_deltas import StateDeltasHandler from synapse.handlers.state_deltas import StateDeltasHandler
from synapse.metrics.background_process_metrics import run_as_background_process from synapse.metrics.background_process_metrics import run_as_background_process
from synapse.storage.roommember import ProfileInfo from synapse.storage.roommember import ProfileInfo
@ -250,7 +250,7 @@ class UserDirectoryHandler(StateDeltasHandler):
prev_event_id, prev_event_id,
event_id, event_id,
key_name="history_visibility", key_name="history_visibility",
public_value="world_readable", public_value=HistoryVisibility.WORLD_READABLE,
) )
elif typ == EventTypes.JoinRules: elif typ == EventTypes.JoinRules:
change = await self._get_key_change( change = await self._get_key_change(

View File

@ -34,7 +34,7 @@ from prometheus_client import Counter
from twisted.internet import defer from twisted.internet import defer
import synapse.server import synapse.server
from synapse.api.constants import EventTypes, Membership from synapse.api.constants import EventTypes, HistoryVisibility, Membership
from synapse.api.errors import AuthError from synapse.api.errors import AuthError
from synapse.events import EventBase from synapse.events import EventBase
from synapse.handlers.presence import format_user_presence_state from synapse.handlers.presence import format_user_presence_state
@ -611,7 +611,9 @@ class Notifier:
room_id, EventTypes.RoomHistoryVisibility, "" room_id, EventTypes.RoomHistoryVisibility, ""
) )
if state and "history_visibility" in state.content: if state and "history_visibility" in state.content:
return state.content["history_visibility"] == "world_readable" return (
state.content["history_visibility"] == HistoryVisibility.WORLD_READABLE
)
else: else:
return False return False

View File

@ -17,7 +17,7 @@ import logging
import re import re
from typing import Any, Dict, Iterable, Optional, Set, Tuple from typing import Any, Dict, Iterable, Optional, Set, Tuple
from synapse.api.constants import EventTypes, JoinRules from synapse.api.constants import EventTypes, HistoryVisibility, JoinRules
from synapse.storage.database import DatabasePool from synapse.storage.database import DatabasePool
from synapse.storage.databases.main.state import StateFilter from synapse.storage.databases.main.state import StateFilter
from synapse.storage.databases.main.state_deltas import StateDeltasStore from synapse.storage.databases.main.state_deltas import StateDeltasStore
@ -360,7 +360,10 @@ class UserDirectoryBackgroundUpdateStore(StateDeltasStore):
if hist_vis_id: if hist_vis_id:
hist_vis_ev = await self.get_event(hist_vis_id, allow_none=True) hist_vis_ev = await self.get_event(hist_vis_id, allow_none=True)
if hist_vis_ev: if hist_vis_ev:
if hist_vis_ev.content.get("history_visibility") == "world_readable": if (
hist_vis_ev.content.get("history_visibility")
== HistoryVisibility.WORLD_READABLE
):
return True return True
return False return False

View File

@ -12,11 +12,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import logging import logging
import operator import operator
from synapse.api.constants import AccountDataTypes, EventTypes, Membership from synapse.api.constants import (
AccountDataTypes,
EventTypes,
HistoryVisibility,
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
@ -25,7 +29,12 @@ from synapse.types import get_domain_from_id
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
VISIBILITY_PRIORITY = ("world_readable", "shared", "invited", "joined") VISIBILITY_PRIORITY = (
HistoryVisibility.WORLD_READABLE,
HistoryVisibility.SHARED,
HistoryVisibility.INVITED,
HistoryVisibility.JOINED,
)
MEMBERSHIP_PRIORITY = ( MEMBERSHIP_PRIORITY = (
@ -150,12 +159,14 @@ async def filter_events_for_client(
# get the room_visibility at the time of the event. # get the room_visibility at the time of the event.
visibility_event = state.get((EventTypes.RoomHistoryVisibility, ""), None) visibility_event = state.get((EventTypes.RoomHistoryVisibility, ""), None)
if visibility_event: if visibility_event:
visibility = visibility_event.content.get("history_visibility", "shared") visibility = visibility_event.content.get(
"history_visibility", HistoryVisibility.SHARED
)
else: else:
visibility = "shared" visibility = HistoryVisibility.SHARED
if visibility not in VISIBILITY_PRIORITY: if visibility not in VISIBILITY_PRIORITY:
visibility = "shared" visibility = HistoryVisibility.SHARED
# Always allow history visibility events on boundaries. This is done # Always allow history visibility events on boundaries. This is done
# by setting the effective visibility to the least restrictive # by setting the effective visibility to the least restrictive
@ -165,7 +176,7 @@ async def filter_events_for_client(
prev_visibility = prev_content.get("history_visibility", None) prev_visibility = prev_content.get("history_visibility", None)
if prev_visibility not in VISIBILITY_PRIORITY: if prev_visibility not in VISIBILITY_PRIORITY:
prev_visibility = "shared" prev_visibility = HistoryVisibility.SHARED
new_priority = VISIBILITY_PRIORITY.index(visibility) new_priority = VISIBILITY_PRIORITY.index(visibility)
old_priority = VISIBILITY_PRIORITY.index(prev_visibility) old_priority = VISIBILITY_PRIORITY.index(prev_visibility)
@ -210,17 +221,17 @@ async def filter_events_for_client(
# otherwise, it depends on the room visibility. # otherwise, it depends on the room visibility.
if visibility == "joined": if visibility == HistoryVisibility.JOINED:
# we weren't a member at the time of the event, so we can't # we weren't a member at the time of the event, so we can't
# see this event. # see this event.
return None return None
elif visibility == "invited": elif visibility == HistoryVisibility.INVITED:
# user can also see the event if they were *invited* at the time # user can also see the event if they were *invited* at the time
# of the event. # of the event.
return event if membership == Membership.INVITE else None return event if membership == Membership.INVITE else None
elif visibility == "shared" and is_peeking: elif visibility == HistoryVisibility.SHARED and is_peeking:
# if the visibility is shared, users cannot see the event unless # if the visibility is shared, users cannot see the event unless
# they have *subequently* joined the room (or were members at the # they have *subequently* joined the room (or were members at the
# time, of course) # time, of course)
@ -284,8 +295,10 @@ async def filter_events_for_server(
def check_event_is_visible(event, state): def check_event_is_visible(event, state):
history = state.get((EventTypes.RoomHistoryVisibility, ""), None) history = state.get((EventTypes.RoomHistoryVisibility, ""), None)
if history: if history:
visibility = history.content.get("history_visibility", "shared") visibility = history.content.get(
if visibility in ["invited", "joined"]: "history_visibility", HistoryVisibility.SHARED
)
if visibility in [HistoryVisibility.INVITED, HistoryVisibility.JOINED]:
# We now loop through all state events looking for # We now loop through all state events looking for
# membership states for the requesting server to determine # membership states for the requesting server to determine
# if the server is either in the room or has been invited # if the server is either in the room or has been invited
@ -305,7 +318,7 @@ async def filter_events_for_server(
if memtype == Membership.JOIN: if memtype == Membership.JOIN:
return True return True
elif memtype == Membership.INVITE: elif memtype == Membership.INVITE:
if visibility == "invited": if visibility == HistoryVisibility.INVITED:
return True return True
else: else:
# server has no users in the room: redact # server has no users in the room: redact
@ -336,7 +349,8 @@ async def filter_events_for_server(
else: else:
event_map = await storage.main.get_events(visibility_ids) event_map = await storage.main.get_events(visibility_ids)
all_open = all( all_open = all(
e.content.get("history_visibility") in (None, "shared", "world_readable") e.content.get("history_visibility")
in (None, HistoryVisibility.SHARED, HistoryVisibility.WORLD_READABLE)
for e in event_map.values() for e in event_map.values()
) )