Merge remote-tracking branch 'upstream/release-v1.59'

This commit is contained in:
Tulir Asokan 2022-05-18 14:09:18 +03:00
commit 4d95e65860
8 changed files with 62 additions and 12 deletions

View file

@ -1,3 +1,14 @@
Synapse 1.59.1 (2022-05-18)
===========================
This release fixes a long-standing issue which could prevent Synapse's user directory for updating properly.
Bugfixes
----------------
- Fix a long-standing bug where the user directory background process would fail to make forward progress if a user included a null codepoint in their display name or avatar. Contributed by Nick @ Beeper. ([\#12762](https://github.com/matrix-org/synapse/issues/12762))
Synapse 1.59.0 (2022-05-17) Synapse 1.59.0 (2022-05-17)
=========================== ===========================

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
matrix-synapse-py3 (1.59.1) stable; urgency=medium
* New Synapse release 1.59.1.
-- Synapse Packaging team <packages@matrix.org> Wed, 18 May 2022 11:41:46 +0100
matrix-synapse-py3 (1.59.0) stable; urgency=medium matrix-synapse-py3 (1.59.0) stable; urgency=medium
* New Synapse release 1.59.0. * New Synapse release 1.59.0.

View file

@ -54,7 +54,7 @@ skip_gitignore = true
[tool.poetry] [tool.poetry]
name = "matrix-synapse" name = "matrix-synapse"
version = "1.59.0" version = "1.59.1"
description = "Homeserver for the Matrix decentralised comms protocol" description = "Homeserver for the Matrix decentralised comms protocol"
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"] authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
license = "Apache-2.0" license = "Apache-2.0"

View file

@ -109,10 +109,10 @@ class RoomStateEventRestServlet(TransactionRestServlet):
self.auth = hs.get_auth() self.auth = hs.get_auth()
def register(self, http_server: HttpServer) -> None: def register(self, http_server: HttpServer) -> None:
# /room/$roomid/state/$eventtype # /rooms/$roomid/state/$eventtype
no_state_key = "/rooms/(?P<room_id>[^/]*)/state/(?P<event_type>[^/]*)$" no_state_key = "/rooms/(?P<room_id>[^/]*)/state/(?P<event_type>[^/]*)$"
# /room/$roomid/state/$eventtype/$statekey # /rooms/$roomid/state/$eventtype/$statekey
state_key = ( state_key = (
"/rooms/(?P<room_id>[^/]*)/state/" "/rooms/(?P<room_id>[^/]*)/state/"
"(?P<event_type>[^/]*)/(?P<state_key>[^/]*)$" "(?P<event_type>[^/]*)/(?P<state_key>[^/]*)$"

View file

@ -53,6 +53,7 @@ from synapse.storage.util.sequence import SequenceGenerator
from synapse.types import StateMap, get_domain_from_id from synapse.types import StateMap, get_domain_from_id
from synapse.util import json_encoder from synapse.util import json_encoder
from synapse.util.iterutils import batch_iter, sorted_topologically from synapse.util.iterutils import batch_iter, sorted_topologically
from synapse.util.stringutils import non_null_str_or_none
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.server import HomeServer from synapse.server import HomeServer
@ -1737,9 +1738,6 @@ class PersistEventsStore:
not affect the current local state. not affect the current local state.
""" """
def non_null_str_or_none(val: Any) -> Optional[str]:
return val if isinstance(val, str) and "\u0000" not in val else None
self.db_pool.simple_insert_many_txn( self.db_pool.simple_insert_many_txn(
txn, txn,
table="room_memberships", table="room_memberships",

View file

@ -29,6 +29,7 @@ from typing import (
from typing_extensions import TypedDict from typing_extensions import TypedDict
from synapse.api.errors import StoreError from synapse.api.errors import StoreError
from synapse.util.stringutils import non_null_str_or_none
if TYPE_CHECKING: if TYPE_CHECKING:
from synapse.server import HomeServer from synapse.server import HomeServer
@ -469,11 +470,9 @@ class UserDirectoryBackgroundUpdateStore(StateDeltasStore):
""" """
Update or add a user's profile in the user directory. Update or add a user's profile in the user directory.
""" """
# If the display name or avatar URL are unexpected types, overwrite them. # If the display name or avatar URL are unexpected types, replace with None.
if not isinstance(display_name, str): display_name = non_null_str_or_none(display_name)
display_name = None avatar_url = non_null_str_or_none(avatar_url)
if not isinstance(avatar_url, str):
avatar_url = None
def _update_profile_in_user_dir_txn(txn: LoggingTransaction) -> None: def _update_profile_in_user_dir_txn(txn: LoggingTransaction) -> None:
self.db_pool.simple_upsert_txn( self.db_pool.simple_upsert_txn(

View file

@ -16,7 +16,7 @@ import itertools
import re import re
import secrets import secrets
import string import string
from typing import Iterable, Optional, Tuple from typing import Any, Iterable, Optional, Tuple
from netaddr import valid_ipv6 from netaddr import valid_ipv6
@ -247,3 +247,11 @@ def base62_encode(num: int, minwidth: int = 1) -> str:
# pad to minimum width # pad to minimum width
pad = "0" * (minwidth - len(res)) pad = "0" * (minwidth - len(res))
return pad + res return pad + res
def non_null_str_or_none(val: Any) -> Optional[str]:
"""Check that the arg is a string containing no null (U+0000) codepoints.
If so, returns the given string unmodified; otherwise, returns None.
"""
return val if isinstance(val, str) and "\u0000" not in val else None

View file

@ -1007,6 +1007,34 @@ class UserDirectoryTestCase(unittest.HomeserverTestCase):
self.assertEqual(in_public, {(bob, room1), (bob, room2)}) self.assertEqual(in_public, {(bob, room1), (bob, room2)})
self.assertEqual(in_private, set()) self.assertEqual(in_private, set())
def test_ignore_display_names_with_null_codepoints(self) -> None:
MXC_DUMMY = "mxc://dummy"
# Alice creates a public room.
alice = self.register_user("alice", "pass")
# Alice has a user directory entry to start with.
self.assertIn(
alice,
self.get_success(self.user_dir_helper.get_profiles_in_user_directory()),
)
# Alice changes her name to include a null codepoint.
self.get_success(
self.hs.get_user_directory_handler().handle_local_profile_change(
alice,
ProfileInfo(
display_name="abcd\u0000efgh",
avatar_url=MXC_DUMMY,
),
)
)
# Alice's profile should be updated with the new avatar, but no display name.
self.assertEqual(
self.get_success(self.user_dir_helper.get_profiles_in_user_directory()),
{alice: ProfileInfo(display_name=None, avatar_url=MXC_DUMMY)},
)
class TestUserDirSearchDisabled(unittest.HomeserverTestCase): class TestUserDirSearchDisabled(unittest.HomeserverTestCase):
servlets = [ servlets = [