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 #6434 from matrix-org/erikj/msc2367_membership_rea…
Browse files Browse the repository at this point in the history
…sons

Implement MSC 2367 - Membership Reasons
  • Loading branch information
erikjohnston authored Nov 29, 2019
2 parents 6d8576c + 19ba7c1 commit 1c3a615
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 10 deletions.
1 change: 1 addition & 0 deletions changelog.d/6434.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for MSC 2367, which allows specifying a reason on all membership events.
4 changes: 2 additions & 2 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1428,9 +1428,9 @@ def on_invite_request(self, origin, pdu):
return event

@defer.inlineCallbacks
def do_remotely_reject_invite(self, target_hosts, room_id, user_id):
def do_remotely_reject_invite(self, target_hosts, room_id, user_id, content):
origin, event, event_format_version = yield self._make_and_verify_event(
target_hosts, room_id, user_id, "leave"
target_hosts, room_id, user_id, "leave", content=content,
)
# Mark as outlier as we don't have any state for this event; we're not
# even in the room.
Expand Down
13 changes: 9 additions & 4 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def _remote_join(self, requester, remote_room_hosts, room_id, user, content):
raise NotImplementedError()

@abc.abstractmethod
def _remote_reject_invite(self, requester, remote_room_hosts, room_id, target):
def _remote_reject_invite(
self, requester, remote_room_hosts, room_id, target, content
):
"""Attempt to reject an invite for a room this server is not in. If we
fail to do so we locally mark the invite as rejected.
Expand All @@ -104,6 +106,7 @@ def _remote_reject_invite(self, requester, remote_room_hosts, room_id, target):
reject invite
room_id (str)
target (UserID): The user rejecting the invite
content (dict): The content for the rejection event
Returns:
Deferred[dict]: A dictionary to be returned to the client, may
Expand Down Expand Up @@ -471,7 +474,7 @@ def _update_membership(
# send the rejection to the inviter's HS.
remote_room_hosts = remote_room_hosts + [inviter.domain]
res = yield self._remote_reject_invite(
requester, remote_room_hosts, room_id, target
requester, remote_room_hosts, room_id, target, content,
)
return res

Expand Down Expand Up @@ -971,13 +974,15 @@ def _remote_join(self, requester, remote_room_hosts, room_id, user, content):
)

@defer.inlineCallbacks
def _remote_reject_invite(self, requester, remote_room_hosts, room_id, target):
def _remote_reject_invite(
self, requester, remote_room_hosts, room_id, target, content
):
"""Implements RoomMemberHandler._remote_reject_invite
"""
fed_handler = self.federation_handler
try:
ret = yield fed_handler.do_remotely_reject_invite(
remote_room_hosts, room_id, target.to_string()
remote_room_hosts, room_id, target.to_string(), content=content,
)
return ret
except Exception as e:
Expand Down
5 changes: 4 additions & 1 deletion synapse/handlers/room_member_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,17 @@ def _remote_join(self, requester, remote_room_hosts, room_id, user, content):

return ret

def _remote_reject_invite(self, requester, remote_room_hosts, room_id, target):
def _remote_reject_invite(
self, requester, remote_room_hosts, room_id, target, content
):
"""Implements RoomMemberHandler._remote_reject_invite
"""
return self._remote_reject_client(
requester=requester,
remote_room_hosts=remote_room_hosts,
room_id=room_id,
user_id=target.to_string(),
content=content,
)

def _user_joined_room(self, target, room_id):
Expand Down
7 changes: 5 additions & 2 deletions synapse/replication/http/membership.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class ReplicationRemoteRejectInviteRestServlet(ReplicationEndpoint):
{
"requester": ...,
"remote_room_hosts": [...],
"content": { ... }
}
"""

Expand All @@ -107,7 +108,7 @@ def __init__(self, hs):
self.clock = hs.get_clock()

