1
0

Incorporate review

This commit is contained in:
Brendan Abolivier
2019-06-18 14:53:33 +01:00
parent f7339d42ee
commit d36a876d2d
+71 -80
View File
@@ -18,14 +18,21 @@ import email.utils
from twisted.internet import defer
from synapse.api.constants import EventTypes
from synapse.api.errors import SynapseError
from synapse.config._base import ConfigError
from synapse.rulecheck.domain_rule_checker import DomainRuleChecker
from synapse.types import get_domain_from_id
ACCESS_RULES_TYPE = "im.vector.room.access_rules"
ACCESS_RULE_RESTRICTED = "restricted"
ACCESS_RULE_UNRESTRICTED = "unrestricted"
ACCESS_RULE_DIRECT = "direct"
VALID_ACCESS_RULES = (
ACCESS_RULE_DIRECT,
ACCESS_RULE_RESTRICTED,
ACCESS_RULE_UNRESTRICTED,
)
class RoomAccessRules(object):
"""Implementation of the ThirdPartyEventRules module API that allows federation admins
@@ -75,7 +82,7 @@ class RoomAccessRules(object):
rules_in_initial_state = False
# If there's a rules event in the initial state, check if it complies with the
# spec for im.vector.room.access_rules and fix it if not.
# spec for im.vector.room.access_rules and deny the request if not.
for event in config.get("initial_state", []):
if event["type"] == ACCESS_RULES_TYPE:
rules_in_initial_state = True
@@ -84,27 +91,27 @@ class RoomAccessRules(object):
# Make sure the event has a valid content.
if rule is None:
event["content"] = {
"rule": self._on_create_room_default_rule(is_direct)
}
raise SynapseError(400, "Invalid access rule",)
# Make sure the rule name is valid.
if not self._is_rule_name_valid(rule):
event["content"]["rule"] = self._on_create_room_default_rule(
is_direct,
)
if rule not in VALID_ACCESS_RULES:
raise SynapseError(400, "Invalid access rule", )
# Make sure the rule is "direct" if the room is a direct chat.
if is_direct and rule != ACCESS_RULE_DIRECT:
event["content"]["rule"] = ACCESS_RULE_DIRECT
# Make sure the rule is not "direct" if the room isn't a direct chat.
if rule == ACCESS_RULE_DIRECT and not is_direct:
event["content"]["rule"] = ACCESS_RULE_RESTRICTED
if (
(is_direct and rule != ACCESS_RULE_DIRECT)
or (rule == ACCESS_RULE_DIRECT and not is_direct)
):
raise SynapseError(400, "Invalid access rule",)
# If there's no rules event in the initial state, create one with the default
# setting.
if not rules_in_initial_state:
if is_direct:
default_rule = ACCESS_RULE_DIRECT
else:
default_rule = ACCESS_RULE_RESTRICTED
if not config.get("initial_state"):
config["initial_state"] = []
@@ -112,25 +119,10 @@ class RoomAccessRules(object):
"type": ACCESS_RULES_TYPE,
"state_key": "",
"content": {
"rule": self._on_create_room_default_rule(is_direct),
"rule": default_rule,
}
})
@staticmethod
def _on_create_room_default_rule(is_direct):
"""Returns the default rule to set.
Args:
is_direct (bool): Is the room created with "is_direct" set to True.
Returns:
str, the name of the rule tu use as the default.
"""
if is_direct:
return ACCESS_RULE_DIRECT
else:
return ACCESS_RULE_RESTRICTED
@defer.inlineCallbacks
def check_threepid_can_be_invited(self, medium, address, state_events):
"""Implements synapse.events.ThirdPartyEventRules.check_threepid_can_be_invited
@@ -145,7 +137,9 @@ class RoomAccessRules(object):
defer.returnValue(False)
if rule != ACCESS_RULE_RESTRICTED:
# Only "restricted" requires filtering 3PID invites.
# Only "restricted" requires filtering 3PID invites. We don't need to do
# anything for "direct" here, because only "restricted" requires filtering
# based on the HS the address is mapped to.
defer.returnValue(True)
parsed_address = email.utils.parseaddr(address)[1]
@@ -211,17 +205,16 @@ class RoomAccessRules(object):
new_rule = event.content.get("rule")
# Check for invalid values.
if not self._is_rule_name_valid(new_rule):
if new_rule not in VALID_ACCESS_RULES:
return False
# Make sure we don't apply "direct" if the room has more than two members.
if new_rule == ACCESS_RULE_DIRECT:
member_events_count = 0
for key, event in state_events.items():
if key[0] == EventTypes.Member:
member_events_count += 1
existing_members, threepid_tokens = self._get_members_and_tokens_from_state(
state_events,
)
if member_events_count > 2:
if len(existing_members) > 2 or len(threepid_tokens) > 1:
return False
prev_rules_event = state_events.get((ACCESS_RULES_TYPE, ""))
@@ -251,7 +244,7 @@ class RoomAccessRules(object):
# included in a limited list of domains.
if event.type != EventTypes.Member and event.type != EventTypes.ThirdPartyInvite:
return True
invitee_domain = DomainRuleChecker._get_domain_from_id(event.state_key)
invitee_domain = get_domain_from_id(event.state_key)
return invitee_domain not in self.domains_forbidden_when_restricted
def _apply_unrestricted(self):
@@ -279,26 +272,21 @@ class RoomAccessRules(object):
if event.type != EventTypes.Member and event.type != EventTypes.ThirdPartyInvite:
return True
# Get the m.room.member and m.room.third_party_invite events from the room's
# state.
member_events = []
threepid_invite_events = []
for key, event in state_events.items():
if key[0] == EventTypes.Member:
member_events.append(event)
if key[0] == EventTypes.ThirdPartyInvite:
threepid_invite_events.append(event)
# Get the room memberships and 3PID invite tokens from the room's state.
existing_members, threepid_tokens = self._get_members_and_tokens_from_state(
state_events,
)
# There should never be more than one 3PID invite in the room state: if the second
# original user came and left, and we're inviting them using their email address,
# given we know they have a Matrix account binded to the address (so they could
# join the first time), Synapse will successfully look it up before attempting to
# store an invite on the IS.
if len(threepid_invite_events) == 1 and event.type == EventTypes.ThirdPartyInvite:
if len(threepid_tokens) == 1 and event.type == EventTypes.ThirdPartyInvite:
# If we already have a 3PID invite in flight, don't accept another one.
return False
if len(member_events) == 2:
if len(existing_members) == 2:
# If the user was within the two initial user of the room, Synapse would have
# looked it up successfully and thus sent a m.room.member here instead of
# m.room.third_party_invite.
@@ -308,16 +296,11 @@ class RoomAccessRules(object):
# We can only have m.room.member events here. The rule in this case is to only
# allow the event if its target is one of the initial two members in the room,
# i.e. the state key of one of the two m.room.member states in the room.
target = event.state_key
for e in member_events:
if e.state_key == target:
return True
return False
return event.state_key in existing_members
# We're alone in the room (and always have been) and there's one 3PID invite in
# flight.
if len(member_events) == 1 and len(threepid_invite_events) == 1:
if len(existing_members) == 1 and len(threepid_tokens) == 1:
# We can only have m.room.member events here. In this case, we can only allow
# the event if it's either a m.room.member from the joined user (we can assume
# that the only m.room.member event is a join otherwise we wouldn't be able to
@@ -325,10 +308,9 @@ class RoomAccessRules(object):
# user.
target = event.state_key
is_from_threepid_invite = self._is_invite_from_threepid(
event, threepid_invite_events[0],
event, threepid_tokens[0],
)
if is_from_threepid_invite or target == member_events[0].state_key:
if is_from_threepid_invite or target == existing_members[0]:
return True
return False
@@ -341,7 +323,7 @@ class RoomAccessRules(object):
Args:
state_events (dict[tuple[event type, state key], EventBase]): The set of state
events
events.
Returns:
str, the name of the rule (either "direct", "restricted" or "unrestricted")
"""
@@ -353,28 +335,37 @@ class RoomAccessRules(object):
return rule
@staticmethod
def _is_invite_from_threepid(invite, threepid_invite):
def _get_members_and_tokens_from_state(state_events):
"""Retrieves from a list of state events the list of users that have a
m.room.member event in the room, and the tokens of 3PID invites in the room.
Args:
state_events (dict[tuple[event type, state key], EventBase]): The set of state
events.
Returns:
existing_members (list[str]): List of targets of the m.room.member events in
the state.
threepid_invite_tokens (list[str]): List of tokens of the 3PID invites in the
state.
"""
existing_members = []
threepid_invite_tokens = []
for key, event in state_events.items():
if key[0] == EventTypes.Member:
existing_members.append(event.state_key)
if key[0] == EventTypes.ThirdPartyInvite:
threepid_invite_tokens.append(event.state_key)
return existing_members, threepid_invite_tokens
@staticmethod
def _is_invite_from_threepid(invite, threepid_invite_token):
"""Checks whether the given invite follows the given 3PID invite.
Args:
invite (EventBase): The m.room.member event with "invite" membership.
threepid_invite (EventBase): The m.room.third_party_invite event.
threepid_invite_token (str): The state key from the 3PID invite.
"""
token = invite.content.get("third_party_signed", {}).get("token", "")
return token == threepid_invite.state_key
@staticmethod
def _is_rule_name_valid(rule):
"""Returns whether the given rule name is within the allowed values ("direct",
"restricted" or "unrestricted").
Args:
rule (str): The name of the rule.
Returns:
bool, True if the name is valid, False otherwise.
"""
return (
rule == ACCESS_RULE_DIRECT
or rule == ACCESS_RULE_RESTRICTED
or rule == ACCESS_RULE_UNRESTRICTED
)
return token == threepid_invite_token