diff options
Diffstat (limited to 'tests/rest/client')
-rw-r--r-- | tests/rest/client/test_auth.py | 265 | ||||
-rw-r--r-- | tests/rest/client/test_relations.py | 239 |
2 files changed, 50 insertions, 454 deletions
diff --git a/tests/rest/client/test_auth.py b/tests/rest/client/test_auth.py index 72bbc87b4a..8552671431 100644 --- a/tests/rest/client/test_auth.py +++ b/tests/rest/client/test_auth.py @@ -12,7 +12,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from http import HTTPStatus from typing import Optional, Union from twisted.internet.defer import succeed @@ -514,39 +513,12 @@ class RefreshAuthTests(unittest.HomeserverTestCase): self.user_pass = "pass" self.user = self.register_user("test", self.user_pass) - def use_refresh_token(self, refresh_token: str) -> FakeChannel: - """ - Helper that makes a request to use a refresh token. - """ - return self.make_request( - "POST", - "/_matrix/client/v1/refresh", - {"refresh_token": refresh_token}, - ) - - def is_access_token_valid(self, access_token) -> bool: - """ - Checks whether an access token is valid, returning whether it is or not. - """ - code = self.make_request( - "GET", "/_matrix/client/v3/account/whoami", access_token=access_token - ).code - - # Either 200 or 401 is what we get back; anything else is a bug. - assert code in {HTTPStatus.OK, HTTPStatus.UNAUTHORIZED} - - return code == HTTPStatus.OK - 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, - } + body = {"type": "m.login.password", "user": "test", "password": self.user_pass} login_without_refresh = self.make_request( "POST", "/_matrix/client/r0/login", body @@ -556,8 +528,8 @@ class RefreshAuthTests(unittest.HomeserverTestCase): login_with_refresh = self.make_request( "POST", - "/_matrix/client/r0/login", - {"refresh_token": True, **body}, + "/_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) @@ -583,12 +555,11 @@ class RefreshAuthTests(unittest.HomeserverTestCase): register_with_refresh = self.make_request( "POST", - "/_matrix/client/r0/register", + "/_matrix/client/r0/register?org.matrix.msc2918.refresh_token=true", { "username": "test3", "password": self.user_pass, "auth": {"type": LoginType.DUMMY}, - "refresh_token": True, }, ) self.assertEqual(register_with_refresh.code, 200, register_with_refresh.result) @@ -599,22 +570,17 @@ class RefreshAuthTests(unittest.HomeserverTestCase): """ A refresh token can be used to issue a new access token. """ - body = { - "type": "m.login.password", - "user": "test", - "password": self.user_pass, - "refresh_token": True, - } + body = {"type": "m.login.password", "user": "test", "password": self.user_pass} login_response = self.make_request( "POST", - "/_matrix/client/r0/login", + "/_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/v1/refresh", + "/_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) @@ -633,19 +599,14 @@ class RefreshAuthTests(unittest.HomeserverTestCase): ) @override_config({"refreshable_access_token_lifetime": "1m"}) - def test_refreshable_access_token_expiration(self): + 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, - "refresh_token": True, - } + body = {"type": "m.login.password", "user": "test", "password": self.user_pass} login_response = self.make_request( "POST", - "/_matrix/client/r0/login", + "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true", body, ) self.assertEqual(login_response.code, 200, login_response.result) @@ -655,198 +616,13 @@ class RefreshAuthTests(unittest.HomeserverTestCase): refresh_response = self.make_request( "POST", - "/_matrix/client/v1/refresh", + "/_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 ) - access_token = refresh_response.json_body["access_token"] - - # Advance 59 seconds in the future (just shy of 1 minute, the time of expiry) - self.reactor.advance(59.0) - # Check that our token is valid - self.assertEqual( - self.make_request( - "GET", "/_matrix/client/v3/account/whoami", access_token=access_token - ).code, - HTTPStatus.OK, - ) - - # Advance 2 more seconds (just past the time of expiry) - self.reactor.advance(2.0) - # Check that our token is invalid - self.assertEqual( - self.make_request( - "GET", "/_matrix/client/v3/account/whoami", access_token=access_token - ).code, - HTTPStatus.UNAUTHORIZED, - ) - - @override_config( - { - "refreshable_access_token_lifetime": "1m", - "nonrefreshable_access_token_lifetime": "10m", - } - ) - def test_different_expiry_for_refreshable_and_nonrefreshable_access_tokens(self): - """ - Tests that the expiry times for refreshable and non-refreshable access - tokens can be different. - """ - body = { - "type": "m.login.password", - "user": "test", - "password": self.user_pass, - } - login_response1 = self.make_request( - "POST", - "/_matrix/client/r0/login", - {"refresh_token": True, **body}, - ) - self.assertEqual(login_response1.code, 200, login_response1.result) - self.assertApproximates( - login_response1.json_body["expires_in_ms"], 60 * 1000, 100 - ) - refreshable_access_token = login_response1.json_body["access_token"] - - login_response2 = self.make_request( - "POST", - "/_matrix/client/r0/login", - body, - ) - self.assertEqual(login_response2.code, 200, login_response2.result) - nonrefreshable_access_token = login_response2.json_body["access_token"] - - # Advance 59 seconds in the future (just shy of 1 minute, the time of expiry) - self.reactor.advance(59.0) - - # Both tokens should still be valid. - self.assertTrue(self.is_access_token_valid(refreshable_access_token)) - self.assertTrue(self.is_access_token_valid(nonrefreshable_access_token)) - - # Advance to 61 s (just past 1 minute, the time of expiry) - self.reactor.advance(2.0) - - # Only the non-refreshable token is still valid. - self.assertFalse(self.is_access_token_valid(refreshable_access_token)) - self.assertTrue(self.is_access_token_valid(nonrefreshable_access_token)) - - # Advance to 599 s (just shy of 10 minutes, the time of expiry) - self.reactor.advance(599.0 - 61.0) - - # It's still the case that only the non-refreshable token is still valid. - self.assertFalse(self.is_access_token_valid(refreshable_access_token)) - self.assertTrue(self.is_access_token_valid(nonrefreshable_access_token)) - - # Advance to 601 s (just past 10 minutes, the time of expiry) - self.reactor.advance(2.0) - - # Now neither token is valid. - self.assertFalse(self.is_access_token_valid(refreshable_access_token)) - self.assertFalse(self.is_access_token_valid(nonrefreshable_access_token)) - - @override_config( - {"refreshable_access_token_lifetime": "1m", "refresh_token_lifetime": "2m"} - ) - def test_refresh_token_expiry(self): - """ - The refresh token can be configured to have a limited lifetime. - When that lifetime has ended, the refresh token can no longer be used to - refresh the session. - """ - - body = { - "type": "m.login.password", - "user": "test", - "password": self.user_pass, - "refresh_token": True, - } - login_response = self.make_request( - "POST", - "/_matrix/client/r0/login", - body, - ) - self.assertEqual(login_response.code, HTTPStatus.OK, login_response.result) - refresh_token1 = login_response.json_body["refresh_token"] - - # Advance 119 seconds in the future (just shy of 2 minutes) - self.reactor.advance(119.0) - - # Refresh our session. The refresh token should still JUST be valid right now. - # By doing so, we get a new access token and a new refresh token. - refresh_response = self.use_refresh_token(refresh_token1) - self.assertEqual(refresh_response.code, HTTPStatus.OK, refresh_response.result) - self.assertIn( - "refresh_token", - refresh_response.json_body, - "No new refresh token returned after refresh.", - ) - refresh_token2 = refresh_response.json_body["refresh_token"] - - # Advance 121 seconds in the future (just a bit more than 2 minutes) - self.reactor.advance(121.0) - - # Try to refresh our session, but instead notice that the refresh token is - # not valid (it just expired). - refresh_response = self.use_refresh_token(refresh_token2) - self.assertEqual( - refresh_response.code, HTTPStatus.FORBIDDEN, refresh_response.result - ) - - @override_config( - { - "refreshable_access_token_lifetime": "2m", - "refresh_token_lifetime": "2m", - "session_lifetime": "3m", - } - ) - def test_ultimate_session_expiry(self): - """ - The session can be configured to have an ultimate, limited lifetime. - """ - - body = { - "type": "m.login.password", - "user": "test", - "password": self.user_pass, - "refresh_token": True, - } - login_response = self.make_request( - "POST", - "/_matrix/client/r0/login", - body, - ) - self.assertEqual(login_response.code, 200, login_response.result) - refresh_token = login_response.json_body["refresh_token"] - - # Advance shy of 2 minutes into the future - self.reactor.advance(119.0) - - # Refresh our session. The refresh token should still be valid right now. - refresh_response = self.use_refresh_token(refresh_token) - self.assertEqual(refresh_response.code, 200, refresh_response.result) - self.assertIn( - "refresh_token", - refresh_response.json_body, - "No new refresh token returned after refresh.", - ) - # Notice that our access token lifetime has been diminished to match the - # session lifetime. - # 3 minutes - 119 seconds = 61 seconds. - self.assertEqual(refresh_response.json_body["expires_in_ms"], 61_000) - refresh_token = refresh_response.json_body["refresh_token"] - - # Advance 61 seconds into the future. Our session should have expired - # now, because we've had our 3 minutes. - self.reactor.advance(61.0) - - # Try to issue a new, refreshed, access token. - # This should fail because the refresh token's lifetime has also been - # diminished as our session expired. - refresh_response = self.use_refresh_token(refresh_token) - self.assertEqual(refresh_response.code, 403, refresh_response.result) def test_refresh_token_invalidation(self): """Refresh tokens are invalidated after first use of the next token. @@ -864,15 +640,10 @@ class RefreshAuthTests(unittest.HomeserverTestCase): |-> fourth_refresh (fails) """ - body = { - "type": "m.login.password", - "user": "test", - "password": self.user_pass, - "refresh_token": True, - } + body = {"type": "m.login.password", "user": "test", "password": self.user_pass} login_response = self.make_request( "POST", - "/_matrix/client/r0/login", + "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true", body, ) self.assertEqual(login_response.code, 200, login_response.result) @@ -880,7 +651,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase): # This first refresh should work properly first_refresh_response = self.make_request( "POST", - "/_matrix/client/v1/refresh", + "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", {"refresh_token": login_response.json_body["refresh_token"]}, ) self.assertEqual( @@ -890,7 +661,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase): # This one as well, since the token in the first one was never used second_refresh_response = self.make_request( "POST", - "/_matrix/client/v1/refresh", + "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", {"refresh_token": login_response.json_body["refresh_token"]}, ) self.assertEqual( @@ -900,7 +671,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase): # This one should not, since the token from the first refresh is not valid anymore third_refresh_response = self.make_request( "POST", - "/_matrix/client/v1/refresh", + "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", {"refresh_token": first_refresh_response.json_body["refresh_token"]}, ) self.assertEqual( @@ -928,7 +699,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase): # 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/v1/refresh", + "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", {"refresh_token": login_response.json_body["refresh_token"]}, ) self.assertEqual( @@ -938,7 +709,7 @@ class RefreshAuthTests(unittest.HomeserverTestCase): # But refreshing from the last valid refresh token still works fifth_refresh_response = self.make_request( "POST", - "/_matrix/client/v1/refresh", + "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", {"refresh_token": second_refresh_response.json_body["refresh_token"]}, ) self.assertEqual( diff --git a/tests/rest/client/test_relations.py b/tests/rest/client/test_relations.py index 397c12c2a6..eb10d43217 100644 --- a/tests/rest/client/test_relations.py +++ b/tests/rest/client/test_relations.py @@ -19,7 +19,7 @@ from typing import Dict, List, Optional, Tuple from synapse.api.constants import EventTypes, RelationTypes from synapse.rest import admin -from synapse.rest.client import login, register, relations, room, sync +from synapse.rest.client import login, register, relations, room from tests import unittest from tests.server import FakeChannel @@ -29,7 +29,6 @@ class RelationsTestCase(unittest.HomeserverTestCase): servlets = [ relations.register_servlets, room.register_servlets, - sync.register_servlets, login.register_servlets, register.register_servlets, admin.register_servlets_for_client_rest_resource, @@ -455,9 +454,11 @@ class RelationsTestCase(unittest.HomeserverTestCase): self.assertEquals(400, channel.code, channel.json_body) @unittest.override_config({"experimental_features": {"msc3440_enabled": True}}) - def test_bundled_aggregations(self): - """Test that annotations, references, and threads get correctly bundled.""" - # Setup by sending a variety of relations. + def test_aggregation_get_event(self): + """Test that annotations, references, and threads get correctly bundled when + getting the parent event. + """ + channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a") self.assertEquals(200, channel.code, channel.json_body) @@ -484,169 +485,43 @@ class RelationsTestCase(unittest.HomeserverTestCase): self.assertEquals(200, channel.code, channel.json_body) thread_2 = channel.json_body["event_id"] - def assert_bundle(actual): - """Assert the expected values of the bundled aggregations.""" - - # Ensure the fields are as expected. - self.assertCountEqual( - actual.keys(), - ( - RelationTypes.ANNOTATION, - RelationTypes.REFERENCE, - RelationTypes.THREAD, - ), - ) - - # Check the values of each field. - self.assertEquals( - { - "chunk": [ - {"type": "m.reaction", "key": "a", "count": 2}, - {"type": "m.reaction", "key": "b", "count": 1}, - ] - }, - actual[RelationTypes.ANNOTATION], - ) - - self.assertEquals( - {"chunk": [{"event_id": reply_1}, {"event_id": reply_2}]}, - actual[RelationTypes.REFERENCE], - ) - - self.assertEquals( - 2, - actual[RelationTypes.THREAD].get("count"), - ) - # The latest thread event has some fields that don't matter. - self.assert_dict( - { - "content": { - "m.relates_to": { - "event_id": self.parent_id, - "rel_type": RelationTypes.THREAD, - } - }, - "event_id": thread_2, - "room_id": self.room, - "sender": self.user_id, - "type": "m.room.test", - "user_id": self.user_id, - }, - actual[RelationTypes.THREAD].get("latest_event"), - ) - - def _find_and_assert_event(events): - """ - Find the parent event in a chunk of events and assert that it has the proper bundled aggregations. - """ - for event in events: - if event["event_id"] == self.parent_id: - break - else: - raise AssertionError(f"Event {self.parent_id} not found in chunk") - assert_bundle(event["unsigned"].get("m.relations")) - - # Request the event directly. channel = self.make_request( "GET", - f"/rooms/{self.room}/event/{self.parent_id}", - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - assert_bundle(channel.json_body["unsigned"].get("m.relations")) - - # Request the room messages. - channel = self.make_request( - "GET", - f"/rooms/{self.room}/messages?dir=b", - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - _find_and_assert_event(channel.json_body["chunk"]) - - # Request the room context. - channel = self.make_request( - "GET", - f"/rooms/{self.room}/context/{self.parent_id}", - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - assert_bundle(channel.json_body["event"]["unsigned"].get("m.relations")) - - # Request sync. - channel = self.make_request("GET", "/sync", access_token=self.user_token) - self.assertEquals(200, channel.code, channel.json_body) - room_timeline = channel.json_body["rooms"]["join"][self.room]["timeline"] - self.assertTrue(room_timeline["limited"]) - _find_and_assert_event(room_timeline["events"]) - - # Note that /relations is tested separately in test_aggregation_get_event_for_thread - # since it needs different data configured. - - def test_aggregation_get_event_for_annotation(self): - """Test that annotations do not get bundled aggregations included - when directly requested. - """ - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a") - self.assertEquals(200, channel.code, channel.json_body) - annotation_id = channel.json_body["event_id"] - - # Annotate the annotation. - channel = self._send_relation( - RelationTypes.ANNOTATION, "m.reaction", "a", parent_id=annotation_id - ) - self.assertEquals(200, channel.code, channel.json_body) - - channel = self.make_request( - "GET", - f"/rooms/{self.room}/event/{annotation_id}", + "/rooms/%s/event/%s" % (self.room, self.parent_id), access_token=self.user_token, ) self.assertEquals(200, channel.code, channel.json_body) - self.assertIsNone(channel.json_body["unsigned"].get("m.relations")) - def test_aggregation_get_event_for_thread(self): - """Test that threads get bundled aggregations included when directly requested.""" - channel = self._send_relation(RelationTypes.THREAD, "m.room.test") - self.assertEquals(200, channel.code, channel.json_body) - thread_id = channel.json_body["event_id"] - - # Annotate the annotation. - channel = self._send_relation( - RelationTypes.ANNOTATION, "m.reaction", "a", parent_id=thread_id - ) - self.assertEquals(200, channel.code, channel.json_body) - - channel = self.make_request( - "GET", - f"/rooms/{self.room}/event/{thread_id}", - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) self.assertEquals( channel.json_body["unsigned"].get("m.relations"), { RelationTypes.ANNOTATION: { - "chunk": [{"count": 1, "key": "a", "type": "m.reaction"}] + "chunk": [ + {"type": "m.reaction", "key": "a", "count": 2}, + {"type": "m.reaction", "key": "b", "count": 1}, + ] }, - }, - ) - - # It should also be included when the entire thread is requested. - channel = self.make_request( - "GET", - f"/_matrix/client/unstable/rooms/{self.room}/relations/{self.parent_id}?limit=1", - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - self.assertEqual(len(channel.json_body["chunk"]), 1) - - thread_message = channel.json_body["chunk"][0] - self.assertEquals( - thread_message["unsigned"].get("m.relations"), - { - RelationTypes.ANNOTATION: { - "chunk": [{"count": 1, "key": "a", "type": "m.reaction"}] + RelationTypes.REFERENCE: { + "chunk": [{"event_id": reply_1}, {"event_id": reply_2}] + }, + RelationTypes.THREAD: { + "count": 2, + "latest_event": { + "age": 100, + "content": { + "m.relates_to": { + "event_id": self.parent_id, + "rel_type": RelationTypes.THREAD, + } + }, + "event_id": thread_2, + "origin_server_ts": 1600, + "room_id": self.room, + "sender": self.user_id, + "type": "m.room.test", + "unsigned": {"age": 100}, + "user_id": self.user_id, + }, }, }, ) @@ -797,56 +672,6 @@ class RelationsTestCase(unittest.HomeserverTestCase): {"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict ) - def test_edit_edit(self): - """Test that an edit cannot be edited.""" - new_body = {"msgtype": "m.text", "body": "Initial edit"} - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message", - content={ - "msgtype": "m.text", - "body": "Wibble", - "m.new_content": new_body, - }, - ) - self.assertEquals(200, channel.code, channel.json_body) - edit_event_id = channel.json_body["event_id"] - - # Edit the edit event. - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message", - content={ - "msgtype": "m.text", - "body": "foo", - "m.new_content": {"msgtype": "m.text", "body": "Ignored edit"}, - }, - parent_id=edit_event_id, - ) - self.assertEquals(200, channel.code, channel.json_body) - - # Request the original event. - channel = self.make_request( - "GET", - "/rooms/%s/event/%s" % (self.room, self.parent_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - # The edit to the edit should be ignored. - self.assertEquals(channel.json_body["content"], new_body) - - # The relations information should not include the edit to the edit. - relations_dict = channel.json_body["unsigned"].get("m.relations") - self.assertIn(RelationTypes.REPLACE, relations_dict) - - m_replace_dict = relations_dict[RelationTypes.REPLACE] - for key in ["event_id", "sender", "origin_server_ts"]: - self.assertIn(key, m_replace_dict) - - self.assert_dict( - {"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict - ) - def test_relations_redaction_redacts_edits(self): """Test that edits of an event are redacted when the original event is redacted. |