commit
						3959858eaa
					
				|  | @ -0,0 +1 @@ | |||
| Fix bug in shutdown room admin API where it would fail if a user in the room hadn't consented to the privacy policy. | ||||
|  | @ -165,6 +165,7 @@ class BaseHandler(object): | |||
|                     member_event.room_id, | ||||
|                     "leave", | ||||
|                     ratelimit=False, | ||||
|                     require_consent=False, | ||||
|                 ) | ||||
|             except Exception as e: | ||||
|                 logger.exception("Error kicking guest user: %s" % (e,)) | ||||
|  |  | |||
|  | @ -164,6 +164,7 @@ class DeactivateAccountHandler(BaseHandler): | |||
|                     room_id, | ||||
|                     "leave", | ||||
|                     ratelimit=False, | ||||
|                     require_consent=False, | ||||
|                 ) | ||||
|             except Exception: | ||||
|                 logger.exception( | ||||
|  |  | |||
|  | @ -255,7 +255,7 @@ class EventCreationHandler(object): | |||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def create_event(self, requester, event_dict, token_id=None, txn_id=None, | ||||
|                      prev_events_and_hashes=None): | ||||
|                      prev_events_and_hashes=None, require_consent=True): | ||||
|         """ | ||||
|         Given a dict from a client, create a new event. | ||||
| 
 | ||||
|  | @ -276,6 +276,9 @@ class EventCreationHandler(object): | |||
|                 where *hashes* is a map from algorithm to hash. | ||||
| 
 | ||||
|                 If None, they will be requested from the database. | ||||
| 
 | ||||
|             require_consent (bool): Whether to check if the requester has | ||||
|                 consented to privacy policy. | ||||
|         Raises: | ||||
|             ResourceLimitError if server is blocked to some resource being | ||||
|             exceeded | ||||
|  | @ -317,7 +320,7 @@ class EventCreationHandler(object): | |||
|                     ) | ||||
| 
 | ||||
|         is_exempt = yield self._is_exempt_from_privacy_policy(builder, requester) | ||||
|         if not is_exempt: | ||||
|         if require_consent and not is_exempt: | ||||
|             yield self.assert_accepted_privacy_policy(requester) | ||||
| 
 | ||||
|         if token_id is not None: | ||||
|  |  | |||
|  | @ -160,6 +160,7 @@ class RoomMemberHandler(object): | |||
|         txn_id=None, | ||||
|         ratelimit=True, | ||||
|         content=None, | ||||
|         require_consent=True, | ||||
|     ): | ||||
|         user_id = target.to_string() | ||||
| 
 | ||||
|  | @ -185,6 +186,7 @@ class RoomMemberHandler(object): | |||
|             token_id=requester.access_token_id, | ||||
|             txn_id=txn_id, | ||||
|             prev_events_and_hashes=prev_events_and_hashes, | ||||
|             require_consent=require_consent, | ||||
|         ) | ||||
| 
 | ||||
|         # Check if this event matches the previous membership event for the user. | ||||
|  | @ -305,6 +307,7 @@ class RoomMemberHandler(object): | |||
|             third_party_signed=None, | ||||
|             ratelimit=True, | ||||
|             content=None, | ||||
|             require_consent=True, | ||||
|     ): | ||||
|         key = (room_id,) | ||||
| 
 | ||||
|  | @ -319,6 +322,7 @@ class RoomMemberHandler(object): | |||
|                 third_party_signed=third_party_signed, | ||||
|                 ratelimit=ratelimit, | ||||
|                 content=content, | ||||
|                 require_consent=require_consent, | ||||
|             ) | ||||
| 
 | ||||
|         defer.returnValue(result) | ||||
|  | @ -335,6 +339,7 @@ class RoomMemberHandler(object): | |||
|             third_party_signed=None, | ||||
|             ratelimit=True, | ||||
|             content=None, | ||||
|             require_consent=True, | ||||
|     ): | ||||
|         content_specified = bool(content) | ||||
|         if content is None: | ||||
|  | @ -516,6 +521,7 @@ class RoomMemberHandler(object): | |||
|             ratelimit=ratelimit, | ||||
|             prev_events_and_hashes=prev_events_and_hashes, | ||||
|             content=content, | ||||
|             require_consent=require_consent, | ||||
|         ) | ||||
|         defer.returnValue(res) | ||||
| 
 | ||||
|  |  | |||
|  | @ -490,40 +490,54 @@ class ShutdownRoomRestServlet(ClientV1RestServlet): | |||
| 
 | ||||
|         requester_user_id = requester.user.to_string() | ||||
| 
 | ||||
|         logger.info("Shutting down room %r", room_id) | ||||
|         logger.info( | ||||
|             "Shutting down room %r, joining to new room: %r", | ||||
|             room_id, new_room_id, | ||||
|         ) | ||||
| 
 | ||||
|         # This will work even if the room is already blocked, but that is | ||||
|         # desirable in case the first attempt at blocking the room failed below. | ||||
|         yield self.store.block_room(room_id, requester_user_id) | ||||
| 
 | ||||
|         users = yield self.state.get_current_user_in_room(room_id) | ||||
|         kicked_users = [] | ||||
|         failed_to_kick_users = [] | ||||
|         for user_id in users: | ||||
|             if not self.hs.is_mine_id(user_id): | ||||
|                 continue | ||||
| 
 | ||||
|             logger.info("Kicking %r from %r...", user_id, room_id) | ||||
| 
 | ||||
|             target_requester = create_requester(user_id) | ||||
|             yield self.room_member_handler.update_membership( | ||||
|                 requester=target_requester, | ||||
|                 target=target_requester.user, | ||||
|                 room_id=room_id, | ||||
|                 action=Membership.LEAVE, | ||||
|                 content={}, | ||||
|                 ratelimit=False | ||||
|             ) | ||||
|             try: | ||||
|                 target_requester = create_requester(user_id) | ||||
|                 yield self.room_member_handler.update_membership( | ||||
|                     requester=target_requester, | ||||
|                     target=target_requester.user, | ||||
|                     room_id=room_id, | ||||
|                     action=Membership.LEAVE, | ||||
|                     content={}, | ||||
|                     ratelimit=False, | ||||
|                     require_consent=False, | ||||
|                 ) | ||||
| 
 | ||||
|             yield self.room_member_handler.forget(target_requester.user, room_id) | ||||
|                 yield self.room_member_handler.forget(target_requester.user, room_id) | ||||
| 
 | ||||
|             yield self.room_member_handler.update_membership( | ||||
|                 requester=target_requester, | ||||
|                 target=target_requester.user, | ||||
|                 room_id=new_room_id, | ||||
|                 action=Membership.JOIN, | ||||
|                 content={}, | ||||
|                 ratelimit=False | ||||
|             ) | ||||
|                 yield self.room_member_handler.update_membership( | ||||
|                     requester=target_requester, | ||||
|                     target=target_requester.user, | ||||
|                     room_id=new_room_id, | ||||
|                     action=Membership.JOIN, | ||||
|                     content={}, | ||||
|                     ratelimit=False, | ||||
|                     require_consent=False, | ||||
|                 ) | ||||
| 
 | ||||
|             kicked_users.append(user_id) | ||||
|                 kicked_users.append(user_id) | ||||
|             except Exception: | ||||
|                 logger.exception( | ||||
|                     "Failed to leave old room and join new room for %r", user_id, | ||||
|                 ) | ||||
|                 failed_to_kick_users.append(user_id) | ||||
| 
 | ||||
|         yield self.event_creation_handler.create_and_send_nonmember_event( | ||||
|             room_creator_requester, | ||||
|  | @ -544,6 +558,7 @@ class ShutdownRoomRestServlet(ClientV1RestServlet): | |||
| 
 | ||||
|         defer.returnValue((200, { | ||||
|             "kicked_users": kicked_users, | ||||
|             "failed_to_kick_users": failed_to_kick_users, | ||||
|             "local_aliases": aliases_for_room, | ||||
|             "new_room_id": new_room_id, | ||||
|         })) | ||||
|  |  | |||
|  | @ -500,10 +500,22 @@ class RoomStore(RoomWorkerStore, SearchStore): | |||
| 
 | ||||
|     @defer.inlineCallbacks | ||||
|     def block_room(self, room_id, user_id): | ||||
|         yield self._simple_insert( | ||||
|         """Marks the room as blocked. Can be called multiple times. | ||||
| 
 | ||||
