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

Extend spam checking rules #4875

Merged
merged 6 commits into from
Mar 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions synapse/events/spamcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,37 +46,48 @@ def check_event_for_spam(self, event):

return self.spam_checker.check_event_for_spam(event)

def user_may_invite(self, inviter_userid, invitee_userid, room_id):
def user_may_invite(self, inviter_userid, invitee_userid, room_id, new_room):
"""Checks if a given user may send an invite

If this method returns false, the invite will be rejected.

Args:
userid (string): The sender's user ID
inviter_userid (str)
invitee_userid (str)
room_id (str)
new_room (bool): Wether the user is being invited to the room as
part of a room creation, if so the invitee would have been
included in the call to `user_may_create_room`.

Returns:
bool: True if the user may send an invite, otherwise False
"""
if self.spam_checker is None:
return True

return self.spam_checker.user_may_invite(inviter_userid, invitee_userid, room_id)
return self.spam_checker.user_may_invite(
inviter_userid, invitee_userid, room_id, new_room,
)

def user_may_create_room(self, userid):
def user_may_create_room(self, userid, invite_list, cloning):
"""Checks if a given user may create a room

If this method returns false, the creation request will be rejected.

Args:
userid (string): The sender's user ID
invite_list (list[str]): List of user IDs that would be invited to
the new room.
cloning (bool): Whether the user is cloning an existing room, e.g.
upgrading a room.

Returns:
bool: True if the user may create a room, otherwise False
"""
if self.spam_checker is None:
return True

return self.spam_checker.user_may_create_room(userid)
return self.spam_checker.user_may_create_room(userid, invite_list, cloning)

def user_may_create_room_alias(self, userid, room_alias):
"""Checks if a given user may create a room alias
Expand Down Expand Up @@ -111,3 +122,21 @@ def user_may_publish_room(self, userid, room_id):
return True

return self.spam_checker.user_may_publish_room(userid, room_id)

def user_may_join_room(self, userid, room_id, is_invited):
"""Checks if a given users is allowed to join a room.

Is not called when the user creates a room.

Args:
userid (str)
room_id (str)
is_invited (bool): Whether the user is invited into the room