@staticmethod
def _serialize_payload(requester, room_id, user_id, remote_room_hosts):
def _serialize_payload(requester, room_id, user_id, remote_room_hosts, content):
"""
Args:
requester(Requester)
Expand All @@ -118,12 +119,14 @@ def _serialize_payload(requester, room_id, user_id, remote_room_hosts):
return {
"requester": requester.serialize(),
"remote_room_hosts": remote_room_hosts,
"content": content,
}

async def _handle_request(self, request, room_id, user_id):
content = parse_json_object_from_request(request)

remote_room_hosts = content["remote_room_hosts"]
event_content = content["content"]

requester = Requester.deserialize(self.store, content["requester"])

Expand All @@ -134,7 +137,7 @@ async def _handle_request(self, request, room_id, user_id):

try:
event = await self.federation_handler.do_remotely_reject_invite(
remote_room_hosts, room_id, user_id
remote_room_hosts, room_id, user_id, event_content,
)
ret = event.get_pdu_json()
except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion synapse/rest/client/v1/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ async def on_POST(self, request, room_id, membership_action, txn_id=None):
target = UserID.from_string(content["user_id"])

event_content = None
if "reason" in content and membership_action in ["kick", "ban"]:
if "reason" in content:
event_content = {"reason": content["reason"]}

await self.room_member_handler.update_membership(
Expand Down
140 changes: 140 additions & 0 deletions tests/rest/client/v1/test_rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1180,3 +1180,143 @@ def test_per_room_profile_forbidden(self):

res_displayname = channel.json_body["content"]["displayname"]
self.assertEqual(res_displayname, self.displayname, channel.result)


class RoomMembershipReasonTestCase(unittest.HomeserverTestCase):
"""Tests that clients can add a "reason" field to membership events and
that they get correctly added to the generated events and propagated.
"""

servlets = [
synapse.rest.admin.register_servlets_for_client_rest_resource,
room.register_servlets,
login.register_servlets,
]

def prepare(self, reactor, clock, homeserver):
self.creator = self.register_user("creator", "test")
self.creator_tok = self.login("creator", "test")

self.second_user_id = self.register_user("second", "test")
self.second_tok = self.login("second", "test")

self.room_id = self.helper.create_room_as(self.creator, tok=self.creator_tok)

def test_join_reason(self):
reason = "hello"
request, channel = self.make_request(
"POST",
"/_matrix/client/r0/rooms/{}/join".format(self.room_id),
content={"reason": reason},
access_token=self.second_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

self._check_for_reason(reason)

def test_leave_reason(self):
self.helper.join(self.room_id, user=self.second_user_id, tok=self.second_tok)

reason = "hello"
request, channel = self.make_request(
"POST",
"/_matrix/client/r0/rooms/{}/leave".format(self.room_id),
content={"reason": reason},
access_token=self.second_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

self._check_for_reason(reason)

def test_kick_reason(self):
self.helper.join(self.room_id, user=self.second_user_id, tok=self.second_tok)

reason = "hello"
request, channel = self.make_request(
"POST",
"/_matrix/client/r0/rooms/{}/kick".format(self.room_id),
content={"reason": reason, "user_id": self.second_user_id},
access_token=self.second_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

self._check_for_reason(reason)

def test_ban_reason(self):
self.helper.join(self.room_id, user=self.second_user_id, tok=self.second_tok)

reason = "hello"
request, channel = self.make_request(
"POST",
"/_matrix/client/r0/rooms/{}/ban".format(self.room_id),
content={"reason": reason, "user_id": self.second_user_id},
access_token=self.creator_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

self._check_for_reason(reason)

def test_unban_reason(self):
reason = "hello"
request, channel = self.make_request(
"POST",
"/_matrix/client/r0/rooms/{}/unban".format(self.room_id),
content={"reason": reason, "user_id": self.second_user_id},
access_token=self.creator_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

self._check_for_reason(reason)

def test_invite_reason(self):
reason = "hello"
request, channel = self.make_request(
"POST",
"/_matrix/client/r0/rooms/{}/invite".format(self.room_id),
content={"reason": reason, "user_id": self.second_user_id},
access_token=self.creator_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

self._check_for_reason(reason)

def test_reject_invite_reason(self):
self.helper.invite(
self.room_id,
src=self.creator,
targ=self.second_user_id,
tok=self.creator_tok,
)

reason = "hello"
request, channel = self.make_request(
"POST",
"/_matrix/client/r0/rooms/{}/leave".format(self.room_id),
content={"reason": reason},
access_token=self.second_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

self._check_for_reason(reason)

def _check_for_reason(self, reason):
request, channel = self.make_request(
"GET",
"/_matrix/client/r0/rooms/{}/state/m.room.member/{}".format(
self.room_id, self.second_user_id
),
access_token=self.creator_tok,
)
self.render(request)
self.assertEqual(channel.code, 200, channel.result)

event_content = channel.json_body

self.assertEqual(event_content.get("reason"), reason, channel.result)

0 comments on commit 1c3a615

Please sign in to comment.