mirror of
https://mau.dev/maunium/synapse.git
synced 2024-08-27 12:42:39 +00:00
Merge branch 'room_config' into develop
This commit is contained in:
commit
a85612baf8
@ -17,9 +17,10 @@
|
|||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import Membership
|
from synapse.api.constants import Membership, JoinRules
|
||||||
from synapse.api.errors import AuthError, StoreError, Codes
|
from synapse.api.errors import AuthError, StoreError, Codes
|
||||||
from synapse.api.events.room import RoomMemberEvent
|
from synapse.api.events.room import RoomMemberEvent
|
||||||
|
from synapse.util.logutils import log_function
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -44,15 +45,28 @@ class Auth(object):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if hasattr(event, "room_id"):
|
if hasattr(event, "room_id"):
|
||||||
|
is_state = hasattr(event, "state_key")
|
||||||
|
|
||||||
if event.type == RoomMemberEvent.TYPE:
|
if event.type == RoomMemberEvent.TYPE:
|
||||||
|
yield self._can_replace_state(event)
|
||||||
allowed = yield self.is_membership_change_allowed(event)
|
allowed = yield self.is_membership_change_allowed(event)
|
||||||
defer.returnValue(allowed)
|
defer.returnValue(allowed)
|
||||||
else:
|
return
|
||||||
|
|
||||||
self._check_joined_room(
|
self._check_joined_room(
|
||||||
member=snapshot.membership_state,
|
member=snapshot.membership_state,
|
||||||
user_id=snapshot.user_id,
|
user_id=snapshot.user_id,
|
||||||
room_id=snapshot.room_id,
|
room_id=snapshot.room_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if is_state:
|
||||||
|
# TODO (erikj): This really only should be called for *new*
|
||||||
|
# state
|
||||||
|
yield self._can_add_state(event)
|
||||||
|
yield self._can_replace_state(event)
|
||||||
|
else:
|
||||||
|
yield self._can_send_event(event)
|
||||||
|
|
||||||
defer.returnValue(True)
|
defer.returnValue(True)
|
||||||
else:
|
else:
|
||||||
raise AuthError(500, "Unknown event: %s" % event)
|
raise AuthError(500, "Unknown event: %s" % event)
|
||||||
@ -111,7 +125,14 @@ class Auth(object):
|
|||||||
|
|
||||||
membership = event.content["membership"]
|
membership = event.content["membership"]
|
||||||
|
|
||||||
|
join_rule = yield self.store.get_room_join_rule(event.room_id)
|
||||||
|
if not join_rule:
|
||||||
|
join_rule = JoinRules.INVITE
|
||||||
|
|
||||||
if Membership.INVITE == membership:
|
if Membership.INVITE == membership:
|
||||||
|
# TODO (erikj): We should probably handle this more intelligently
|
||||||
|
# PRIVATE join rules.
|
||||||
|
|
||||||
# Invites are valid iff caller is in the room and target isn't.
|
# Invites are valid iff caller is in the room and target isn't.
|
||||||
if not caller_in_room: # caller isn't joined
|
if not caller_in_room: # caller isn't joined
|
||||||
raise AuthError(403, "You are not in room %s." % event.room_id)
|
raise AuthError(403, "You are not in room %s." % event.room_id)
|
||||||
@ -124,18 +145,42 @@ class Auth(object):
|
|||||||
# joined: It's a NOOP
|
# joined: It's a NOOP
|
||||||
if event.user_id != target_user_id:
|
if event.user_id != target_user_id:
|
||||||
raise AuthError(403, "Cannot force another user to join.")
|
raise AuthError(403, "Cannot force another user to join.")
|
||||||
elif room.is_public:
|
elif join_rule == JoinRules.PUBLIC or room.is_public:
|
||||||
pass # anyone can join public rooms.
|
pass
|
||||||
elif (not caller or caller.membership not in
|
elif join_rule == JoinRules.INVITE:
|
||||||
[Membership.INVITE, Membership.JOIN]):
|
if (
|
||||||
|
not caller or caller.membership not in
|
||||||
|
[Membership.INVITE, Membership.JOIN]
|
||||||
|
):
|
||||||
raise AuthError(403, "You are not invited to this room.")
|
raise AuthError(403, "You are not invited to this room.")
|
||||||
|
else:
|
||||||
|
# TODO (erikj): may_join list
|
||||||
|
# TODO (erikj): private rooms
|
||||||
|
raise AuthError(403, "You are not allowed to join this room")
|
||||||
elif Membership.LEAVE == membership:
|
elif Membership.LEAVE == membership:
|
||||||
|
# TODO (erikj): Implement kicks.
|
||||||
|
|
||||||
if not caller_in_room: # trying to leave a room you aren't joined
|
if not caller_in_room: # trying to leave a room you aren't joined
|
||||||
raise AuthError(403, "You are not in room %s." % event.room_id)
|
raise AuthError(403, "You are not in room %s." % event.room_id)
|
||||||
elif target_user_id != event.user_id:
|
elif target_user_id != event.user_id:
|
||||||
# trying to force another user to leave
|
# trying to force another user to leave
|
||||||
raise AuthError(403, "Cannot force %s to leave." %
|
raise AuthError(403, "Cannot force %s to leave." %
|
||||||
target_user_id)
|
target_user_id)
|
||||||
|
elif Membership.BAN == membership:
|
||||||
|
user_level = yield self.store.get_power_level(
|
||||||
|
event.room_id,
|
||||||
|
event.user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
ban_level, _ = yield self.store.get_ops_levels(event.room_id)
|
||||||
|
|
||||||
|
if ban_level:
|
||||||
|
ban_level = int(ban_level)
|
||||||
|
else:
|
||||||
|
ban_level = 5 # FIXME (erikj): What should we do here?
|
||||||
|
|
||||||
|
if user_level < ban_level:
|
||||||
|
raise AuthError(403, "You don't have permission to ban")
|
||||||
else:
|
else:
|
||||||
raise AuthError(500, "Unknown membership %s" % membership)
|
raise AuthError(500, "Unknown membership %s" % membership)
|
||||||
|
|
||||||
@ -176,3 +221,85 @@ class Auth(object):
|
|||||||
except StoreError:
|
except StoreError:
|
||||||
raise AuthError(403, "Unrecognised access token.",
|
raise AuthError(403, "Unrecognised access token.",
|
||||||
errcode=Codes.UNKNOWN_TOKEN)
|
errcode=Codes.UNKNOWN_TOKEN)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
@log_function
|
||||||
|
def _can_send_event(self, event):
|
||||||
|
send_level = yield self.store.get_send_event_level(event.room_id)
|
||||||
|
|
||||||
|
if send_level:
|
||||||
|
send_level = int(send_level)
|
||||||
|
else:
|
||||||
|
send_level = 0
|
||||||
|
|
||||||
|
user_level = yield self.store.get_power_level(
|
||||||
|
event.room_id,
|
||||||
|
event.user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_level:
|
||||||
|
user_level = int(user_level)
|
||||||
|
else:
|
||||||
|
user_level = 0
|
||||||
|
|
||||||
|
if user_level < send_level:
|
||||||
|
raise AuthError(
|
||||||
|
403, "You don't have permission to post to the room"
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue(True)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def _can_add_state(self, event):
|
||||||
|
add_level = yield self.store.get_add_state_level(event.room_id)
|
||||||
|
|
||||||
|
if not add_level:
|
||||||
|
defer.returnValue(True)
|
||||||
|
|
||||||
|
add_level = int(add_level)
|
||||||
|
|
||||||
|
user_level = yield self.store.get_power_level(
|
||||||
|
event.room_id,
|
||||||
|
event.user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
user_level = int(user_level)
|
||||||
|
|
||||||
|
if user_level < add_level:
|
||||||
|
raise AuthError(
|
||||||
|
403, "You don't have permission to add state to the room"
|
||||||
|
)
|
||||||
|
|
||||||
|
defer.returnValue(True)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def _can_replace_state(self, event):
|
||||||
|
current_state = yield self.store.get_current_state(
|
||||||
|
event.room_id,
|
||||||
|
event.type,
|
||||||
|
event.state_key,
|
||||||
|
)
|
||||||
|
|
||||||
|
if current_state:
|
||||||
|
current_state = current_state[0]
|
||||||
|
|
||||||
|
user_level = yield self.store.get_power_level(
|
||||||
|
event.room_id,
|
||||||
|
event.user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_level:
|
||||||
|
user_level = int(user_level)
|
||||||
|
else:
|
||||||
|
user_level = 0
|
||||||
|
|
||||||
|
logger.debug("Checking power level for %s, %s", event.user_id, user_level)
|
||||||
|
if current_state and hasattr(current_state, "required_power_level"):
|
||||||
|
req = current_state.required_power_level
|
||||||
|
|
||||||
|
logger.debug("Checked power level for %s, %s", event.user_id, req)
|
||||||
|
if user_level < req:
|
||||||
|
raise AuthError(
|
||||||
|
403,
|
||||||
|
"You don't have permission to change that state"
|
||||||
|
)
|
||||||
|
@ -23,7 +23,8 @@ class Membership(object):
|
|||||||
JOIN = u"join"
|
JOIN = u"join"
|
||||||
KNOCK = u"knock"
|
KNOCK = u"knock"
|
||||||
LEAVE = u"leave"
|
LEAVE = u"leave"
|
||||||
LIST = (INVITE, JOIN, KNOCK, LEAVE)
|
BAN = u"ban"
|
||||||
|
LIST = (INVITE, JOIN, KNOCK, LEAVE, BAN)
|
||||||
|
|
||||||
|
|
||||||
class Feedback(object):
|
class Feedback(object):
|
||||||
@ -42,3 +43,10 @@ class PresenceState(object):
|
|||||||
UNAVAILABLE = u"unavailable"
|
UNAVAILABLE = u"unavailable"
|
||||||
ONLINE = u"online"
|
ONLINE = u"online"
|
||||||
FREE_FOR_CHAT = u"free_for_chat"
|
FREE_FOR_CHAT = u"free_for_chat"
|
||||||
|
|
||||||
|
|
||||||
|
class JoinRules(object):
|
||||||
|
PUBLIC = u"public"
|
||||||
|
KNOCK = u"knock"
|
||||||
|
INVITE = u"invite"
|
||||||
|
PRIVATE = u"private"
|
||||||
|
@ -42,6 +42,7 @@ class SynapseEvent(JsonEncodedObject):
|
|||||||
"user_id", # sender/initiator
|
"user_id", # sender/initiator
|
||||||
"content", # HTTP body, JSON
|
"content", # HTTP body, JSON
|
||||||
"state_key",
|
"state_key",
|
||||||
|
"required_power_level",
|
||||||
]
|
]
|
||||||
|
|
||||||
internal_keys = [
|
internal_keys = [
|
||||||
@ -52,6 +53,7 @@ class SynapseEvent(JsonEncodedObject):
|
|||||||
"destinations",
|
"destinations",
|
||||||
"origin",
|
"origin",
|
||||||
"outlier",
|
"outlier",
|
||||||
|
"power_level",
|
||||||
]
|
]
|
||||||
|
|
||||||
required_keys = [
|
required_keys = [
|
||||||
@ -152,3 +154,10 @@ class SynapseEvent(JsonEncodedObject):
|
|||||||
msg = self._check_json(entry, template[key][0])
|
msg = self._check_json(entry, template[key][0])
|
||||||
if msg:
|
if msg:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
class SynapseStateEvent(SynapseEvent):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
if "state_key" not in kwargs:
|
||||||
|
kwargs["state_key"] = ""
|
||||||
|
super(SynapseStateEvent, self).__init__(**kwargs)
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
from synapse.api.events.room import (
|
from synapse.api.events.room import (
|
||||||
RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
|
RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
|
||||||
InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent,
|
InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent,
|
||||||
|
RoomPowerLevelsEvent, RoomJoinRulesEvent, RoomOpsPowerLevelsEvent,
|
||||||
|
RoomCreateEvent, RoomAddStateLevelEvent, RoomSendEventLevelEvent
|
||||||
)
|
)
|
||||||
|
|
||||||
from synapse.util.stringutils import random_string
|
from synapse.util.stringutils import random_string
|
||||||
@ -30,7 +32,13 @@ class EventFactory(object):
|
|||||||
RoomMemberEvent,
|
RoomMemberEvent,
|
||||||
FeedbackEvent,
|
FeedbackEvent,
|
||||||
InviteJoinEvent,
|
InviteJoinEvent,
|
||||||
RoomConfigEvent
|
RoomConfigEvent,
|
||||||
|
RoomPowerLevelsEvent,
|
||||||
|
RoomJoinRulesEvent,
|
||||||
|
RoomCreateEvent,
|
||||||
|
RoomAddStateLevelEvent,
|
||||||
|
RoomSendEventLevelEvent,
|
||||||
|
RoomOpsPowerLevelsEvent,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
from synapse.api.constants import Feedback, Membership
|
from synapse.api.constants import Feedback, Membership
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from . import SynapseEvent
|
from . import SynapseEvent, SynapseStateEvent
|
||||||
|
|
||||||
|
|
||||||
class GenericEvent(SynapseEvent):
|
class GenericEvent(SynapseEvent):
|
||||||
@ -132,3 +132,45 @@ class RoomConfigEvent(SynapseEvent):
|
|||||||
|
|
||||||
def get_content_template(self):
|
def get_content_template(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class RoomCreateEvent(SynapseStateEvent):
|
||||||
|
TYPE = "m.room.create"
|
||||||
|
|
||||||
|
def get_content_template(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class RoomJoinRulesEvent(SynapseStateEvent):
|
||||||
|
TYPE = "m.room.join_rules"
|
||||||
|
|
||||||
|
def get_content_template(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class RoomPowerLevelsEvent(SynapseStateEvent):
|
||||||
|
TYPE = "m.room.power_levels"
|
||||||
|
|
||||||
|
def get_content_template(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class RoomAddStateLevelEvent(SynapseStateEvent):
|
||||||
|
TYPE = "m.room.add_state_level"
|
||||||
|
|
||||||
|
def get_content_template(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class RoomSendEventLevelEvent(SynapseStateEvent):
|
||||||
|
TYPE = "m.room.send_event_level"
|
||||||
|
|
||||||
|
def get_content_template(self):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class RoomOpsPowerLevelsEvent(SynapseStateEvent):
|
||||||
|
TYPE = "m.room.ops_levels"
|
||||||
|
|
||||||
|
def get_content_template(self):
|
||||||
|
return {}
|
||||||
|
@ -68,6 +68,7 @@ class Pdu(JsonEncodedObject):
|
|||||||
"power_level",
|
"power_level",
|
||||||
"prev_state_id",
|
"prev_state_id",
|
||||||
"prev_state_origin",
|
"prev_state_origin",
|
||||||
|
"required_power_level",
|
||||||
]
|
]
|
||||||
|
|
||||||
internal_keys = [
|
internal_keys = [
|
||||||
|
@ -17,10 +17,12 @@
|
|||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.types import UserID, RoomAlias, RoomID
|
from synapse.types import UserID, RoomAlias, RoomID
|
||||||
from synapse.api.constants import Membership
|
from synapse.api.constants import Membership, JoinRules
|
||||||
from synapse.api.errors import StoreError, SynapseError
|
from synapse.api.errors import StoreError, SynapseError
|
||||||
from synapse.api.events.room import (
|
from synapse.api.events.room import (
|
||||||
RoomMemberEvent, RoomConfigEvent
|
RoomMemberEvent, RoomCreateEvent, RoomPowerLevelsEvent,
|
||||||
|
RoomJoinRulesEvent, RoomAddStateLevelEvent,
|
||||||
|
RoomSendEventLevelEvent, RoomOpsPowerLevelsEvent,
|
||||||
)
|
)
|
||||||
from synapse.util import stringutils
|
from synapse.util import stringutils
|
||||||
from ._base import BaseRoomHandler
|
from ._base import BaseRoomHandler
|
||||||
@ -62,6 +64,8 @@ class RoomCreationHandler(BaseRoomHandler):
|
|||||||
else:
|
else:
|
||||||
room_alias = None
|
room_alias = None
|
||||||
|
|
||||||
|
is_public = config.get("visibility", None) == "public"
|
||||||
|
|
||||||
if room_id:
|
if room_id:
|
||||||
# Ensure room_id is the correct type
|
# Ensure room_id is the correct type
|
||||||
room_id_obj = RoomID.from_string(room_id, self.hs)
|
room_id_obj = RoomID.from_string(room_id, self.hs)
|
||||||
@ -71,7 +75,7 @@ class RoomCreationHandler(BaseRoomHandler):
|
|||||||
yield self.store.store_room(
|
yield self.store.store_room(
|
||||||
room_id=room_id,
|
room_id=room_id,
|
||||||
room_creator_user_id=user_id,
|
room_creator_user_id=user_id,
|
||||||
is_public=config["visibility"] == "public"
|
is_public=is_public
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# autogen room IDs and try to create it. We may clash, so just
|
# autogen room IDs and try to create it. We may clash, so just
|
||||||
@ -85,7 +89,7 @@ class RoomCreationHandler(BaseRoomHandler):
|
|||||||
yield self.store.store_room(
|
yield self.store.store_room(
|
||||||
room_id=gen_room_id.to_string(),
|
room_id=gen_room_id.to_string(),
|
||||||
room_creator_user_id=user_id,
|
room_creator_user_id=user_id,
|
||||||
is_public=config["visibility"] == "public"
|
is_public=is_public
|
||||||
)
|
)
|
||||||
room_id = gen_room_id.to_string()
|
room_id = gen_room_id.to_string()
|
||||||
break
|
break
|
||||||
@ -94,18 +98,10 @@ class RoomCreationHandler(BaseRoomHandler):
|
|||||||
if not room_id:
|
if not room_id:
|
||||||
raise StoreError(500, "Couldn't generate a room ID.")
|
raise StoreError(500, "Couldn't generate a room ID.")
|
||||||
|
|
||||||
config_event = self.event_factory.create_event(
|
|
||||||
etype=RoomConfigEvent.TYPE,
|
|
||||||
room_id=room_id,
|
|
||||||
user_id=user_id,
|
|
||||||
content=config,
|
|
||||||
)
|
|
||||||
|
|
||||||
snapshot = yield self.store.snapshot_room(
|
user = self.hs.parse_userid(user_id)
|
||||||
room_id=room_id,
|
creation_events = self._create_events_for_new_room(
|
||||||
user_id=user_id,
|
user, room_id, is_public=is_public
|
||||||
state_type=RoomConfigEvent.TYPE,
|
|
||||||
state_key="",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if room_alias:
|
if room_alias:
|
||||||
@ -115,11 +111,18 @@ class RoomCreationHandler(BaseRoomHandler):
|
|||||||
servers=[self.hs.hostname],
|
servers=[self.hs.hostname],
|
||||||
)
|
)
|
||||||
|
|
||||||
yield self.state_handler.handle_new_event(config_event, snapshot)
|
|
||||||
# store_id = persist...
|
|
||||||
|
|
||||||
federation_handler = self.hs.get_handlers().federation_handler
|
federation_handler = self.hs.get_handlers().federation_handler
|
||||||
yield federation_handler.handle_new_event(config_event, snapshot)
|
|
||||||
|
for event in creation_events:
|
||||||
|
snapshot = yield self.store.snapshot_room(
|
||||||
|
room_id=room_id,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Event: %s", event)
|
||||||
|
|
||||||
|
yield self.state_handler.handle_new_event(event, snapshot)
|
||||||
|
yield self._on_new_room_event(event, snapshot, extra_users=[user])
|
||||||
|
|
||||||
content = {"membership": Membership.JOIN}
|
content = {"membership": Membership.JOIN}
|
||||||
join_event = self.event_factory.create_event(
|
join_event = self.event_factory.create_event(
|
||||||
@ -142,6 +145,63 @@ class RoomCreationHandler(BaseRoomHandler):
|
|||||||
|
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
|
def _create_events_for_new_room(self, creator, room_id, is_public=False):
|
||||||
|
event_keys = {
|
||||||
|
"room_id": room_id,
|
||||||
|
"user_id": creator.to_string(),
|
||||||
|
"required_power_level": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
def create(etype, **content):
|
||||||
|
return self.event_factory.create_event(
|
||||||
|
etype=etype,
|
||||||
|
content=content,
|
||||||
|
**event_keys
|
||||||
|
)
|
||||||
|
|
||||||
|
creation_event = create(
|
||||||
|
etype=RoomCreateEvent.TYPE,
|
||||||
|
creator=creator.to_string(),
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
power_levels_event = self.event_factory.create_event(
|
||||||
|
etype=RoomPowerLevelsEvent.TYPE,
|
||||||
|
content={creator.to_string(): 10, "default": 0},
|
||||||
|
**event_keys
|
||||||
|
)
|
||||||
|
|
||||||
|
join_rule = JoinRules.PUBLIC if is_public else JoinRules.INVITE
|
||||||
|
join_rules_event = create(
|
||||||
|
etype=RoomJoinRulesEvent.TYPE,
|
||||||
|
join_rule=join_rule,
|
||||||
|
)
|
||||||
|
|
||||||
|
add_state_event = create(
|
||||||
|
etype=RoomAddStateLevelEvent.TYPE,
|
||||||
|
level=10,
|
||||||
|
)
|
||||||
|
|
||||||
|
send_event = create(
|
||||||
|
etype=RoomSendEventLevelEvent.TYPE,
|
||||||
|
level=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
ops = create(
|
||||||
|
etype=RoomOpsPowerLevelsEvent.TYPE,
|
||||||
|
ban_level=5,
|
||||||
|
kick_level=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
creation_event,
|
||||||
|
power_levels_event,
|
||||||
|
join_rules_event,
|
||||||
|
add_state_event,
|
||||||
|
send_event,
|
||||||
|
ops,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class RoomMemberHandler(BaseRoomHandler):
|
class RoomMemberHandler(BaseRoomHandler):
|
||||||
# TODO(paul): This handler currently contains a messy conflation of
|
# TODO(paul): This handler currently contains a messy conflation of
|
||||||
@ -285,6 +345,16 @@ class RoomMemberHandler(BaseRoomHandler):
|
|||||||
if do_auth:
|
if do_auth:
|
||||||
yield self.auth.check(event, snapshot, raises=True)
|
yield self.auth.check(event, snapshot, raises=True)
|
||||||
|
|
||||||
|
# If we're banning someone, set a req power level
|
||||||
|
if event.membership == Membership.BAN:
|
||||||
|
if not hasattr(event, "required_power_level") or event.required_power_level is None:
|
||||||
|
# Add some default required_power_level
|
||||||
|
user_level = yield self.store.get_power_level(
|
||||||
|
event.room_id,
|
||||||
|
event.user_id,
|
||||||
|
)
|
||||||
|
event.required_power_level = user_level
|
||||||
|
|
||||||
if prev_state and prev_state.membership == event.membership:
|
if prev_state and prev_state.membership == event.membership:
|
||||||
# double same action, treat this event as a NOOP.
|
# double same action, treat this event as a NOOP.
|
||||||
defer.returnValue({})
|
defer.returnValue({})
|
||||||
@ -445,8 +515,7 @@ class RoomMemberHandler(BaseRoomHandler):
|
|||||||
host = target_user.domain
|
host = target_user.domain
|
||||||
destinations.append(host)
|
destinations.append(host)
|
||||||
|
|
||||||
# If we are joining a remote HS, include that.
|
# Always include target domain
|
||||||
if membership == Membership.JOIN:
|
|
||||||
host = target_user.domain
|
host = target_user.domain
|
||||||
destinations.append(host)
|
destinations.append(host)
|
||||||
|
|
||||||
|
@ -19,6 +19,10 @@ from synapse.api.events.room import (
|
|||||||
RoomMemberEvent, RoomTopicEvent, FeedbackEvent,
|
RoomMemberEvent, RoomTopicEvent, FeedbackEvent,
|
||||||
# RoomConfigEvent,
|
# RoomConfigEvent,
|
||||||
RoomNameEvent,
|
RoomNameEvent,
|
||||||
|
RoomJoinRulesEvent,
|
||||||
|
RoomPowerLevelsEvent,
|
||||||
|
RoomAddStateLevelEvent,
|
||||||
|
RoomSendEventLevelEvent,
|
||||||
)
|
)
|
||||||
|
|
||||||
from synapse.util.logutils import log_function
|
from synapse.util.logutils import log_function
|
||||||
@ -124,12 +128,18 @@ class DataStore(RoomMemberStore, RoomStore,
|
|||||||
self._store_room_member_txn(txn, event)
|
self._store_room_member_txn(txn, event)
|
||||||
elif event.type == FeedbackEvent.TYPE:
|
elif event.type == FeedbackEvent.TYPE:
|
||||||
self._store_feedback_txn(txn, event)
|
self._store_feedback_txn(txn, event)
|
||||||
# elif event.type == RoomConfigEvent.TYPE:
|
|
||||||
# self._store_room_config_txn(txn, event)
|
|
||||||
elif event.type == RoomNameEvent.TYPE:
|
elif event.type == RoomNameEvent.TYPE:
|
||||||
self._store_room_name_txn(txn, event)
|
self._store_room_name_txn(txn, event)
|
||||||
elif event.type == RoomTopicEvent.TYPE:
|
elif event.type == RoomTopicEvent.TYPE:
|
||||||
self._store_room_topic_txn(txn, event)
|
self._store_room_topic_txn(txn, event)
|
||||||
|
elif event.type == RoomJoinRulesEvent.TYPE:
|
||||||
|
self._store_join_rule(txn, event)
|
||||||
|
elif event.type == RoomPowerLevelsEvent.TYPE:
|
||||||
|
self._store_power_levels(txn, event)
|
||||||
|
elif event.type == RoomAddStateLevelEvent.TYPE:
|
||||||
|
self._store_add_state_level(txn, event)
|
||||||
|
elif event.type == RoomSendEventLevelEvent.TYPE:
|
||||||
|
self._store_send_event_level(txn, event)
|
||||||
|
|
||||||
vals = {
|
vals = {
|
||||||
"topological_ordering": event.depth,
|
"topological_ordering": event.depth,
|
||||||
@ -223,7 +233,6 @@ class DataStore(RoomMemberStore, RoomStore,
|
|||||||
|
|
||||||
defer.returnValue(self.min_token)
|
defer.returnValue(self.min_token)
|
||||||
|
|
||||||
|
|
||||||
def snapshot_room(self, room_id, user_id, state_type=None, state_key=None):
|
def snapshot_room(self, room_id, user_id, state_type=None, state_key=None):
|
||||||
"""Snapshot the room for an update by a user
|
"""Snapshot the room for an update by a user
|
||||||
Args:
|
Args:
|
||||||
|
@ -27,6 +27,9 @@ import logging
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
OpsLevel = collections.namedtuple("OpsLevel", ("ban_level", "kick_level"))
|
||||||
|
|
||||||
|
|
||||||
class RoomStore(SQLBaseStore):
|
class RoomStore(SQLBaseStore):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@ -129,6 +132,98 @@ class RoomStore(SQLBaseStore):
|
|||||||
|
|
||||||
defer.returnValue(ret)
|
defer.returnValue(ret)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_room_join_rule(self, room_id):
|
||||||
|
sql = (
|
||||||
|
"SELECT join_rule FROM room_join_rules as r "
|
||||||
|
"INNER JOIN current_state_events as c "
|
||||||
|
"ON r.event_id = c.event_id "
|
||||||
|
"WHERE c.room_id = ? "
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = yield self._execute(None, sql, room_id)
|
||||||
|
|
||||||
|
if len(rows) == 1:
|
||||||
|
defer.returnValue(rows[0][0])
|
||||||
|
else:
|
||||||
|
defer.returnValue(None)
|
||||||
|
|
||||||
|
def get_power_level(self, room_id, user_id):
|
||||||
|
return self._db_pool.runInteraction(
|
||||||
|
self._get_power_level,
|
||||||
|
room_id, user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_power_level(self, txn, room_id, user_id):
|
||||||
|
sql = (
|
||||||
|
"SELECT level FROM room_power_levels as r "
|
||||||
|
"INNER JOIN current_state_events as c "
|
||||||
|
"ON r.event_id = c.event_id "
|
||||||
|
"WHERE c.room_id = ? AND r.user_id = ? "
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = txn.execute(sql, (room_id, user_id,)).fetchall()
|
||||||
|
|
||||||
|
if len(rows) == 1:
|
||||||
|
return rows[0][0]
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"SELECT level FROM room_default_levels as r "
|
||||||
|
"INNER JOIN current_state_events as c "
|
||||||
|
"ON r.event_id = c.event_id "
|
||||||
|
"WHERE c.room_id = ? "
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = txn.execute(sql, (room_id,)).fetchall()
|
||||||
|
|
||||||
|
if len(rows) == 1:
|
||||||
|
return rows[0][0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_ops_levels(self, room_id):
|
||||||
|
return self._db_pool.runInteraction(
|
||||||
|
self._get_ops_levels,
|
||||||
|
room_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_ops_levels(self, txn, room_id):
|
||||||
|
sql = (
|
||||||
|
"SELECT ban_level, kick_level FROM room_ops_levels as r "
|
||||||
|
"INNER JOIN current_state_events as c "
|
||||||
|
"ON r.event_id = c.event_id "
|
||||||
|
"WHERE c.room_id = ? "
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = txn.execute(sql, (room_id,)).fetchall()
|
||||||
|
|
||||||
|
if len(rows) == 1:
|
||||||
|
return OpsLevel(rows[0][0], rows[0][1])
|
||||||
|
else:
|
||||||
|
return OpsLevel(None, None)
|
||||||
|
|
||||||
|
def get_add_state_level(self, room_id):
|
||||||
|
return self._get_level_from_table("room_add_state_levels", room_id)
|
||||||
|
|
||||||
|
def get_send_event_level(self, room_id):
|
||||||
|
return self._get_level_from_table("room_send_event_levels", room_id)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def _get_level_from_table(self, table, room_id):
|
||||||
|
sql = (
|
||||||
|
"SELECT level FROM %(table)s as r "
|
||||||
|
"INNER JOIN current_state_events as c "
|
||||||
|
"ON r.event_id = c.event_id "
|
||||||
|
"WHERE c.room_id = ? "
|
||||||
|
) % {"table": table}
|
||||||
|
|
||||||
|
rows = yield self._execute(None, sql, room_id)
|
||||||
|
|
||||||
|
if len(rows) == 1:
|
||||||
|
defer.returnValue(rows[0][0])
|
||||||
|
else:
|
||||||
|
defer.returnValue(None)
|
||||||
|
|
||||||
def _store_room_topic_txn(self, txn, event):
|
def _store_room_topic_txn(self, txn, event):
|
||||||
self._simple_insert_txn(
|
self._simple_insert_txn(
|
||||||
txn,
|
txn,
|
||||||
@ -151,6 +246,92 @@ class RoomStore(SQLBaseStore):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _store_join_rule(self, txn, event):
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
"room_join_rules",
|
||||||
|
{
|
||||||
|
"event_id": event.event_id,
|
||||||
|
"room_id": event.room_id,
|
||||||
|
"join_rule": event.content["join_rule"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _store_power_levels(self, txn, event):
|
||||||
|
for user_id, level in event.content.items():
|
||||||
|
if user_id == "default":
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
"room_default_levels",
|
||||||
|
{
|
||||||
|
"event_id": event.event_id,
|
||||||
|
"room_id": event.room_id,
|
||||||
|
"level": level,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
"room_power_levels",
|
||||||
|
{
|
||||||
|
"event_id": event.event_id,
|
||||||
|
"room_id": event.room_id,
|
||||||
|
"user_id": user_id,
|
||||||
|
"level": level
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _store_default_level(self, txn, event):
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
"room_default_levels",
|
||||||
|
{
|
||||||
|
"event_id": event.event_id,
|
||||||
|
"room_id": event.room_id,
|
||||||
|
"level": event.content["default_level"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _store_add_state_level(self, txn, event):
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
"room_add_state_levels",
|
||||||
|
{
|
||||||
|
"event_id": event.event_id,
|
||||||
|
"room_id": event.room_id,
|
||||||
|
"level": event.content["level"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _store_send_event_level(self, txn, event):
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
"room_send_event_levels",
|
||||||
|
{
|
||||||
|
"event_id": event.event_id,
|
||||||
|
"room_id": event.room_id,
|
||||||
|
"level": event.content["level"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _store_ops_level(self, txn, event):
|
||||||
|
content = {
|
||||||
|
"event_id": event.event_id,
|
||||||
|
"room_id": event.room_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if "kick_level" in event.content:
|
||||||
|
content["kick_level"] = event.content["kick_level"]
|
||||||
|
|
||||||
|
if "ban_level" in event.content:
|
||||||
|
content["ban_level"] = event.content["ban_level"]
|
||||||
|
|
||||||
|
self._simple_insert_txn(
|
||||||
|
txn,
|
||||||
|
"room_send_event_levels",
|
||||||
|
content,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RoomsTable(Table):
|
class RoomsTable(Table):
|
||||||
table_name = "rooms"
|
table_name = "rooms"
|
||||||
|
@ -96,8 +96,71 @@ CREATE TABLE IF NOT EXISTS rooms(
|
|||||||
creator TEXT
|
creator TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS room_join_rules(
|
||||||
|
event_id TEXT NOT NULL,
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
join_rule TEXT NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_join_rules_event_id ON room_join_rules(event_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_join_rules_room_id ON room_join_rules(room_id);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS room_power_levels(
|
||||||
|
event_id TEXT NOT NULL,
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
level INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_power_levels_event_id ON room_power_levels(event_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_power_levels_room_id ON room_power_levels(room_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_power_levels_room_user ON room_power_levels(room_id, user_id);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS room_default_levels(
|
||||||
|
event_id TEXT NOT NULL,
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
level INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS room_default_levels_event_id ON room_default_levels(event_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_default_levels_room_id ON room_default_levels(room_id);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS room_add_state_levels(
|
||||||
|
event_id TEXT NOT NULL,
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
level INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS room_add_state_levels_event_id ON room_add_state_levels(event_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_add_state_levels_room_id ON room_add_state_levels(room_id);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS room_send_event_levels(
|
||||||
|
event_id TEXT NOT NULL,
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
level INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS room_send_event_levels_event_id ON room_send_event_levels(event_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_send_event_levels_room_id ON room_send_event_levels(room_id);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS room_ops_levels(
|
||||||
|
event_id TEXT NOT NULL,
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
ban_level INTEGER,
|
||||||
|
kick_level INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS room_ops_levels_event_id ON room_ops_levels(event_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS room_ops_levels_room_id ON room_ops_levels(room_id);
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS room_hosts(
|
CREATE TABLE IF NOT EXISTS room_hosts(
|
||||||
room_id TEXT NOT NULL,
|
room_id TEXT NOT NULL,
|
||||||
host TEXT NOT NULL,
|
host TEXT NOT NULL,
|
||||||
CONSTRAINT room_hosts_uniq UNIQUE (room_id, host) ON CONFLICT IGNORE
|
CONSTRAINT room_hosts_uniq UNIQUE (room_id, host) ON CONFLICT IGNORE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS room_hosts_room_id ON room_hosts (room_id);
|
||||||
|
@ -330,6 +330,8 @@ class RoomCreationTest(unittest.TestCase):
|
|||||||
datastore=NonCallableMock(spec_set=[
|
datastore=NonCallableMock(spec_set=[
|
||||||
"store_room",
|
"store_room",
|
||||||
"snapshot_room",
|
"snapshot_room",
|
||||||
|
"persist_event",
|
||||||
|
"get_joined_hosts_for_room",
|
||||||
]),
|
]),
|
||||||
http_client=NonCallableMock(spec_set=[]),
|
http_client=NonCallableMock(spec_set=[]),
|
||||||
notifier=NonCallableMock(spec_set=["on_new_room_event"]),
|
notifier=NonCallableMock(spec_set=["on_new_room_event"]),
|
||||||
@ -362,6 +364,10 @@ class RoomCreationTest(unittest.TestCase):
|
|||||||
])
|
])
|
||||||
self.room_member_handler = self.handlers.room_member_handler
|
self.room_member_handler = self.handlers.room_member_handler
|
||||||
|
|
||||||
|
def hosts(room):
|
||||||
|
return defer.succeed([])
|
||||||
|
self.datastore.get_joined_hosts_for_room.side_effect = hosts
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_room_creation(self):
|
def test_room_creation(self):
|
||||||
user_id = "@foo:red"
|
user_id = "@foo:red"
|
||||||
@ -385,9 +391,3 @@ class RoomCreationTest(unittest.TestCase):
|
|||||||
self.assertTrue(self.state_handler.handle_new_event.called)
|
self.assertTrue(self.state_handler.handle_new_event.called)
|
||||||
|
|
||||||
self.assertTrue(self.federation.handle_new_event.called)
|
self.assertTrue(self.federation.handle_new_event.called)
|
||||||
config_event = self.federation.handle_new_event.call_args[0][0]
|
|
||||||
|
|
||||||
self.assertEquals(RoomConfigEvent.TYPE, config_event.type)
|
|
||||||
self.assertEquals(room_id, config_event.room_id)
|
|
||||||
self.assertEquals(user_id, config_event.user_id)
|
|
||||||
self.assertEquals(config, config_event.content)
|
|
||||||
|
@ -234,6 +234,20 @@ class MemoryDataStore(object):
|
|||||||
def get_room_events_max_id(self):
|
def get_room_events_max_id(self):
|
||||||
return 0 # TODO (erikj)
|
return 0 # TODO (erikj)
|
||||||
|
|
||||||
|
def get_send_event_level(self, room_id):
|
||||||
|
return defer.succeed(0)
|
||||||
|
|
||||||
|
def get_power_level(self, room_id, user_id):
|
||||||
|
return defer.succeed(0)
|
||||||
|
|
||||||
|
def get_add_state_level(self, room_id):
|
||||||
|
return defer.succeed(0)
|
||||||
|
|
||||||
|
def get_room_join_rule(self, room_id):
|
||||||
|
# TODO (erikj): This should be configurable
|
||||||
|
return defer.succeed("invite")
|
||||||
|
|
||||||
|
|
||||||
def _format_call(args, kwargs):
|
def _format_call(args, kwargs):
|
||||||
return ", ".join(
|
return ", ".join(
|
||||||
["%r" % (a) for a in args] +
|
["%r" % (a) for a in args] +
|
||||||
|
Loading…
Reference in New Issue
Block a user