Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4904 from matrix-org/erikj/fix_shutdown
Browse files Browse the repository at this point in the history
Fixup shutdown room API
  • Loading branch information
erikjohnston authored Mar 21, 2019
2 parents 09f991a + cd80cbf commit 3959858
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 25 deletions.
1 change: 1 addition & 0 deletions changelog.d/4904.bugfix
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions synapse/handlers/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def kick_guest_users(self, current_state):
member_event.room_id,
"leave",
ratelimit=False,
require_consent=False,
)
except Exception as e:
logger.exception("Error kicking guest user: %s" % (e,))
1 change: 1 addition & 0 deletions synapse/handlers/deactivate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ def _part_user(self, user_id):
room_id,
"leave",
ratelimit=False,
require_consent=False,
)
except Exception:
logger.exception(
Expand Down
7 changes: 5 additions & 2 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def __init__(self, hs):

@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.
Expand All @@ -276,6 +276,9 @@ def create_event(self, requester, event_dict, token_id=None, txn_id=None,
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
Expand Down Expand Up @@ -317,7 +320,7 @@ def create_event(self, requester, event_dict, token_id=None, txn_id=None,
)

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:
Expand Down
6 changes: 6 additions & 0 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def _local_membership_update(
txn_id=None,
ratelimit=True,
content=None,
require_consent=True,
):
user_id = target.to_string()

Expand All @@ -185,6 +186,7 @@ def _local_membership_update(
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.
Expand Down Expand Up @@ -305,6 +307,7 @@ def update_membership(
third_party_signed=None,
ratelimit=True,
content=None,
require_consent=True,
):
key = (room_id,)

Expand All @@ -319,6 +322,7 @@ def update_membership(
third_party_signed=third_party_signed,
ratelimit=ratelimit,
content=content,
require_consent=require_consent,
)

defer.returnValue(result)
Expand All @@ -335,6 +339,7 @@ def _update_membership(
third_party_signed=None,
ratelimit=True,
content=None,
require_consent=True,
):
content_specified = bool(content)
if content is None:
Expand Down Expand Up @@ -516,6 +521,7 @@ def _update_membership(
ratelimit=ratelimit,
prev_events_and_hashes=prev_events_and_hashes,
content=content,
require_consent=require_consent,
)
defer.returnValue(res)

Expand Down
55 changes: 35 additions & 20 deletions synapse/rest/client/v1/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,40 +490,54 @@ def on_POST(self, request, room_id):

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,
Expand All @@ -544,6 +558,7 @@ def on_POST(self, request, room_id):

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,
}))
Expand Down
16 changes: 14 additions & 2 deletions synapse/storage/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,22 @@ def get_all_new_public_rooms(txn):

@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",
Expand Down
75 changes: 74 additions & 1 deletion tests/rest/client/v1/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
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

Expand Down Expand Up @@ -353,3 +353,76 @@ def nonce():

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)

0 comments on commit 3959858

Please sign in to comment.