forked from MirrorHub/synapse
Merge pull request #501 from matrix-org/daniel/unban
Require unbanning before other membership changes
This commit is contained in:
commit
5de1563997
6 changed files with 116 additions and 58 deletions
|
@ -29,6 +29,7 @@ class Codes(object):
|
||||||
USER_IN_USE = "M_USER_IN_USE"
|
USER_IN_USE = "M_USER_IN_USE"
|
||||||
ROOM_IN_USE = "M_ROOM_IN_USE"
|
ROOM_IN_USE = "M_ROOM_IN_USE"
|
||||||
BAD_PAGINATION = "M_BAD_PAGINATION"
|
BAD_PAGINATION = "M_BAD_PAGINATION"
|
||||||
|
BAD_STATE = "M_BAD_STATE"
|
||||||
UNKNOWN = "M_UNKNOWN"
|
UNKNOWN = "M_UNKNOWN"
|
||||||
NOT_FOUND = "M_NOT_FOUND"
|
NOT_FOUND = "M_NOT_FOUND"
|
||||||
MISSING_TOKEN = "M_MISSING_TOKEN"
|
MISSING_TOKEN = "M_MISSING_TOKEN"
|
||||||
|
|
|
@ -1693,7 +1693,7 @@ class FederationHandler(BaseHandler):
|
||||||
self.auth.check(event, context.current_state)
|
self.auth.check(event, context.current_state)
|
||||||
yield self._validate_keyserver(event, auth_events=context.current_state)
|
yield self._validate_keyserver(event, auth_events=context.current_state)
|
||||||
member_handler = self.hs.get_handlers().room_member_handler
|
member_handler = self.hs.get_handlers().room_member_handler
|
||||||
yield member_handler.change_membership(event, context)
|
yield member_handler.send_membership_event(event, context)
|
||||||
else:
|
else:
|
||||||
destinations = set([x.split(":", 1)[-1] for x in (sender, room_id)])
|
destinations = set([x.split(":", 1)[-1] for x in (sender, room_id)])
|
||||||
yield self.replication_layer.forward_third_party_invite(
|
yield self.replication_layer.forward_third_party_invite(
|
||||||
|
@ -1722,7 +1722,7 @@ class FederationHandler(BaseHandler):
|
||||||
# TODO: Make sure the signatures actually are correct.
|
# TODO: Make sure the signatures actually are correct.
|
||||||
event.signatures.update(returned_invite.signatures)
|
event.signatures.update(returned_invite.signatures)
|
||||||
member_handler = self.hs.get_handlers().room_member_handler
|
member_handler = self.hs.get_handlers().room_member_handler
|
||||||
yield member_handler.change_membership(event, context)
|
yield member_handler.send_membership_event(event, context)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def add_display_name_to_third_party_invite(self, event_dict, event, context):
|
def add_display_name_to_third_party_invite(self, event_dict, event, context):
|
||||||
|
|
|
@ -174,30 +174,25 @@ class MessageHandler(BaseHandler):
|
||||||
defer.returnValue(chunk)
|
defer.returnValue(chunk)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def create_and_send_event(self, event_dict, ratelimit=True,
|
def create_event(self, event_dict, token_id=None, txn_id=None):
|
||||||
token_id=None, txn_id=None, is_guest=False):
|
"""
|
||||||
""" Given a dict from a client, create and handle a new event.
|
Given a dict from a client, create a new event.
|
||||||
|
|
||||||
Creates an FrozenEvent object, filling out auth_events, prev_events,
|
Creates an FrozenEvent object, filling out auth_events, prev_events,
|
||||||
etc.
|
etc.
|
||||||
|
|
||||||
Adds display names to Join membership events.
|
Adds display names to Join membership events.
|
||||||
|
|
||||||
Persists and notifies local clients and federation.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event_dict (dict): An entire event
|
event_dict (dict): An entire event
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of created event (FrozenEvent), Context
|
||||||
"""
|
"""
|
||||||
builder = self.event_builder_factory.new(event_dict)
|
builder = self.event_builder_factory.new(event_dict)
|
||||||
|
|
||||||
self.validator.validate_new(builder)
|
self.validator.validate_new(builder)
|
||||||
|
|
||||||
if ratelimit:
|
|
||||||
self.ratelimit(builder.user_id)
|
|
||||||
# TODO(paul): Why does 'event' not have a 'user' object?
|
|
||||||
user = UserID.from_string(builder.user_id)
|
|
||||||
assert self.hs.is_mine(user), "User must be our own: %s" % (user,)
|
|
||||||
|
|
||||||
if builder.type == EventTypes.Member:
|
if builder.type == EventTypes.Member:
|
||||||
membership = builder.content.get("membership", None)
|
membership = builder.content.get("membership", None)
|
||||||
if membership == Membership.JOIN:
|
if membership == Membership.JOIN:
|
||||||
|
@ -216,6 +211,25 @@ class MessageHandler(BaseHandler):
|
||||||
event, context = yield self._create_new_client_event(
|
event, context = yield self._create_new_client_event(
|
||||||
builder=builder,
|
builder=builder,
|
||||||
)
|
)
|
||||||
|
defer.returnValue((event, context))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def send_event(self, event, context, ratelimit=True, is_guest=False):
|
||||||
|
"""
|
||||||
|
Persists and notifies local clients and federation of an event.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (FrozenEvent) the event to send.
|
||||||
|
context (Context) the context of the event.
|
||||||
|
ratelimit (bool): Whether to rate limit this send.
|
||||||
|
is_guest (bool): Whether the sender is a guest.
|
||||||
|
"""
|
||||||
|
user = UserID.from_string(event.sender)
|
||||||
|
|
||||||
|
assert self.hs.is_mine(user), "User must be our own: %s" % (user,)
|
||||||
|
|
||||||
|
if ratelimit:
|
||||||
|
self.ratelimit(event.sender)
|
||||||
|
|
||||||
if event.is_state():
|
if event.is_state():
|
||||||
prev_state = context.current_state.get((event.type, event.state_key))
|
prev_state = context.current_state.get((event.type, event.state_key))
|
||||||
|
@ -229,7 +243,7 @@ class MessageHandler(BaseHandler):
|
||||||
|
|
||||||
if event.type == EventTypes.Member:
|
if event.type == EventTypes.Member:
|
||||||
member_handler = self.hs.get_handlers().room_member_handler
|
member_handler = self.hs.get_handlers().room_member_handler
|
||||||
yield member_handler.change_membership(event, context, is_guest=is_guest)
|
yield member_handler.send_membership_event(event, context, is_guest=is_guest)
|
||||||
else:
|
else:
|
||||||
yield self.handle_new_client_event(
|
yield self.handle_new_client_event(
|
||||||
event=event,
|
event=event,
|
||||||
|
@ -241,6 +255,25 @@ class MessageHandler(BaseHandler):
|
||||||
with PreserveLoggingContext():
|
with PreserveLoggingContext():
|
||||||
presence.bump_presence_active_time(user)
|
presence.bump_presence_active_time(user)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def create_and_send_event(self, event_dict, ratelimit=True,
|
||||||
|
token_id=None, txn_id=None, is_guest=False):
|
||||||
|
"""
|
||||||
|
Creates an event, then sends it.
|
||||||
|
|
||||||
|
See self.create_event and self.send_event.
|
||||||
|
"""
|
||||||
|
event, context = yield self.create_event(
|
||||||
|
event_dict,
|
||||||
|
token_id=token_id,
|
||||||
|
txn_id=txn_id
|
||||||
|
)
|
||||||
|
yield self.send_event(
|
||||||
|
event,
|
||||||
|
context,
|
||||||
|
ratelimit=ratelimit,
|
||||||
|
is_guest=is_guest
|
||||||
|
)
|
||||||
defer.returnValue(event)
|
defer.returnValue(event)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
|
@ -22,7 +22,7 @@ from synapse.types import UserID, RoomAlias, RoomID
|
||||||
from synapse.api.constants import (
|
from synapse.api.constants import (
|
||||||
EventTypes, Membership, JoinRules, RoomCreationPreset,
|
EventTypes, Membership, JoinRules, RoomCreationPreset,
|
||||||
)
|
)
|
||||||
from synapse.api.errors import AuthError, StoreError, SynapseError
|
from synapse.api.errors import AuthError, StoreError, SynapseError, Codes
|
||||||
from synapse.util import stringutils, unwrapFirstError
|
from synapse.util import stringutils, unwrapFirstError
|
||||||
from synapse.util.async import run_on_reactor
|
from synapse.util.async import run_on_reactor
|
||||||
|
|
||||||
|
@ -397,7 +397,58 @@ class RoomMemberHandler(BaseHandler):
|
||||||
remotedomains.add(member.domain)
|
remotedomains.add(member.domain)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def change_membership(self, event, context, is_guest=False):
|
def update_membership(self, requester, target, room_id, action, txn_id=None):
|
||||||
|
effective_membership_state = action
|
||||||
|
if action in ["kick", "unban"]:
|
||||||
|
effective_membership_state = "leave"
|
||||||
|
elif action == "forget":
|
||||||
|
effective_membership_state = "leave"
|
||||||
|
|
||||||
|
msg_handler = self.hs.get_handlers().message_handler
|
||||||
|
|
||||||
|
content = {"membership": unicode(effective_membership_state)}
|
||||||
|
if requester.is_guest:
|
||||||
|
content["kind"] = "guest"
|
||||||
|
|
||||||
|
event, context = yield msg_handler.create_event(
|
||||||
|
{
|
||||||
|
"type": EventTypes.Member,
|
||||||
|
"content": content,
|
||||||
|
"room_id": room_id,
|
||||||
|
"sender": requester.user.to_string(),
|
||||||
|
"state_key": target.to_string(),
|
||||||
|
},
|
||||||
|
token_id=requester.access_token_id,
|
||||||
|
txn_id=txn_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
old_state = context.current_state.get((EventTypes.Member, event.state_key))
|
||||||
|
old_membership = old_state.content.get("membership") if old_state else None
|
||||||
|
if action == "unban" and old_membership != "ban":
|
||||||
|
raise SynapseError(
|
||||||
|
403,
|
||||||
|
"Cannot unban user who was not banned (membership=%s)" % old_membership,
|
||||||
|
errcode=Codes.BAD_STATE
|
||||||
|
)
|
||||||
|
if old_membership == "ban" and action != "unban":
|
||||||
|
raise SynapseError(
|
||||||
|
403,
|
||||||
|
"Cannot %s user who was is banned" % (action,),
|
||||||
|
errcode=Codes.BAD_STATE
|
||||||
|
)
|
||||||
|
|
||||||
|
yield msg_handler.send_event(
|
||||||
|
event,
|
||||||
|
context,
|
||||||
|
ratelimit=True,
|
||||||
|
is_guest=requester.is_guest
|
||||||
|
)
|
||||||
|
|
||||||
|
if action == "forget":
|
||||||
|
yield self.forget(requester.user, room_id)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def send_membership_event(self, event, context, is_guest=False):
|
||||||
""" Change the membership status of a user in a room.
|
""" Change the membership status of a user in a room.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
|
@ -442,7 +442,7 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
|
||||||
def register(self, http_server):
|
def register(self, http_server):
|
||||||
# /rooms/$roomid/[invite|join|leave]
|
# /rooms/$roomid/[invite|join|leave]
|
||||||
PATTERNS = ("/rooms/(?P<room_id>[^/]*)/"
|
PATTERNS = ("/rooms/(?P<room_id>[^/]*)/"
|
||||||
"(?P<membership_action>join|invite|leave|ban|kick|forget)")
|
"(?P<membership_action>join|invite|leave|ban|unban|kick|forget)")
|
||||||
register_txn_path(self, PATTERNS, http_server)
|
register_txn_path(self, PATTERNS, http_server)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
@ -451,9 +451,6 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
|
||||||
request,
|
request,
|
||||||
allow_guest=True,
|
allow_guest=True,
|
||||||
)
|
)
|
||||||
user = requester.user
|
|
||||||
|
|
||||||
effective_membership_action = membership_action
|
|
||||||
|
|
||||||
if requester.is_guest and membership_action not in {
|
if requester.is_guest and membership_action not in {
|
||||||
Membership.JOIN,
|
Membership.JOIN,
|
||||||
|
@ -463,13 +460,10 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
|
||||||
|
|
||||||
content = _parse_json(request)
|
content = _parse_json(request)
|
||||||
|
|
||||||
# target user is you unless it is an invite
|
|
||||||
state_key = user.to_string()
|
|
||||||
|
|
||||||
if membership_action == "invite" and self._has_3pid_invite_keys(content):
|
if membership_action == "invite" and self._has_3pid_invite_keys(content):
|
||||||
yield self.handlers.room_member_handler.do_3pid_invite(
|
yield self.handlers.room_member_handler.do_3pid_invite(
|
||||||
room_id,
|
room_id,
|
||||||
user,
|
requester.user,
|
||||||
content["medium"],
|
content["medium"],
|
||||||
content["address"],
|
content["address"],
|
||||||
content["id_server"],
|
content["id_server"],
|
||||||
|
@ -478,42 +472,21 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
|
||||||
)
|
)
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {}))
|
||||||
return
|
return
|
||||||
elif membership_action in ["invite", "ban", "kick"]:
|
|
||||||
if "user_id" in content:
|
target = requester.user
|
||||||
state_key = content["user_id"]
|
if membership_action in ["invite", "ban", "unban", "kick"]:
|
||||||
else:
|
if "user_id" not in content:
|
||||||
raise SynapseError(400, "Missing user_id key.")
|
raise SynapseError(400, "Missing user_id key.")
|
||||||
|
target = UserID.from_string(content["user_id"])
|
||||||
|
|
||||||
# make sure it looks like a user ID; it'll throw if it's invalid.
|
yield self.handlers.room_member_handler.update_membership(
|
||||||
UserID.from_string(state_key)
|
requester=requester,
|
||||||
|
target=target,
|
||||||
if membership_action == "kick":
|
room_id=room_id,
|
||||||
effective_membership_action = "leave"
|
action=membership_action,
|
||||||
elif membership_action == "forget":
|
|
||||||
effective_membership_action = "leave"
|
|
||||||
|
|
||||||
msg_handler = self.handlers.message_handler
|
|
||||||
|
|
||||||
content = {"membership": unicode(effective_membership_action)}
|
|
||||||
if requester.is_guest:
|
|
||||||
content["kind"] = "guest"
|
|
||||||
|
|
||||||
yield msg_handler.create_and_send_event(
|
|
||||||
{
|
|
||||||
"type": EventTypes.Member,
|
|
||||||
"content": content,
|
|
||||||
"room_id": room_id,
|
|
||||||
"sender": user.to_string(),
|
|
||||||
"state_key": state_key,
|
|
||||||
},
|
|
||||||
token_id=requester.access_token_id,
|
|
||||||
txn_id=txn_id,
|
txn_id=txn_id,
|
||||||
is_guest=requester.is_guest,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if membership_action == "forget":
|
|
||||||
yield self.handlers.room_member_handler.forget(user, room_id)
|
|
||||||
|
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {}))
|
||||||
|
|
||||||
def _has_3pid_invite_keys(self, content):
|
def _has_3pid_invite_keys(self, content):
|
||||||
|
|
|
@ -156,7 +156,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
|
||||||
builder
|
builder
|
||||||
)
|
)
|
||||||
|
|
||||||
yield room_handler.change_membership(event, context)
|
yield room_handler.send_membership_event(event, context)
|
||||||
|
|
||||||
self.state_handler.compute_event_context.assert_called_once_with(
|
self.state_handler.compute_event_context.assert_called_once_with(
|
||||||
builder
|
builder
|
||||||
|
@ -232,7 +232,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Actual invocation
|
# Actual invocation
|
||||||
yield room_handler.change_membership(event, context)
|
yield room_handler.send_membership_event(event, context)
|
||||||
|
|
||||||
self.federation.handle_new_event.assert_called_once_with(
|
self.federation.handle_new_event.assert_called_once_with(
|
||||||
event, destinations=set()
|
event, destinations=set()
|
||||||
|
@ -312,7 +312,7 @@ class RoomMemberHandlerTestCase(unittest.TestCase):
|
||||||
self.distributor.observe("user_left_room", leave_signal_observer)
|
self.distributor.observe("user_left_room", leave_signal_observer)
|
||||||
|
|
||||||
# Actual invocation
|
# Actual invocation
|
||||||
yield room_handler.change_membership(event, context)
|
yield room_handler.send_membership_event(event, context)
|
||||||
|
|
||||||
self.federation.handle_new_event.assert_called_once_with(
|
self.federation.handle_new_event.assert_called_once_with(
|
||||||
event, destinations=set(['red'])
|
event, destinations=set(['red'])
|
||||||
|
|
Loading…
Reference in a new issue