Skip to content

Commit e53b95f

Browse files
committed
Famedly v1.129
2 parents f26edcd + 9a62b2d commit e53b95f

File tree

20 files changed

+315
-142
lines changed

20 files changed

+315
-142
lines changed

CHANGES.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,49 @@
1+
# Synapse 1.129.0 (2025-05-06)
2+
3+
No significant changes since 1.129.0rc2.
4+
5+
6+
7+
8+
# Synapse 1.129.0rc2 (2025-04-30)
9+
10+
Synapse 1.129.0rc1 was never formally released due to regressions discovered during the release process. 1.129.0rc2 fixes those regressions by reverting the affected PRs.
11+
12+
### Internal Changes
13+
14+
- Revert the slow background update introduced by [\#18068](https://github.com/element-hq/synapse/issues/18068) in v1.128.0. ([\#18372](https://github.com/element-hq/synapse/issues/18372))
15+
- Revert "Add total event, unencrypted message, and e2ee event counts to stats reporting", added in v1.129.0rc1. ([\#18373](https://github.com/element-hq/synapse/issues/18373))
16+
17+
18+
19+
20+
# Synapse 1.129.0rc1 (2025-04-15)
21+
22+
### Features
23+
24+
- Add `passthrough_authorization_parameters` in OIDC configuration to allow passing parameters to the authorization grant URL. ([\#18232](https://github.com/element-hq/synapse/issues/18232))
25+
- Add `total_event_count`, `total_message_count`, and `total_e2ee_event_count` fields to the homeserver usage statistics. ([\#18260](https://github.com/element-hq/synapse/issues/18260))
26+
27+
### Bugfixes
28+
29+
- Fix `force_tracing_for_users` config when using delegated auth. ([\#18334](https://github.com/element-hq/synapse/issues/18334))
30+
- Fix the token introspection cache logging access tokens when MAS integration is in use. ([\#18335](https://github.com/element-hq/synapse/issues/18335))
31+
- Stop caching introspection failures when delegating auth to MAS. ([\#18339](https://github.com/element-hq/synapse/issues/18339))
32+
- Fix `ExternalIDReuse` exception after migrating to MAS on workers with a high traffic. ([\#18342](https://github.com/element-hq/synapse/issues/18342))
33+
- Fix minor performance regression caused by tracking of room participation. Regressed in v1.128.0. ([\#18345](https://github.com/element-hq/synapse/issues/18345))
34+
35+
### Updates to the Docker image
36+
37+
- Optimize the build of the complement-synapse image. ([\#18294](https://github.com/element-hq/synapse/issues/18294))
38+
39+
### Internal Changes
40+
41+
- Disable statement timeout during room purge. ([\#18133](https://github.com/element-hq/synapse/issues/18133))
42+
- Add cache to storage functions used to auth requests when using delegated auth. ([\#18337](https://github.com/element-hq/synapse/issues/18337))
43+
44+
45+
46+
147
# Synapse 1.128.0 (2025-04-08)
248

349
No significant changes since 1.128.0rc1.

debian/changelog

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
matrix-synapse-py3 (1.129.0) stable; urgency=medium
2+
3+
* New Synapse release 1.129.0.
4+
5+
-- Synapse Packaging team <packages@matrix.org> Tue, 06 May 2025 12:22:11 +0100
6+
7+
matrix-synapse-py3 (1.129.0~rc2) stable; urgency=medium
8+
9+
* New synapse release 1.129.0rc2.
10+
11+
-- Synapse Packaging team <packages@matrix.org> Wed, 30 Apr 2025 13:13:16 +0000
12+
13+
matrix-synapse-py3 (1.129.0~rc1) stable; urgency=medium
14+
15+
* New Synapse release 1.129.0rc1.
16+
17+
-- Synapse Packaging team <packages@matrix.org> Tue, 15 Apr 2025 10:47:43 -0600
18+
119
matrix-synapse-py3 (1.128.0) stable; urgency=medium
220

321
* New Synapse release 1.128.0.

docker/complement/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ FROM $FROM
2525
RUN adduser --system --uid 999 postgres --home /var/lib/postgresql
2626
COPY --from=postgres_base /usr/lib/postgresql /usr/lib/postgresql
2727
COPY --from=postgres_base /usr/share/postgresql /usr/share/postgresql
28-
RUN mkdir /var/run/postgresql && chown postgres /var/run/postgresql
28+
COPY --from=postgres_base --chown=postgres /var/run/postgresql /var/run/postgresql
2929
ENV PATH="${PATH}:/usr/lib/postgresql/13/bin"
3030
ENV PGDATA=/var/lib/postgresql/data
3131

docs/usage/configuration/config_documentation.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3672,6 +3672,9 @@ Options for each entry include:
36723672
* `additional_authorization_parameters`: String to string dictionary that will be passed as
36733673
additional parameters to the authorization grant URL.
36743674

3675+
* `passthrough_authorization_parameters`: List of parameters that will be passed through from the redirect endpoint
3676+
to the authorization grant URL.
3677+
36753678
* `allow_existing_users`: set to true to allow a user logging in via OIDC to
36763679
match a pre-existing account instead of failing. This could be used if
36773680
switching from password logins to OIDC. Defaults to false.
@@ -3798,6 +3801,7 @@ oidc_providers:
37983801
jwks_uri: "https://accounts.example.com/.well-known/jwks.json"
37993802
additional_authorization_parameters:
38003803
acr_values: 2fa
3804+
passthrough_authorization_parameters: ["login_hint"]
38013805
skip_verification: true
38023806
enable_registration: true
38033807
user_mapping_provider:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ module-name = "synapse.synapse_rust"
9797

9898
[tool.poetry]
9999
name = "matrix-synapse"
100-
version = "1.128.0"
100+
version = "1.129.0"
101101
description = "Homeserver for the Matrix decentralised comms protocol"
102102
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
103103
license = "AGPL-3.0-or-later"

synapse/api/auth/msc3861_delegated.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@
4545
)
4646
from synapse.http.site import SynapseRequest
4747
from synapse.logging.context import make_deferred_yieldable
48+
from synapse.logging.opentracing import active_span, force_tracing, start_active_span
4849
from synapse.types import Requester, UserID, create_requester
4950
from synapse.util import json_decoder
5051
from synapse.util.caches.cached_call import RetryOnExceptionCachedCall
51-
from synapse.util.caches.response_cache import ResponseCache
52+
from synapse.util.caches.response_cache import ResponseCache, ResponseCacheContext
5253

5354
if TYPE_CHECKING:
5455
from synapse.rest.admin.experimental_features import ExperimentalFeature
@@ -177,6 +178,7 @@ def __init__(self, hs: "HomeServer"):
177178
self._http_client = hs.get_proxied_http_client()
178179
self._hostname = hs.hostname
179180
self._admin_token: Callable[[], Optional[str]] = self._config.admin_token
181+
self._force_tracing_for_users = hs.config.tracing.force_tracing_for_users
180182

181183
# # Token Introspection Cache
182184
# This remembers what users/devices are represented by which access tokens,
@@ -201,6 +203,8 @@ def __init__(self, hs: "HomeServer"):
201203
self._clock,
202204
"token_introspection",
203205
timeout_ms=120_000,
206+
# don't log because the keys are access tokens
207+
enable_logging=False,
204208
)
205209

206210
self._issuer_metadata = RetryOnExceptionCachedCall[OpenIDProviderMetadata](
@@ -275,7 +279,9 @@ async def _introspection_endpoint(self) -> str:
275279
metadata = await self._issuer_metadata.get()
276280
return metadata.get("introspection_endpoint")
277281

278-
async def _introspect_token(self, token: str) -> IntrospectionResult:
282+
async def _introspect_token(
283+
self, token: str, cache_context: ResponseCacheContext[str]
284+
) -> IntrospectionResult:
279285
"""
280286
Send a token to the introspection endpoint and returns the introspection response
281287
@@ -291,6 +297,8 @@ async def _introspect_token(self, token: str) -> IntrospectionResult:
291297
Returns:
292298
The introspection response
293299
"""
300+
# By default, we shouldn't cache the result unless we know it's valid
301+
cache_context.should_cache = False
294302
introspection_endpoint = await self._introspection_endpoint()
295303
raw_headers: Dict[str, str] = {
296304
"Content-Type": "application/x-www-form-urlencoded",
@@ -348,6 +356,8 @@ async def _introspect_token(self, token: str) -> IntrospectionResult:
348356
"The introspection endpoint returned an invalid JSON response."
349357
)
350358

359+
# We had a valid response, so we can cache it
360+
cache_context.should_cache = True
351361
return IntrospectionResult(
352362
IntrospectionToken(**resp), retrieved_at_ms=self._clock.time_msec()
353363
)
@@ -361,6 +371,55 @@ async def get_user_by_req(
361371
allow_guest: bool = False,
362372
allow_expired: bool = False,
363373
allow_locked: bool = False,
374+
) -> Requester:
375+
"""Get a registered user's ID.
376+
377+
Args:
378+
request: An HTTP request with an access_token query parameter.
379+
allow_guest: If False, will raise an AuthError if the user making the
380+
request is a guest.
381+
allow_expired: If True, allow the request through even if the account
382+
is expired, or session token lifetime has ended. Note that
383+
/login will deliver access tokens regardless of expiration.
384+
385+
Returns:
386+
Resolves to the requester
387+
Raises:
388+
InvalidClientCredentialsError if no user by that token exists or the token
389+
is invalid.
390+
AuthError if access is denied for the user in the access token
391+
"""
392+
parent_span = active_span()
393+
with start_active_span("get_user_by_req"):
394+
requester = await self._wrapped_get_user_by_req(
395+
request, allow_guest, allow_expired, allow_locked
396+
)
397+
398+
if parent_span:
399+
if requester.authenticated_entity in self._force_tracing_for_users:
400+
# request tracing is enabled for this user, so we need to force it
401+
# tracing on for the parent span (which will be the servlet span).
402+
#
403+
# It's too late for the get_user_by_req span to inherit the setting,
404+
# so we also force it on for that.
405+
force_tracing()
406+
force_tracing(parent_span)
407+
parent_span.set_tag(
408+
"authenticated_entity", requester.authenticated_entity
409+
)
410+
parent_span.set_tag("user_id", requester.user.to_string())
411+
if requester.device_id is not None:
412+
parent_span.set_tag("device_id", requester.device_id)
413+
if requester.app_service is not None:
414+
parent_span.set_tag("appservice_id", requester.app_service.id)
415+
return requester
416+
417+
async def _wrapped_get_user_by_req(
418+
self,
419+
request: SynapseRequest,
420+
allow_guest: bool = False,
421+
allow_expired: bool = False,
422+
allow_locked: bool = False,
364423
) -> Requester:
365424
access_token = self.get_access_token_from_request(request)
366425

@@ -429,7 +488,7 @@ async def get_user_by_access_token(
429488

430489
try:
431490
introspection_result = await self._introspection_cache.wrap(
432-
token, self._introspect_token, token
491+
token, self._introspect_token, token, cache_context=True
433492
)
434493
except Exception:
435494
logger.exception("Failed to introspect token")

synapse/config/oidc.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ def _parse_oidc_config_dict(
356356
additional_authorization_parameters=oidc_config.get(
357357
"additional_authorization_parameters", {}
358358
),
359+
passthrough_authorization_parameters=oidc_config.get(
360+
"passthrough_authorization_parameters", []
361+
),
359362
)
360363

361364

@@ -501,3 +504,6 @@ class OidcProviderConfig:
501504

502505
# Additional parameters that will be passed to the authorization grant URL
503506
additional_authorization_parameters: Mapping[str, str]
507+
508+
# Allow query parameters to the redirect endpoint that will be passed to the authorization grant URL
509+
passthrough_authorization_parameters: Collection[str]

synapse/handlers/device.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ async def get_device(self, user_id: str, device_id: str) -> JsonDict:
163163
raise errors.NotFoundError()
164164

165165
ips = await self.store.get_last_client_ip_by_device(user_id, device_id)
166+
167+
device = dict(device)
166168
_update_device_from_client_ips(device, ips)
167169

168170
set_tag("device", str(device))

synapse/handlers/oidc.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,10 @@ def __init__(
467467

468468
self._sso_handler.register_identity_provider(self)
469469

470+
self.passthrough_authorization_parameters = (
471+
provider.passthrough_authorization_parameters
472+
)
473+
470474
def _validate_metadata(self, m: OpenIDProviderMetadata) -> None:
471475
"""Verifies the provider metadata.
472476
@@ -1005,7 +1009,6 @@ async def handle_redirect_request(
10051009
when everything is done (or None for UI Auth)
10061010
ui_auth_session_id: The session ID of the ongoing UI Auth (or
10071011
None if this is a login).
1008-
10091012
Returns:
10101013
The redirect URL to the authorization endpoint.
10111014
@@ -1078,6 +1081,13 @@ async def handle_redirect_request(
10781081
)
10791082
)
10801083

1084+
# add passthrough additional authorization parameters
1085+
passthrough_authorization_parameters = self.passthrough_authorization_parameters
1086+
for parameter in passthrough_authorization_parameters:
1087+
parameter_value = parse_string(request, parameter)
1088+
if parameter_value:
1089+
additional_authorization_parameters.update({parameter: parameter_value})
1090+
10811091
authorization_endpoint = metadata.get("authorization_endpoint")
10821092
return prepare_grant_uri(
10831093
authorization_endpoint,

synapse/storage/databases/main/devices.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,10 @@ def count_devices_by_users_txn(
282282
"count_devices_by_users", count_devices_by_users_txn, user_ids
283283
)
284284

285+
@cached()
285286
async def get_device(
286287
self, user_id: str, device_id: str
287-
) -> Optional[Dict[str, Any]]:
288+
) -> Optional[Mapping[str, Any]]:
288289
"""Retrieve a device. Only returns devices that are not marked as
289290
hidden.
290291
@@ -1817,6 +1818,8 @@ async def store_device(
18171818
},
18181819
desc="store_device",
18191820
)
1821+
await self.invalidate_cache_and_stream("get_device", (user_id, device_id))
1822+
18201823
if not inserted:
18211824
# if the device already exists, check if it's a real device, or
18221825
# if the device ID is reserved by something else
@@ -1882,6 +1885,9 @@ def _delete_devices_txn(txn: LoggingTransaction, device_ids: List[str]) -> None:
18821885
values=device_ids,
18831886
keyvalues={"user_id": user_id},
18841887
)
1888+
self._invalidate_cache_and_stream_bulk(
1889+
txn, self.get_device, [(user_id, device_id) for device_id in device_ids]
1890+
)
18851891

18861892
for batch in batch_iter(device_ids, 100):
18871893
await self.db_pool.runInteraction(
@@ -1915,6 +1921,7 @@ async def update_device(
19151921
updatevalues=updates,
19161922
desc="update_device",
19171923
)
1924+
await self.invalidate_cache_and_stream("get_device", (user_id, device_id))
19181925

19191926
async def update_remote_device_list_cache_entry(
19201927
self, user_id: str, device_id: str, content: JsonDict, stream_id: str

0 commit comments

Comments
 (0)