Merge branch 'develop' into shay/more_batching
This commit is contained in:
20
CHANGES.md
20
CHANGES.md
@@ -1,12 +1,28 @@
|
||||
Synapse 1.69.0rc1 (2022-10-04)
|
||||
Synapse 1.69.0rc2 (2022-10-06)
|
||||
==============================
|
||||
|
||||
|
||||
Please note that legacy Prometheus metric names are now deprecated and will be removed in Synapse 1.73.0.
|
||||
Server administrators should update their dashboards and alerting rules to avoid using the deprecated metric names.
|
||||
See the [upgrade notes](https://matrix-org.github.io/synapse/v1.69/upgrade.html#upgrading-to-v1690) for more details.
|
||||
|
||||
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
||||
- Deprecate the `generate_short_term_login_token` method in favor of an async `create_login_token` method in the Module API. ([\#13842](https://github.com/matrix-org/synapse/issues/13842))
|
||||
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
- Ensure Synapse v1.69 works with upcoming database changes in v1.70. ([\#14045](https://github.com/matrix-org/synapse/issues/14045))
|
||||
- Fix a bug introduced in Synapse v1.68.0 where messages could not be sent in rooms with non-integer `notifications` power level. ([\#14073](https://github.com/matrix-org/synapse/issues/14073))
|
||||
- Temporarily pin build-system requirements to workaround an incompatibility with poetry-core 1.3.0. This will be reverted before the v1.69.0 release proper, see [\#14079](https://github.com/matrix-org/synapse/issues/14079). ([\#14080](https://github.com/matrix-org/synapse/issues/14080))
|
||||
|
||||
|
||||
Synapse 1.69.0rc1 (2022-10-04)
|
||||
==============================
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
|
||||
1
changelog.d/14065.misc
Normal file
1
changelog.d/14065.misc
Normal file
@@ -0,0 +1 @@
|
||||
Fix a bug introduced in Synapse 1.35.0 where errors parsing a `/send_join` or `/state` response would produce excessive, low-quality Sentry events.
|
||||
1
changelog.d/14087.doc
Normal file
1
changelog.d/14087.doc
Normal file
@@ -0,0 +1 @@
|
||||
The changelog entry ending in a full stop or exclamation mark is not optional.
|
||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
||||
matrix-synapse-py3 (1.69.0~rc2) stable; urgency=medium
|
||||
|
||||
* New Synapse release 1.69.0rc2.
|
||||
|
||||
-- Synapse Packaging team <packages@matrix.org> Thu, 06 Oct 2022 14:45:00 +0100
|
||||
|
||||
matrix-synapse-py3 (1.69.0~rc1) stable; urgency=medium
|
||||
|
||||
* The man page for the hash_password script has been updated to reflect
|
||||
|
||||
@@ -390,7 +390,7 @@ This file will become part of our [changelog](
|
||||
https://github.com/matrix-org/synapse/blob/master/CHANGES.md) at the next
|
||||
release, so the content of the file should be a short description of your
|
||||
change in the same style as the rest of the changelog. The file can contain Markdown
|
||||
formatting, and should end with a full stop (.) or an exclamation mark (!) for
|
||||
formatting, and must end with a full stop (.) or an exclamation mark (!) for
|
||||
consistency.
|
||||
|
||||
Adding credits to the changelog is encouraged, we value your
|
||||
|
||||
@@ -423,7 +423,7 @@ Synapse config:
|
||||
user_mapping_provider:
|
||||
config:
|
||||
display_name_template: "{{ user.name }}"
|
||||
email_template: "{{ '{{ user.email }}' }}"
|
||||
email_template: "{{ user.email }}"
|
||||
```
|
||||
|
||||
Relevant documents:
|
||||
|
||||
@@ -128,6 +128,39 @@ you may specify `enable_legacy_metrics: false` in your homeserver configuration.
|
||||
A list of affected metrics is available on the [Metrics How-to page](https://matrix-org.github.io/synapse/v1.69/metrics-howto.html?highlight=metrics%20deprecated#renaming-of-metrics--deprecation-of-old-names-in-12).
|
||||
|
||||
|
||||
## Deprecation of the `generate_short_term_login_token` module API method
|
||||
|
||||
The following method of the module API has been deprecated, and is scheduled to
|
||||
be remove in v1.71.0:
|
||||
|
||||
```python
|
||||
def generate_short_term_login_token(
|
||||
self,
|
||||
user_id: str,
|
||||
duration_in_ms: int = (2 * 60 * 1000),
|
||||
auth_provider_id: str = "",
|
||||
auth_provider_session_id: Optional[str] = None,
|
||||
) -> str:
|
||||
...
|
||||
```
|
||||
|
||||
It has been replaced by an asynchronous equivalent:
|
||||
|
||||
```python
|
||||
async def create_login_token(
|
||||
self,
|
||||
user_id: str,
|
||||
duration_in_ms: int = (2 * 60 * 1000),
|
||||
auth_provider_id: Optional[str] = None,
|
||||
auth_provider_session_id: Optional[str] = None,
|
||||
) -> str:
|
||||
...
|
||||
```
|
||||
|
||||
Synapse will log a warning when a module uses the deprecated method, to help
|
||||
administrators find modules using it.
|
||||
|
||||
|
||||
# Upgrading to v1.68.0
|
||||
|
||||
Two changes announced in the upgrade notes for v1.67.0 have now landed in v1.68.0.
|
||||
|
||||
3
mypy.ini
3
mypy.ini
@@ -106,6 +106,9 @@ disallow_untyped_defs = False
|
||||
[mypy-tests.handlers.test_user_directory]
|
||||
disallow_untyped_defs = True
|
||||
|
||||
[mypy-tests.push.test_bulk_push_rule_evaluator]
|
||||
disallow_untyped_defs = True
|
||||
|
||||
[mypy-tests.test_server]
|
||||
disallow_untyped_defs = True
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ manifest-path = "rust/Cargo.toml"
|
||||
|
||||
[tool.poetry]
|
||||
name = "matrix-synapse"
|
||||
version = "1.69.0rc1"
|
||||
version = "1.69.0rc2"
|
||||
description = "Homeserver for the Matrix decentralised comms protocol"
|
||||
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
|
||||
license = "Apache-2.0"
|
||||
@@ -307,7 +307,7 @@ twine = "*"
|
||||
towncrier = ">=18.6.0rc1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0", "setuptools_rust>=1.3"]
|
||||
requires = ["poetry-core==1.2.0", "setuptools_rust==1.5.2"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ from synapse.federation.units import Transaction
|
||||
from synapse.http.matrixfederationclient import ByteParser
|
||||
from synapse.http.types import QueryParams
|
||||
from synapse.types import JsonDict
|
||||
from synapse.util import ExceptionBundle
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -926,8 +927,7 @@ class SendJoinParser(ByteParser[SendJoinResponse]):
|
||||
return len(data)
|
||||
|
||||
def finish(self) -> SendJoinResponse:
|
||||
for c in self._coros:
|
||||
c.close()
|
||||
_close_coros(self._coros)
|
||||
|
||||
if self._response.event_dict:
|
||||
self._response.event = make_event_from_dict(
|
||||
@@ -970,6 +970,27 @@ class _StateParser(ByteParser[StateRequestResponse]):
|
||||
return len(data)
|
||||
|
||||
def finish(self) -> StateRequestResponse:
|
||||
for c in self._coros:
|
||||
c.close()
|
||||
_close_coros(self._coros)
|
||||
return self._response
|
||||
|
||||
|
||||
def _close_coros(coros: Iterable[Generator[None, bytes, None]]) -> None:
|
||||
"""Close each of the given coroutines.
|
||||
|
||||
Always calls .close() on each coroutine, even if doing so raises an exception.
|
||||
Any exceptions raised are aggregated into an ExceptionBundle.
|
||||
|
||||
:raises ExceptionBundle: if at least one coroutine fails to close.
|
||||
"""
|
||||
exceptions = []
|
||||
for c in coros:
|
||||
try:
|
||||
c.close()
|
||||
except Exception as e:
|
||||
exceptions.append(e)
|
||||
|
||||
if exceptions:
|
||||
# raise from the first exception so that the traceback has slightly more context
|
||||
raise ExceptionBundle(
|
||||
f"There were {len(exceptions)} errors closing coroutines", exceptions
|
||||
) from exceptions[0]
|
||||
|
||||
@@ -748,6 +748,40 @@ class ModuleApi:
|
||||
)
|
||||
)
|
||||
|
||||
async def create_login_token(
|
||||
self,
|
||||
user_id: str,
|
||||
duration_in_ms: int = (2 * 60 * 1000),
|
||||
auth_provider_id: Optional[str] = None,
|
||||
auth_provider_session_id: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Create a login token suitable for m.login.token authentication
|
||||
|
||||
Added in Synapse v1.69.0.
|
||||
|
||||
Args:
|
||||
user_id: gives the ID of the user that the token is for
|
||||
|
||||
duration_in_ms: the time that the token will be valid for
|
||||
|
||||
auth_provider_id: the ID of the SSO IdP that the user used to authenticate
|
||||
to get this token, if any. This is encoded in the token so that
|
||||
/login can report stats on number of successful logins by IdP.
|
||||
|
||||
auth_provider_session_id: The session ID got during login from the SSO IdP,
|
||||
if any.
|
||||
"""
|
||||
# The deprecated `generate_short_term_login_token` method defaulted to an empty
|
||||
# string for the `auth_provider_id` because of how the underlying macaroon was
|
||||
# generated. This will change to a proper NULL-able field when the tokens get
|
||||
# moved to the database.
|
||||
return self._hs.get_macaroon_generator().generate_short_term_login_token(
|
||||
user_id,
|
||||
auth_provider_id or "",
|
||||
auth_provider_session_id,
|
||||
duration_in_ms,
|
||||
)
|
||||
|
||||
def generate_short_term_login_token(
|
||||
self,
|
||||
user_id: str,
|
||||
@@ -759,6 +793,9 @@ class ModuleApi:
|
||||
|
||||
Added in Synapse v1.9.0.
|
||||
|
||||
This was deprecated in Synapse v1.69.0 in favor of create_login_token, and will
|
||||
be removed in Synapse 1.71.0.
|
||||
|
||||
Args:
|
||||
user_id: gives the ID of the user that the token is for
|
||||
|
||||
@@ -768,6 +805,11 @@ class ModuleApi:
|
||||
to get this token, if any. This is encoded in the token so that
|
||||
/login can report stats on number of successful logins by IdP.
|
||||
"""
|
||||
logger.warn(
|
||||
"A module configured on this server uses ModuleApi.generate_short_term_login_token(), "
|
||||
"which is deprecated in favor of ModuleApi.create_login_token(), and will be removed in "
|
||||
"Synapse 1.71.0",
|
||||
)
|
||||
return self._hs.get_macaroon_generator().generate_short_term_login_token(
|
||||
user_id,
|
||||
auth_provider_id,
|
||||
|
||||
@@ -304,6 +304,30 @@ class BulkPushRuleEvaluator:
|
||||
event, context, event_id_to_event
|
||||
)
|
||||
|
||||
# Recursively attempt to find the thread this event relates to.
|
||||
if relation.rel_type == RelationTypes.THREAD:
|
||||
thread_id = relation.parent_id
|
||||
else:
|
||||
# Since the event has not yet been persisted we check whether
|
||||
# the parent is part of a thread.
|
||||
thread_id = await self.store.get_thread_id(relation.parent_id) or "main"
|
||||
|
||||
# It's possible that old room versions have non-integer power levels (floats or
|
||||
# strings). Workaround this by explicitly converting to int.
|
||||
notification_levels = power_levels.get("notifications", {})
|
||||
if not event.room_version.msc3667_int_only_power_levels:
|
||||
for user_id, level in notification_levels.items():
|
||||
notification_levels[user_id] = int(level)
|
||||
|
||||
evaluator = PushRuleEvaluator(
|
||||
_flatten_dict(event),
|
||||
room_member_count,
|
||||
sender_power_level,
|
||||
notification_levels,
|
||||
relations,
|
||||
self._relations_match_enabled,
|
||||
)
|
||||
|
||||
relation = relation_from_event(event)
|
||||
# If the event does not have a relation, then cannot have any mutual
|
||||
# relations or thread ID.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import json
|
||||
import logging
|
||||
import typing
|
||||
from typing import Any, Callable, Dict, Generator, Optional
|
||||
from typing import Any, Callable, Dict, Generator, Optional, Sequence
|
||||
|
||||
import attr
|
||||
from frozendict import frozendict
|
||||
@@ -193,3 +193,15 @@ def log_failure(
|
||||
# Version string with git info. Computed here once so that we don't invoke git multiple
|
||||
# times.
|
||||
SYNAPSE_VERSION = get_distribution_version_string("matrix-synapse", __file__)
|
||||
|
||||
|
||||
class ExceptionBundle(Exception):
|
||||
# A poor stand-in for something like Python 3.11's ExceptionGroup.
|
||||
# (A backport called `exceptiongroup` exists but seems overkill: we just want a
|
||||
# container type here.)
|
||||
def __init__(self, message: str, exceptions: Sequence[Exception]):
|
||||
parts = [message]
|
||||
for e in exceptions:
|
||||
parts.append(str(e))
|
||||
super().__init__("\n - ".join(parts))
|
||||
self.exceptions = exceptions
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock
|
||||
|
||||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.federation.transport.client import SendJoinParser
|
||||
@@ -94,3 +95,39 @@ class SendJoinParserTestCase(TestCase):
|
||||
# Retrieve and check the parsed SendJoinResponse
|
||||
parsed_response = parser.finish()
|
||||
self.assertEqual(parsed_response.servers_in_room, ["hs1", "hs2"])
|
||||
|
||||
def test_errors_closing_coroutines(self) -> None:
|
||||
"""Check we close all coroutines, even if closing the first raises an Exception.
|
||||
|
||||
We also check that an Exception of some kind is raised, but we don't make any
|
||||
assertions about its attributes or type.
|
||||
"""
|
||||
parser = SendJoinParser(RoomVersions.V1, False)
|
||||
response = {"org.matrix.msc3706.servers_in_room": ["hs1", "hs2"]}
|
||||
serialisation = json.dumps(response).encode()
|
||||
|
||||
# Mock the coroutines managed by this parser.
|
||||
# The first one will error when we try to close it.
|
||||
coro_1 = Mock()
|
||||
coro_1.close = Mock(side_effect=RuntimeError("Couldn't close coro 1"))
|
||||
|
||||
coro_2 = Mock()
|
||||
|
||||
coro_3 = Mock()
|
||||
coro_3.close = Mock(side_effect=RuntimeError("Couldn't close coro 3"))
|
||||
|
||||
parser._coros = [coro_1, coro_2, coro_3]
|
||||
|
||||
# Send half of the data to the parser
|
||||
parser.write(serialisation[: len(serialisation) // 2])
|
||||
|
||||
# Close the parser. There should be _some_ kind of exception, but it need not
|
||||
# be that RuntimeError directly. E.g. we might want to raise a wrapper
|
||||
# encompassing multiple errors from multiple coroutines.
|
||||
with self.assertRaises(Exception):
|
||||
parser.finish()
|
||||
|
||||
# In any case, we should have tried to close both coros.
|
||||
coro_1.close.assert_called()
|
||||
coro_2.close.assert_called()
|
||||
coro_3.close.assert_called()
|
||||
|
||||
74
tests/push/test_bulk_push_rule_evaluator.py
Normal file
74
tests/push/test_bulk_push_rule_evaluator.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.push.bulk_push_rule_evaluator import BulkPushRuleEvaluator
|
||||
from synapse.rest import admin
|
||||
from synapse.rest.client import login, register, room
|
||||
from synapse.types import create_requester
|
||||
|
||||
from tests import unittest
|
||||
|
||||
|
||||
class TestBulkPushRuleEvaluator(unittest.HomeserverTestCase):
|
||||
|
||||
servlets = [
|
||||
admin.register_servlets_for_client_rest_resource,
|
||||
room.register_servlets,
|
||||
login.register_servlets,
|
||||
register.register_servlets,
|
||||
]
|
||||
|
||||
def test_action_for_event_by_user_handles_noninteger_power_levels(self) -> None:
|
||||
"""We should convert floats and strings to integers before passing to Rust.
|
||||
|
||||
Reproduces #14060.
|
||||
|
||||
A lack of validation: the gift that keeps on giving.
|
||||
"""
|
||||
# Create a new user and room.
|
||||
alice = self.register_user("alice", "pass")
|
||||
token = self.login(alice, "pass")
|
||||
|
||||
room_id = self.helper.create_room_as(
|
||||
alice, room_version=RoomVersions.V9.identifier, tok=token
|
||||
)
|
||||
|
||||
# Alter the power levels in that room to include stringy and floaty levels.
|
||||
# We need to suppress the validation logic or else it will reject these dodgy
|
||||
# values. (Presumably this validation was not always present.)
|
||||
event_creation_handler = self.hs.get_event_creation_handler()
|
||||
requester = create_requester(alice)
|
||||
with patch("synapse.events.validator.validate_canonicaljson"), patch(
|
||||
"synapse.events.validator.jsonschema.validate"
|
||||
):
|
||||
self.helper.send_state(
|
||||
room_id,
|
||||
"m.room.power_levels",
|
||||
{
|
||||
"users": {alice: "100"}, # stringy
|
||||
"notifications": {"room": 100.0}, # float
|
||||
},
|
||||
token,
|
||||
state_key="",
|
||||
)
|
||||
|
||||
# Create a new message event, and try to evaluate it under the dodgy
|
||||
# power level event.
|
||||
event, context = self.get_success(
|
||||
event_creation_handler.create_event(
|
||||
requester,
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"room_id": room_id,
|
||||
"content": {
|
||||
"msgtype": "m.text",
|
||||
"body": "helo",
|
||||
},
|
||||
"sender": alice,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
bulk_evaluator = BulkPushRuleEvaluator(self.hs)
|
||||
# should not raise
|
||||
self.get_success(bulk_evaluator.action_for_event_by_user(event, context))
|
||||
Reference in New Issue
Block a user