Merge branch 'room_config' into develop

This commit is contained in:
Erik Johnston 2014-09-01 20:28:11 +01:00
commit a85612baf8
12 changed files with 579 additions and 48 deletions

View File

@ -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,16 +45,29 @@ 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)
return
self._check_joined_room(
member=snapshot.membership_state,
user_id=snapshot.user_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: else:
self._check_joined_room( yield self._can_send_event(event)
member=snapshot.membership_state,
user_id=snapshot.user_id, defer.returnValue(True)
room_id=snapshot.room_id,
)
defer.returnValue(True)
else: else:
raise AuthError(500, "Unknown event: %s" % event) raise AuthError(500, "Unknown event: %s" % event)
except AuthError as e: except AuthError as e:
@ -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 (
raise AuthError(403, "You are not invited to this room.") not caller or caller.membership not in
[Membership.INVITE, Membership.JOIN]
):
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"
)

View File

@ -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"

View File

@ -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)

View File

@ -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):

View File

@ -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 {}

View File

@ -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 = [

View File

@ -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,10 +515,9 @@ 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)
return self._on_new_room_event( return self._on_new_room_event(
event, snapshot, extra_destinations=destinations, event, snapshot, extra_destinations=destinations,

View File

@ -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
@ -123,13 +127,19 @@ class DataStore(RoomMemberStore, RoomStore,
if event.type == RoomMemberEvent.TYPE: if event.type == RoomMemberEvent.TYPE:
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:

View File

@ -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"

View File

@ -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);

View File

@ -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)

View File

@ -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] +