Synapse 1.13.0rc2 (2020-05-14)

==============================
 
 Bugfixes
 --------
 
 - Fix a long-standing bug which could cause messages not to be sent over federation, when state events with state keys matching user IDs (such as custom user statuses) were received. ([\#7376](https://github.com/matrix-org/synapse/issues/7376))
 - Restore compatibility with non-compliant clients during the user interactive authentication process, fixing a problem introduced in v1.13.0rc1. ([\#7483](https://github.com/matrix-org/synapse/issues/7483))
 
 Internal Changes
 ----------------
 
 - Fix linting errors in new version of Flake8. ([\#7470](https://github.com/matrix-org/synapse/issues/7470))
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEv27Axt/F4vrTL/8QOSor00I9eP8FAl69IQ8ACgkQOSor00I9
 eP87lAf8DK+v6cs2U0BoD5opzQ7ZazJT6JYTmnMBaTzHU6Wx20V2ttkF7Vpwm3WU
 Zsz0048tdYtHFyYBQ1kF5RNIBBJwV8SA/QUcPkR7FVpwZMLR2q4aJn0EE7kC9OMf
 tYsmdbHeBdyfLXpXzazxWlgHquLyEIt52ykAcCphjx/Jl2fAExFEhtfsxpECoJ2f
 8Dqhjg3WFjd6QWU6AFkElbwHUYCdIWdJOcsC8N1p8OvBmDz5QXv/RlYipHE00Cpx
 QQQOgEjdRc6dlz2mbetMklnfII3p2kO9bzNdmEpOzT0Zt7nFaGdntW4I1QA0yJfa
 gows9bYMzhqYk7YSiyTYOZ4qyavVtw==
 =N/zZ
 -----END PGP SIGNATURE-----

Merge tag 'v1.13.0rc2' into develop

Synapse 1.13.0rc2 (2020-05-14)
==============================

Bugfixes
--------

- Fix a long-standing bug which could cause messages not to be sent over federation, when state events with state keys matching user IDs (such as custom user statuses) were received. ([\#7376](https://github.com/matrix-org/synapse/issues/7376))
- Restore compatibility with non-compliant clients during the user interactive authentication process, fixing a problem introduced in v1.13.0rc1. ([\#7483](https://github.com/matrix-org/synapse/issues/7483))

Internal Changes
----------------

- Fix linting errors in new version of Flake8. ([\#7470](https://github.com/matrix-org/synapse/issues/7470))
This commit is contained in:
Richard van der Hoff 2020-05-14 11:46:38 +01:00
commit dede23ff1e
7 changed files with 95 additions and 68 deletions

View file

@ -1,3 +1,18 @@
Synapse 1.13.0rc2 (2020-05-14)
==============================
Bugfixes
--------
- Fix a long-standing bug which could cause messages not to be sent over federation, when state events with state keys matching user IDs (such as custom user statuses) were received. ([\#7376](https://github.com/matrix-org/synapse/issues/7376))
- Restore compatibility with non-compliant clients during the user interactive authentication process, fixing a problem introduced in v1.13.0rc1. ([\#7483](https://github.com/matrix-org/synapse/issues/7483))
Internal Changes
----------------
- Fix linting errors in new version of Flake8. ([\#7470](https://github.com/matrix-org/synapse/issues/7470))
Synapse 1.13.0rc1 (2020-05-11) Synapse 1.13.0rc1 (2020-05-11)
============================== ==============================

View file

@ -36,7 +36,7 @@ try:
except ImportError: except ImportError:
pass pass
__version__ = "1.13.0rc1" __version__ = "1.13.0rc2"
if bool(os.environ.get("SYNAPSE_TEST_PATCH_LOG_CONTEXTS", False)): if bool(os.environ.get("SYNAPSE_TEST_PATCH_LOG_CONTEXTS", False)):
# We import here so that we don't have to install a bunch of deps when # We import here so that we don't have to install a bunch of deps when

View file

@ -252,7 +252,6 @@ class AuthHandler(BaseHandler):
clientdict: Dict[str, Any], clientdict: Dict[str, Any],
clientip: str, clientip: str,
description: str, description: str,
validate_clientdict: bool = True,
) -> Tuple[dict, dict, str]: ) -> Tuple[dict, dict, str]:
""" """
Takes a dictionary sent by the client in the login / registration Takes a dictionary sent by the client in the login / registration
@ -278,10 +277,6 @@ class AuthHandler(BaseHandler):
description: A human readable string to be displayed to the user that description: A human readable string to be displayed to the user that
describes the operation happening on their account. describes the operation happening on their account.
validate_clientdict: Whether to validate that the operation happening
on the account has not changed. If this is false,
the client dict is persisted instead of validated.
Returns: Returns:
A tuple of (creds, params, session_id). A tuple of (creds, params, session_id).
@ -346,26 +341,30 @@ class AuthHandler(BaseHandler):
# Ensure that the queried operation does not vary between stages of # Ensure that the queried operation does not vary between stages of
# the UI authentication session. This is done by generating a stable # the UI authentication session. This is done by generating a stable
# comparator based on the URI, method, and client dict (minus the # comparator and storing it during the initial query. Subsequent
# auth dict) and storing it during the initial query. Subsequent
# queries ensure that this comparator has not changed. # queries ensure that this comparator has not changed.
if validate_clientdict: #
session_comparator = (session.uri, session.method, session.clientdict) # The comparator is based on the requested URI and HTTP method. The
comparator = (uri, method, clientdict) # client dict (minus the auth dict) should also be checked, but some
else: # clients are not spec compliant, just warn for now if the client
session_comparator = (session.uri, session.method) # type: ignore # dict changes.
comparator = (uri, method) # type: ignore if (session.uri, session.method) != (uri, method):
if session_comparator != comparator:
raise SynapseError( raise SynapseError(
403, 403,
"Requested operation has changed during the UI authentication session.", "Requested operation has changed during the UI authentication session.",
) )
# For backwards compatibility the registration endpoint persists if session.clientdict != clientdict:
# changes to the client dict instead of validating them. logger.warning(
if not validate_clientdict: "Requested operation has changed during the UI "
await self.store.set_ui_auth_clientdict(sid, clientdict) "authentication session. A future version of Synapse "
"will remove this capability."
)
# For backwards compatibility, changes to the client dict are
# persisted as clients modify them throughout their user interactive
# authentication flow.
await self.store.set_ui_auth_clientdict(sid, clientdict)
if not authdict: if not authdict:
raise InteractiveAuthIncompleteError( raise InteractiveAuthIncompleteError(

View file

@ -516,7 +516,6 @@ class RegisterRestServlet(RestServlet):
body, body,
self.hs.get_ip_from_request(request), self.hs.get_ip_from_request(request),
"register a new account", "register a new account",
validate_clientdict=False,
) )
# Check that we're not trying to register a denied 3pid. # Check that we're not trying to register a denied 3pid.

View file

@ -566,7 +566,8 @@ class RoomMemberWorkerStore(EventsWorkerStore):
if key[0] == EventTypes.Member if key[0] == EventTypes.Member
] ]
for etype, state_key in context.delta_ids: for etype, state_key in context.delta_ids:
users_in_room.pop(state_key, None) if etype == EventTypes.Member:
users_in_room.pop(state_key, None)
# We check if we have any of the member event ids in the event cache # We check if we have any of the member event ids in the event cache
# before we ask the DB # before we ask the DB

View file

@ -133,47 +133,6 @@ class FallbackAuthTests(unittest.HomeserverTestCase):
# We're given a registered user. # We're given a registered user.
self.assertEqual(channel.json_body["user_id"], "@user:test") self.assertEqual(channel.json_body["user_id"], "@user:test")
def test_legacy_registration(self):
"""
Registration allows the parameters to vary through the process.
"""
# Make the initial request to register. (Later on a different password
# will be used.)
# Returns a 401 as per the spec
channel = self.register(
401, {"username": "user", "type": "m.login.password", "password": "bar"},
)
# Grab the session
session = channel.json_body["session"]
# Assert our configured public key is being given
self.assertEqual(
channel.json_body["params"]["m.login.recaptcha"]["public_key"], "brokencake"
)
# Complete the recaptcha step.
self.recaptcha(session, 200)
# also complete the dummy auth
self.register(200, {"auth": {"session": session, "type": "m.login.dummy"}})
# Now we should have fulfilled a complete auth flow, including
# the recaptcha fallback step. Make the initial request again, but
# with a changed password. This still completes.
channel = self.register(
200,
{
"username": "user",
"type": "m.login.password",
"password": "foo", # Note that this is different.
"auth": {"session": session},
},
)
# We're given a registered user.
self.assertEqual(channel.json_body["user_id"], "@user:test")
def test_complete_operation_unknown_session(self): def test_complete_operation_unknown_session(self):
""" """
Attempting to mark an invalid session as complete should error. Attempting to mark an invalid session as complete should error.
@ -282,9 +241,15 @@ class UIAuthTests(unittest.HomeserverTestCase):
}, },
) )
def test_cannot_change_body(self): def test_can_change_body(self):
""" """
The initial requested client dict cannot be modified during the user interactive authentication session. The client dict can be modified during the user interactive authentication session.
Note that it is not spec compliant to modify the client dict during a
user interactive authentication session, but many clients currently do.
When Synapse is updated to be spec compliant, the call to re-use the
session ID should be rejected.
""" """
# Create a second login. # Create a second login.
self.login("test", self.user_pass) self.login("test", self.user_pass)
@ -302,9 +267,9 @@ class UIAuthTests(unittest.HomeserverTestCase):
self.assertIn({"stages": ["m.login.password"]}, channel.json_body["flows"]) self.assertIn({"stages": ["m.login.password"]}, channel.json_body["flows"])
# Make another request providing the UI auth flow, but try to delete the # Make another request providing the UI auth flow, but try to delete the
# second device. This results in an error. # second device.
self.delete_devices( self.delete_devices(
403, 200,
{ {
"devices": [device_ids[1]], "devices": [device_ids[1]],
"auth": { "auth": {

View file

@ -22,6 +22,8 @@ from synapse.rest.client.v1 import login, room
from synapse.types import Requester, UserID from synapse.types import Requester, UserID
from tests import unittest from tests import unittest
from tests.test_utils import event_injection
from tests.utils import TestHomeServer
class RoomMemberStoreTestCase(unittest.HomeserverTestCase): class RoomMemberStoreTestCase(unittest.HomeserverTestCase):
@ -38,7 +40,7 @@ class RoomMemberStoreTestCase(unittest.HomeserverTestCase):
) )
return hs return hs
def prepare(self, reactor, clock, hs): def prepare(self, reactor, clock, hs: TestHomeServer):
# We can't test the RoomMemberStore on its own without the other event # We can't test the RoomMemberStore on its own without the other event
# storage logic # storage logic
@ -114,6 +116,52 @@ class RoomMemberStoreTestCase(unittest.HomeserverTestCase):
# It now knows about Charlie's server. # It now knows about Charlie's server.
self.assertEqual(self.store._known_servers_count, 2) self.assertEqual(self.store._known_servers_count, 2)
def test_get_joined_users_from_context(self):
room = self.helper.create_room_as(self.u_alice, tok=self.t_alice)
bob_event = event_injection.inject_member_event(
self.hs, room, self.u_bob, Membership.JOIN
)
# first, create a regular event
event, context = event_injection.create_event(
self.hs,
room_id=room,
sender=self.u_alice,
prev_event_ids=[bob_event.event_id],
type="m.test.1",
content={},
)
users = self.get_success(
self.store.get_joined_users_from_context(event, context)
)
self.assertEqual(users.keys(), {self.u_alice, self.u_bob})
# Regression test for #7376: create a state event whose key matches bob's
# user_id, but which is *not* a membership event, and persist that; then check
# that `get_joined_users_from_context` returns the correct users for the next event.
non_member_event = event_injection.inject_event(
self.hs,
room_id=room,
sender=self.u_bob,
prev_event_ids=[bob_event.event_id],
type="m.test.2",
state_key=self.u_bob,
content={},
)
event, context = event_injection.create_event(
self.hs,
room_id=room,
sender=self.u_alice,
prev_event_ids=[non_member_event.event_id],
type="m.test.3",
content={},
)
users = self.get_success(
self.store.get_joined_users_from_context(event, context)
)
self.assertEqual(users.keys(), {self.u_alice, self.u_bob})
class CurrentStateMembershipUpdateTestCase(unittest.HomeserverTestCase): class CurrentStateMembershipUpdateTestCase(unittest.HomeserverTestCase):
def prepare(self, reactor, clock, homeserver): def prepare(self, reactor, clock, homeserver):