diff --git a/synapse/api/auth.py b/synapse/api/auth.py index 0e8973e82..abd7d73b0 100644 --- a/synapse/api/auth.py +++ b/synapse/api/auth.py @@ -45,7 +45,10 @@ class Auth(object): """ try: if hasattr(event, "room_id"): + is_state = hasattr(event, "state_key") + if event.type == RoomMemberEvent.TYPE: + yield self._can_replace_state(event) allowed = yield self.is_membership_change_allowed(event) defer.returnValue(allowed) return @@ -56,10 +59,11 @@ class Auth(object): room_id=snapshot.room_id, ) - if hasattr(event, "state_key"): + 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) @@ -175,7 +179,7 @@ class Auth(object): else: ban_level = 5 # FIXME (erikj): What should we do here? - if ban_level < user_level: + if user_level < ban_level: raise AuthError(403, "You don't have permission to ban") else: raise AuthError(500, "Unknown membership %s" % membership) @@ -267,3 +271,35 @@ class Auth(object): ) 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" + ) diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py index bf8d288ac..9502f5df8 100644 --- a/synapse/api/events/__init__.py +++ b/synapse/api/events/__init__.py @@ -42,6 +42,7 @@ class SynapseEvent(JsonEncodedObject): "user_id", # sender/initiator "content", # HTTP body, JSON "state_key", + "required_power_level", ] internal_keys = [ @@ -52,6 +53,7 @@ class SynapseEvent(JsonEncodedObject): "destinations", "origin", "outlier", + "power_level", ] required_keys = [ diff --git a/synapse/federation/units.py b/synapse/federation/units.py index 2b2f11f36..b468f7054 100644 --- a/synapse/federation/units.py +++ b/synapse/federation/units.py @@ -68,6 +68,7 @@ class Pdu(JsonEncodedObject): "power_level", "prev_state_id", "prev_state_origin", + "required_power_level", ] internal_keys = [ diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 9262afb47..f33bec9cc 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -166,7 +166,7 @@ class RoomCreationHandler(BaseRoomHandler): power_levels_event = create( etype=RoomPowerLevelsEvent.TYPE, - **{creator.to_string(): 10} + **{creator.to_string(): 10, "default": 0} ) join_rule = JoinRules.PUBLIC if is_public else JoinRules.INVITE @@ -343,6 +343,16 @@ class RoomMemberHandler(BaseRoomHandler): if do_auth: 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: # double same action, treat this event as a NOOP. defer.returnValue({}) diff --git a/synapse/storage/room.py b/synapse/storage/room.py index 3b2d1a8ec..3ca07f435 100644 --- a/synapse/storage/room.py +++ b/synapse/storage/room.py @@ -165,8 +165,7 @@ class RoomStore(SQLBaseStore): rows = txn.execute(sql, (room_id, user_id,)).fetchall() if len(rows) == 1: - defer.returnValue(rows[0][0]) - return + return rows[0][0] sql = ( "SELECT level FROM room_default_levels as r " diff --git a/synapse/storage/schema/im.sql b/synapse/storage/schema/im.sql index 1de0d5906..dbefbbda3 100644 --- a/synapse/storage/schema/im.sql +++ b/synapse/storage/schema/im.sql @@ -150,7 +150,7 @@ CREATE TABLE IF NOT EXISTS room_ops_levels( event_id TEXT NOT NULL, room_id TEXT NOT NULL, ban_level INTEGER, - kick_level INTEGER, + kick_level INTEGER ); CREATE INDEX IF NOT EXISTS room_ops_levels_event_id ON room_ops_levels(event_id);