|         Args: | ||||
|             room_id (str): Room to block | ||||
|             user_id (str): Who blocked it | ||||
| 
 | ||||
|         Returns: | ||||
|             Deferred | ||||
|         """ | ||||
|         yield self._simple_upsert( | ||||
|             table="blocked_rooms", | ||||
|             values={ | ||||
|             keyvalues={ | ||||
|                 "room_id": room_id, | ||||
|             }, | ||||
|             values={}, | ||||
|             insertion_values={ | ||||
|                 "user_id": user_id, | ||||
|             }, | ||||
|             desc="block_room", | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ import json | |||
| from mock import Mock | ||||
| 
 | ||||
| from synapse.api.constants import UserTypes | ||||
| from synapse.rest.client.v1 import admin, login | ||||
| from synapse.rest.client.v1 import admin, login, room | ||||
| 
 | ||||
| from tests import unittest | ||||
| 
 | ||||
|  | @ -353,3 +353,76 @@ class UserRegisterTestCase(unittest.HomeserverTestCase): | |||
| 
 | ||||
|         self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) | ||||
|         self.assertEqual('Invalid user type', channel.json_body["error"]) | ||||
| 
 | ||||
| 
 | ||||
| class ShutdownRoomTestCase(unittest.HomeserverTestCase): | ||||
|     servlets = [ | ||||
|         admin.register_servlets, | ||||
|         login.register_servlets, | ||||
|         room.register_servlets, | ||||
|     ] | ||||
| 
 | ||||
|     def prepare(self, reactor, clock, hs): | ||||
|         self.event_creation_handler = hs.get_event_creation_handler() | ||||
|         hs.config.user_consent_version = "1" | ||||
| 
 | ||||
|         consent_uri_builder = Mock() | ||||
|         consent_uri_builder.build_user_consent_uri.return_value = ( | ||||
|             "http://example.com" | ||||
|         ) | ||||
|         self.event_creation_handler._consent_uri_builder = consent_uri_builder | ||||
| 
 | ||||
|         self.store = hs.get_datastore() | ||||
| 
 | ||||
|         self.admin_user = self.register_user("admin", "pass", admin=True) | ||||
|         self.admin_user_tok = self.login("admin", "pass") | ||||
| 
 | ||||
|         self.other_user = self.register_user("user", "pass") | ||||
|         self.other_user_token = self.login("user", "pass") | ||||
| 
 | ||||
|         # Mark the admin user as having consented | ||||
|         self.get_success( | ||||
|             self.store.user_set_consent_version(self.admin_user, "1"), | ||||
|         ) | ||||
| 
 | ||||
|     def test_shutdown_room_consent(self): | ||||
|         """Test that we can shutdown rooms with local users who have not | ||||
|         yet accepted the privacy policy. This used to fail when we tried to | ||||
|         force part the user from the old room. | ||||
|         """ | ||||
|         self.event_creation_handler._block_events_without_consent_error = None | ||||
| 
 | ||||
|         room_id = self.helper.create_room_as(self.other_user, tok=self.other_user_token) | ||||
| 
 | ||||
|         # Assert one user in room | ||||
|         users_in_room = self.get_success( | ||||
|             self.store.get_users_in_room(room_id), | ||||
|         ) | ||||
|         self.assertEqual([self.other_user], users_in_room) | ||||
| 
 | ||||
|         # Enable require consent to send events | ||||
|         self.event_creation_handler._block_events_without_consent_error = "Error" | ||||
| 
 | ||||
|         # Assert that the user is getting consent error | ||||
|         self.helper.send( | ||||
|             room_id, | ||||
|             body="foo", tok=self.other_user_token, expect_code=403, | ||||
|         ) | ||||
| 
 | ||||
|         # Test that the admin can still send shutdown | ||||
|         url = "admin/shutdown_room/" + room_id | ||||
|         request, channel = self.make_request( | ||||
|             "POST", | ||||
|             url.encode('ascii'), | ||||
|             json.dumps({"new_room_user_id": self.admin_user}), | ||||
|             access_token=self.admin_user_tok, | ||||
|         ) | ||||
|         self.render(request) | ||||
| 
 | ||||
|         self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) | ||||
| 
 | ||||
|         # Assert there is now no longer anyone in the room | ||||
|         users_in_room = self.get_success( | ||||
|             self.store.get_users_in_room(room_id), | ||||
|         ) | ||||
|         self.assertEqual([], users_in_room) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Erik Johnston
						Erik Johnston