diff --git a/tests/api/test_auth.py b/tests/api/test_auth.py
index 1b0a815757..f76fea4f66 100644
--- a/tests/api/test_auth.py
+++ b/tests/api/test_auth.py
@@ -58,6 +58,7 @@ class AuthTestCase(unittest.HomeserverTestCase):
user_id=self.test_user, token_id=5, device_id="device"
)
self.store.get_user_by_access_token = simple_async_mock(user_info)
+ self.store.mark_access_token_as_used = simple_async_mock(None)
request = Mock(args={})
request.args[b"access_token"] = [self.test_token]
diff --git a/tests/federation/transport/test_knocking.py b/tests/federation/transport/test_knocking.py
index 8c215d50f2..aab44bce4a 100644
--- a/tests/federation/transport/test_knocking.py
+++ b/tests/federation/transport/test_knocking.py
@@ -205,9 +205,7 @@ class FederationKnockingTestCase(
# Have this homeserver skip event auth checks. This is necessary due to
# event auth checks ensuring that events were signed by the sender's homeserver.
- async def _check_event_auth(
- origin, event, context, state, auth_events, backfilled
- ):
+ async def _check_event_auth(origin, event, context, *args, **kwargs):
return context
homeserver.get_federation_handler()._check_event_auth = _check_event_auth
diff --git a/tests/handlers/test_device.py b/tests/handlers/test_device.py
index 84c38b295d..3ac48e5e95 100644
--- a/tests/handlers/test_device.py
+++ b/tests/handlers/test_device.py
@@ -257,7 +257,7 @@ class DehydrationTestCase(unittest.HomeserverTestCase):
self.assertEqual(device_data, {"device_data": {"foo": "bar"}})
# Create a new login for the user and dehydrated the device
- device_id, access_token = self.get_success(
+ device_id, access_token, _expiration_time, _refresh_token = self.get_success(
self.registration.register_device(
user_id=user_id,
device_id=None,
diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py
index 8796af45ed..ba8cf44f46 100644
--- a/tests/handlers/test_federation.py
+++ b/tests/handlers/test_federation.py
@@ -251,7 +251,7 @@ class FederationTestCase(unittest.HomeserverTestCase):
join_event.signatures[other_server] = {"x": "y"}
with LoggingContext("send_join"):
d = run_in_background(
- self.handler.on_send_join_request, other_server, join_event
+ self.handler.on_send_membership_event, other_server, join_event
)
self.get_success(d)
diff --git a/tests/handlers/test_register.py b/tests/handlers/test_register.py
index c5f6bc3c75..d3efb67e3e 100644
--- a/tests/handlers/test_register.py
+++ b/tests/handlers/test_register.py
@@ -19,7 +19,7 @@ from synapse.api.constants import UserTypes
from synapse.api.errors import Codes, ResourceLimitError, SynapseError
from synapse.events.spamcheck import load_legacy_spam_checkers
from synapse.spam_checker_api import RegistrationBehaviour
-from synapse.types import RoomAlias, UserID, create_requester
+from synapse.types import RoomAlias, RoomID, UserID, create_requester
from tests.test_utils import make_awaitable
from tests.unittest import override_config
@@ -719,3 +719,50 @@ class RegistrationTestCase(unittest.HomeserverTestCase):
)
return user_id, token
+
+
+class RemoteAutoJoinTestCase(unittest.HomeserverTestCase):
+ """Tests auto-join on remote rooms."""
+
+ def make_homeserver(self, reactor, clock):
+ self.room_id = "!roomid:remotetest"
+
+ async def update_membership(*args, **kwargs):
+ pass
+
+ async def lookup_room_alias(*args, **kwargs):
+ return RoomID.from_string(self.room_id), ["remotetest"]
+
+ self.room_member_handler = Mock(spec=["update_membership", "lookup_room_alias"])
+ self.room_member_handler.update_membership.side_effect = update_membership
+ self.room_member_handler.lookup_room_alias.side_effect = lookup_room_alias
+
+ hs = self.setup_test_homeserver(room_member_handler=self.room_member_handler)
+ return hs
+
+ def prepare(self, reactor, clock, hs):
+ self.handler = self.hs.get_registration_handler()
+ self.store = self.hs.get_datastore()
+
+ @override_config({"auto_join_rooms": ["#room:remotetest"]})
+ def test_auto_create_auto_join_remote_room(self):
+ """Tests that we don't attempt to create remote rooms, and that we don't attempt
+ to invite ourselves to rooms we're not in."""
+
+ # Register a first user; this should call _create_and_join_rooms
+ self.get_success(self.handler.register_user(localpart="jeff"))
+
+ _, kwargs = self.room_member_handler.update_membership.call_args
+
+ self.assertEqual(kwargs["room_id"], self.room_id)
+ self.assertEqual(kwargs["action"], "join")
+ self.assertEqual(kwargs["remote_room_hosts"], ["remotetest"])
+
+ # Register a second user; this should call _join_rooms
+ self.get_success(self.handler.register_user(localpart="jeff2"))
+
+ _, kwargs = self.room_member_handler.update_membership.call_args
+
+ self.assertEqual(kwargs["room_id"], self.room_id)
+ self.assertEqual(kwargs["action"], "join")
+ self.assertEqual(kwargs["remote_room_hosts"], ["remotetest"])
diff --git a/tests/replication/test_federation_sender_shard.py b/tests/replication/test_federation_sender_shard.py
index 584da58371..a0c710f855 100644
--- a/tests/replication/test_federation_sender_shard.py
+++ b/tests/replication/test_federation_sender_shard.py
@@ -228,7 +228,7 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
builder.build(prev_event_ids=prev_event_ids, auth_event_ids=None)
)
- self.get_success(federation.on_send_join_request(remote_server, join_event))
+ self.get_success(federation.on_send_membership_event(remote_server, join_event))
self.replicate()
return room
diff --git a/tests/rest/client/v2_alpha/test_auth.py b/tests/rest/client/v2_alpha/test_auth.py
index 485e3650c3..6b90f838b6 100644
--- a/tests/rest/client/v2_alpha/test_auth.py
+++ b/tests/rest/client/v2_alpha/test_auth.py
@@ -20,7 +20,7 @@ import synapse.rest.admin
from synapse.api.constants import LoginType
from synapse.handlers.ui_auth.checkers import UserInteractiveAuthChecker
from synapse.rest.client.v1 import login
-from synapse.rest.client.v2_alpha import auth, devices, register
+from synapse.rest.client.v2_alpha import account, auth, devices, register
from synapse.rest.synapse.client import build_synapse_client_resource_tree
from synapse.types import JsonDict, UserID
@@ -498,3 +498,221 @@ class UIAuthTests(unittest.HomeserverTestCase):
self.delete_device(
self.user_tok, self.device_id, 403, body={"auth": {"session": session_id}}
)
+
+
+class RefreshAuthTests(unittest.HomeserverTestCase):
+ servlets = [
+ auth.register_servlets,
+ account.register_servlets,
+ login.register_servlets,
+ synapse.rest.admin.register_servlets_for_client_rest_resource,
+ register.register_servlets,
+ ]
+ hijack_auth = False
+
+ def prepare(self, reactor, clock, hs):
+ self.user_pass = "pass"
+ self.user = self.register_user("test", self.user_pass)
+
+ def test_login_issue_refresh_token(self):
+ """
+ A login response should include a refresh_token only if asked.
+ """
+ # Test login
+ body = {"type": "m.login.password", "user": "test", "password": self.user_pass}
+
+ login_without_refresh = self.make_request(
+ "POST", "/_matrix/client/r0/login", body
+ )
+ self.assertEqual(login_without_refresh.code, 200, login_without_refresh.result)
+ self.assertNotIn("refresh_token", login_without_refresh.json_body)
+
+ login_with_refresh = self.make_request(
+ "POST",
+ "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true",
+ body,
+ )
+ self.assertEqual(login_with_refresh.code, 200, login_with_refresh.result)
+ self.assertIn("refresh_token", login_with_refresh.json_body)
+ self.assertIn("expires_in_ms", login_with_refresh.json_body)
+
+ def test_register_issue_refresh_token(self):
+ """
+ A register response should include a refresh_token only if asked.
+ """
+ register_without_refresh = self.make_request(
+ "POST",
+ "/_matrix/client/r0/register",
+ {
+ "username": "test2",
+ "password": self.user_pass,
+ "auth": {"type": LoginType.DUMMY},
+ },
+ )
+ self.assertEqual(
+ register_without_refresh.code, 200, register_without_refresh.result
+ )
+ self.assertNotIn("refresh_token", register_without_refresh.json_body)
+
+ register_with_refresh = self.make_request(
+ "POST",
+ "/_matrix/client/r0/register?org.matrix.msc2918.refresh_token=true",
+ {
+ "username": "test3",
+ "password": self.user_pass,
+ "auth": {"type": LoginType.DUMMY},
+ },
+ )
+ self.assertEqual(register_with_refresh.code, 200, register_with_refresh.result)
+ self.assertIn("refresh_token", register_with_refresh.json_body)
+ self.assertIn("expires_in_ms", register_with_refresh.json_body)
+
+ def test_token_refresh(self):
+ """
+ A refresh token can be used to issue a new access token.
+ """
+ body = {"type": "m.login.password", "user": "test", "password": self.user_pass}
+ login_response = self.make_request(
+ "POST",
+ "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true",
+ body,
+ )
+ self.assertEqual(login_response.code, 200, login_response.result)
+
+ refresh_response = self.make_request(
+ "POST",
+ "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
+ {"refresh_token": login_response.json_body["refresh_token"]},
+ )
+ self.assertEqual(refresh_response.code, 200, refresh_response.result)
+ self.assertIn("access_token", refresh_response.json_body)
+ self.assertIn("refresh_token", refresh_response.json_body)
+ self.assertIn("expires_in_ms", refresh_response.json_body)
+
+ # The access and refresh tokens should be different from the original ones after refresh
+ self.assertNotEqual(
+ login_response.json_body["access_token"],
+ refresh_response.json_body["access_token"],
+ )
+ self.assertNotEqual(
+ login_response.json_body["refresh_token"],
+ refresh_response.json_body["refresh_token"],
+ )
+
+ @override_config({"access_token_lifetime": "1m"})
+ def test_refresh_token_expiration(self):
+ """
+ The access token should have some time as specified in the config.
+ """
+ body = {"type": "m.login.password", "user": "test", "password": self.user_pass}
+ login_response = self.make_request(
+ "POST",
+ "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true",
+ body,
+ )
+ self.assertEqual(login_response.code, 200, login_response.result)
+ self.assertApproximates(
+ login_response.json_body["expires_in_ms"], 60 * 1000, 100
+ )
+
+ refresh_response = self.make_request(
+ "POST",
+ "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
+ {"refresh_token": login_response.json_body["refresh_token"]},
+ )
+ self.assertEqual(refresh_response.code, 200, refresh_response.result)
+ self.assertApproximates(
+ refresh_response.json_body["expires_in_ms"], 60 * 1000, 100
+ )
+
+ def test_refresh_token_invalidation(self):
+ """Refresh tokens are invalidated after first use of the next token.
+
+ A refresh token is considered invalid if:
+ - it was already used at least once
+ - and either
+ - the next access token was used
+ - the next refresh token was used
+
+ The chain of tokens goes like this:
+
+ login -|-> first_refresh -> third_refresh (fails)
+ |-> second_refresh -> fifth_refresh
+ |-> fourth_refresh (fails)
+ """
+
+ body = {"type": "m.login.password", "user": "test", "password": self.user_pass}
+ login_response = self.make_request(
+ "POST",
+ "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true",
+ body,
+ )
+ self.assertEqual(login_response.code, 200, login_response.result)
+
+ # This first refresh should work properly
+ first_refresh_response = self.make_request(
+ "POST",
+ "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
+ {"refresh_token": login_response.json_body["refresh_token"]},
+ )
+ self.assertEqual(
+ first_refresh_response.code, 200, first_refresh_response.result
+ )
+
+ # This one as well, since the token in the first one was never used
+ second_refresh_response = self.make_request(
+ "POST",
+ "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
+ {"refresh_token": login_response.json_body["refresh_token"]},
+ )
+ self.assertEqual(
+ second_refresh_response.code, 200, second_refresh_response.result
+ )
+
+ # This one should not, since the token from the first refresh is not valid anymore
+ third_refresh_response = self.make_request(
+ "POST",
+ "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
+ {"refresh_token": first_refresh_response.json_body["refresh_token"]},
+ )
+ self.assertEqual(
+ third_refresh_response.code, 401, third_refresh_response.result
+ )
+
+ # The associated access token should also be invalid
+ whoami_response = self.make_request(
+ "GET",
+ "/_matrix/client/r0/account/whoami",
+ access_token=first_refresh_response.json_body["access_token"],
+ )
+ self.assertEqual(whoami_response.code, 401, whoami_response.result)
+
+ # But all other tokens should work (they will expire after some time)
+ for access_token in [
+ second_refresh_response.json_body["access_token"],
+ login_response.json_body["access_token"],
+ ]:
+ whoami_response = self.make_request(
+ "GET", "/_matrix/client/r0/account/whoami", access_token=access_token
+ )
+ self.assertEqual(whoami_response.code, 200, whoami_response.result)
+
+ # Now that the access token from the last valid refresh was used once, refreshing with the N-1 token should fail
+ fourth_refresh_response = self.make_request(
+ "POST",
+ "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
+ {"refresh_token": login_response.json_body["refresh_token"]},
+ )
+ self.assertEqual(
+ fourth_refresh_response.code, 403, fourth_refresh_response.result
+ )
+
+ # But refreshing from the last valid refresh token still works
+ fifth_refresh_response = self.make_request(
+ "POST",
+ "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh",
+ {"refresh_token": second_refresh_response.json_body["refresh_token"]},
+ )
+ self.assertEqual(
+ fifth_refresh_response.code, 200, fifth_refresh_response.result
+ )
diff --git a/tests/rest/client/v2_alpha/test_sync.py b/tests/rest/client/v2_alpha/test_sync.py
index 012910f136..cdca3a3e23 100644
--- a/tests/rest/client/v2_alpha/test_sync.py
+++ b/tests/rest/client/v2_alpha/test_sync.py
@@ -41,35 +41,7 @@ class FilterTestCase(unittest.HomeserverTestCase):
channel = self.make_request("GET", "/sync")
self.assertEqual(channel.code, 200)
- self.assertTrue(
- {
- "next_batch",
- "rooms",
- "presence",
- "account_data",
- "to_device",
- "device_lists",
- }.issubset(set(channel.json_body.keys()))
- )
-
- def test_sync_presence_disabled(self):
- """
- When presence is disabled, the key does not appear in /sync.
- """
- self.hs.config.use_presence = False
-
- channel = self.make_request("GET", "/sync")
-
- self.assertEqual(channel.code, 200)
- self.assertTrue(
- {
- "next_batch",
- "rooms",
- "account_data",
- "to_device",
- "device_lists",
- }.issubset(set(channel.json_body.keys()))
- )
+ self.assertIn("next_batch", channel.json_body)
class SyncFilterTestCase(unittest.HomeserverTestCase):
diff --git a/tests/server_notices/test_resource_limits_server_notices.py b/tests/server_notices/test_resource_limits_server_notices.py
index d46521ccdc..3245aa91ca 100644
--- a/tests/server_notices/test_resource_limits_server_notices.py
+++ b/tests/server_notices/test_resource_limits_server_notices.py
@@ -306,8 +306,9 @@ class TestResourceLimitsServerNoticesWithRealRooms(unittest.HomeserverTestCase):
channel = self.make_request("GET", "/sync?timeout=0", access_token=tok)
- invites = channel.json_body["rooms"]["invite"]
- self.assertEqual(len(invites), 0, invites)
+ self.assertNotIn(
+ "rooms", channel.json_body, "Got invites without server notice"
+ )
def test_invite_with_notice(self):
"""Tests that, if the MAU limit is hit, the server notices user invites each user
@@ -364,7 +365,8 @@ class TestResourceLimitsServerNoticesWithRealRooms(unittest.HomeserverTestCase):
# We could also pick another user and sync with it, which would return an
# invite to a system notices room, but it doesn't matter which user we're
# using so we use the last one because it saves us an extra sync.
- invites = channel.json_body["rooms"]["invite"]
+ if "rooms" in channel.json_body:
+ invites = channel.json_body["rooms"]["invite"]
# Make sure we have an invite to process.
self.assertEqual(len(invites), 1, invites)
|