Returns:
bool: Whether the user may join the room
"""
if self.spam_checker is None:
return True

return self.spam_checker.user_may_join_room(userid, room_id, is_invited)
2 changes: 1 addition & 1 deletion synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ def on_invite_request(self, origin, pdu):
raise SynapseError(403, "This server does not accept room invites")

if not self.spam_checker.user_may_invite(
event.sender, event.state_key, event.room_id,
event.sender, event.state_key, event.room_id, new_room=False,
):
raise SynapseError(
403, "This user is not permitted to send invites to this server/user"
Expand Down
38 changes: 35 additions & 3 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def __init__(self, hs):
# linearizer to stop two upgrades happening at once
self._upgrade_linearizer = Linearizer("room_upgrade_linearizer")

self._server_notices_mxid = hs.config.server_notices_mxid

@defer.inlineCallbacks
def upgrade_room(self, requester, old_room_id, new_version):
"""Replace a room with a new room with a different version
Expand Down Expand Up @@ -254,7 +256,21 @@ def clone_existing_room(
"""
user_id = requester.user.to_string()

if not self.spam_checker.user_may_create_room(user_id):
if (self._server_notices_mxid is not None and
requester.user.to_string() == self._server_notices_mxid):
# allow the server notices mxid to create rooms
is_requester_admin = True

else:
is_requester_admin = yield self.auth.is_server_admin(
requester.user,
)

if not is_requester_admin and not self.spam_checker.user_may_create_room(
user_id,
invite_list=[],
cloning=True,
):
raise SynapseError(403, "You are not permitted to create rooms")

creation_content = {
Expand Down Expand Up @@ -475,7 +491,22 @@ def create_room(self, requester, config, ratelimit=True,

yield self.auth.check_auth_blocking(user_id)

if not self.spam_checker.user_may_create_room(user_id):
invite_list = config.get("invite", [])

if (self._server_notices_mxid is not None and
requester.user.to_string() == self._server_notices_mxid):
# allow the server notices mxid to create rooms
is_requester_admin = True
else:
is_requester_admin = yield self.auth.is_server_admin(
requester.user,
)

if not is_requester_admin and not self.spam_checker.user_may_create_room(
user_id,
invite_list=invite_list,
cloning=False,
):
raise SynapseError(403, "You are not permitted to create rooms")

if ratelimit:
Expand Down Expand Up @@ -518,7 +549,6 @@ def create_room(self, requester, config, ratelimit=True,
else:
room_alias = None

invite_list = config.get("invite", [])
for i in invite_list:
try:
UserID.from_string(i)
Expand Down Expand Up @@ -615,6 +645,7 @@ def create_room(self, requester, config, ratelimit=True,
"invite",
ratelimit=False,
content=content,
new_room=True,
)

for invite_3pid in invite_3pid_list:
Expand Down Expand Up @@ -699,6 +730,7 @@ def send(etype, content, **kwargs):
"join",
ratelimit=False,
content=creator_join_profile,
new_room=True,
)

# We treat the power levels override specially as this needs to be one
Expand Down
49 changes: 48 additions & 1 deletion synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,30 @@ def update_membership(
third_party_signed=None,
ratelimit=True,
content=None,
new_room=False,
):
"""Update a users membership in a room

Args:
requester (Requester)
target (UserID)
room_id (str)
action (str): The "action" the requester is performing against the
target. One of join/leave/kick/ban/invite/unban.
txn_id (str|None): The transaction ID associated with the request,
or None not provided.
remote_room_hosts (list[str]|None): List of remote servers to try
and join via if server isn't already in the room.
third_party_signed (dict|None): The signed object for third party
invites.
ratelimit (bool): Whether to apply ratelimiting to this request.
content (dict|None): Fields to include in the new events content.
new_room (bool): Whether these membership changes are happening
as part of a room creation (e.g. initial joins and invites)

Returns:
Deferred[FrozenEvent]
"""
key = (room_id,)

with (yield self.member_linearizer.queue(key)):
Expand All @@ -315,6 +338,7 @@ def update_membership(
third_party_signed=third_party_signed,
ratelimit=ratelimit,
content=content,
new_room=new_room,
)

defer.returnValue(result)
Expand All @@ -331,6 +355,7 @@ def _update_membership(
third_party_signed=None,
ratelimit=True,
content=None,
new_room=False,
):
content_specified = bool(content)
if content is None:
Expand Down Expand Up @@ -392,6 +417,7 @@ def _update_membership(

if not self.spam_checker.user_may_invite(
requester.user.to_string(), target.to_string(), room_id,
new_room=new_room,
):
logger.info("Blocking invite due to spam checker")
block_invite = True
Expand Down Expand Up @@ -461,8 +487,29 @@ def _update_membership(
# so don't really fit into the general auth process.
raise AuthError(403, "Guest access not allowed")

if (self._server_notices_mxid is not None and
requester.user.to_string() == self._server_notices_mxid):
# allow the server notices mxid to join rooms
is_requester_admin = True

else:
is_requester_admin = yield self.auth.is_server_admin(
requester.user,
)

inviter = yield self._get_inviter(target.to_string(), room_id)
if not is_requester_admin:
# We assume that if the spam checker allowed the user to create
# a room then they're allowed to join it.
if not new_room and not self.spam_checker.user_may_join_room(
target.to_string(), room_id,
is_invited=inviter is not None,
):
raise SynapseError(
403, "Not allowed to join this room",
)

if not is_host_in_room:
inviter = yield self._get_inviter(target.to_string(), room_id)
if inviter and not self.hs.is_mine(inviter):
remote_room_hosts.append(inviter.domain)

Expand Down
45 changes: 42 additions & 3 deletions synapse/rulecheck/domain_rule_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@ class DomainRuleChecker(object):
"inviter_domain": [ "invitee_domain_permitted", "other_domain_permitted" ]
"other_inviter_domain": [ "invitee_domain_permitted" ]
default: False
}

# Only let local users join rooms if they were explicitly invited.
can_only_join_rooms_with_invite: false

# Only let local users create rooms if they are inviting only one
# other user, and that user matches the rules above.
can_only_create_one_to_one_rooms: false

# Only let local users invite during room creation, regardless of the
# domain mapping rules above.
can_only_invite_during_room_creation: false

Don't forget to consider if you can invite users from your own domain.
"""
Expand All @@ -43,14 +53,28 @@ def __init__(self, config):
self.domain_mapping = config["domain_mapping"] or {}
self.default = config["default"]

self.can_only_join_rooms_with_invite = config.get(
"can_only_join_rooms_with_invite", False,
)
self.can_only_create_one_to_one_rooms = config.get(
"can_only_create_one_to_one_rooms", False,
)
self.can_only_invite_during_room_creation = config.get(
"can_only_invite_during_room_creation", False,
)

def check_event_for_spam(self, event):
"""Implements synapse.events.SpamChecker.check_event_for_spam
"""
return False

def user_may_invite(self, inviter_userid, invitee_userid, room_id):
def user_may_invite(self, inviter_userid, invitee_userid, room_id,
new_room):
"""Implements synapse.events.SpamChecker.user_may_invite
"""
if self.can_only_invite_during_room_creation and not new_room:
return False

inviter_domain = self._get_domain_from_id(inviter_userid)
invitee_domain = self._get_domain_from_id(invitee_userid)

Expand All @@ -59,9 +83,16 @@ def user_may_invite(self, inviter_userid, invitee_userid, room_id):

return invitee_domain in self.domain_mapping[inviter_domain]

def user_may_create_room(self, userid):
def user_may_create_room(self, userid, invite_list, cloning):
"""Implements synapse.events.SpamChecker.user_may_create_room
"""

if cloning:
return True

if self.can_only_create_one_to_one_rooms and len(invite_list) != 1:
return False

return True

def user_may_create_room_alias(self, userid, room_alias):
Expand All @@ -74,6 +105,14 @@ def user_may_publish_room(self, userid, room_id):
"""
return True

def user_may_join_room(self, userid, room_id, is_invited):
"""Implements synapse.events.SpamChecker.user_may_join_room
"""
if self.can_only_join_rooms_with_invite and not is_invited:
return False

return True

@staticmethod
def parse_config(config):
"""Implements synapse.events.SpamChecker.parse_config
Expand Down
Loading