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))pull/7503/head
commit
dede23ff1e
15
CHANGES.md
15
CHANGES.md
|
@ -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)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue