summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--synapse/api/auth.py221
-rw-r--r--synapse/api/events/__init__.py3
-rw-r--r--synapse/api/events/factory.py7
-rw-r--r--synapse/api/events/room.py22
-rw-r--r--synapse/api/events/utils.py23
-rw-r--r--synapse/handlers/room.py52
-rw-r--r--synapse/storage/__init__.py12
-rw-r--r--synapse/storage/room.py157
8 files changed, 102 insertions, 395 deletions
diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 9eb0491c97..462e97bd90 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -21,8 +21,8 @@ from synapse.api.constants import Membership, JoinRules
 from synapse.api.errors import AuthError, StoreError, Codes, SynapseError
 from synapse.api.events.room import (
     RoomMemberEvent, RoomPowerLevelsEvent, RoomRedactionEvent,
-    RoomJoinRulesEvent, RoomOpsPowerLevelsEvent, InviteJoinEvent,
-    RoomCreateEvent, RoomSendEventLevelEvent, RoomAddStateLevelEvent,
+    RoomJoinRulesEvent, InviteJoinEvent,
+    RoomCreateEvent,
 )
 from synapse.util.logutils import log_function
 
@@ -51,6 +51,7 @@ class Auth(object):
                 if event.old_state_events is None:
                     # Oh, we don't know what the state of the room was, so we
                     # are trusting that this is allowed (at least for now)
+                    logger.warn("Trusting event: %s", event.event_id)
                     return True
 
                 if hasattr(event, "outlier") and event.outlier is True:
@@ -64,7 +65,7 @@ class Auth(object):
                     return True
 
                 if event.type == RoomMemberEvent.TYPE:
-                    self._can_replace_state(event)
+                    self._can_send_event(event)
                     allowed = self.is_membership_change_allowed(event)
                     if allowed:
                         logger.debug("Allowing! %s", event)
@@ -72,16 +73,7 @@ class Auth(object):
                         logger.debug("Denying! %s", event)
                     return allowed
 
-                if not event.type == InviteJoinEvent.TYPE:
-                    self.check_event_sender_in_room(event)
-
-                if is_state:
-                    # TODO (erikj): This really only should be called for *new*
-                    # state
-                    self._can_add_state(event)
-                    self._can_replace_state(event)
-                else:
-                    self._can_send_event(event)
+                self._can_send_event(event)
 
                 if event.type == RoomPowerLevelsEvent.TYPE:
                     self._check_power_levels(event)
@@ -239,21 +231,21 @@ class Auth(object):
         power_level_event = event.old_state_events.get(key)
         level = None
         if power_level_event:
-            level = power_level_event.content.get(user_id)
+            level = power_level_event.content.get("users", {}).get(user_id)
             if not level:
-                level = power_level_event.content.get("default", 0)
+                level = power_level_event.content.get("users_default", 0)
 
         return level
 
     def _get_ops_level_from_event_state(self, event):
-        key = (RoomOpsPowerLevelsEvent.TYPE, "", )
-        ops_event = event.old_state_events.get(key)
+        key = (RoomPowerLevelsEvent.TYPE, "", )
+        power_level_event = event.old_state_events.get(key)
 
-        if ops_event:
+        if power_level_event:
             return (
-                ops_event.content.get("ban_level"),
-                ops_event.content.get("kick_level"),
-                ops_event.content.get("redact_level"),
+                power_level_event.content.get("ban", 50),
+                power_level_event.content.get("kick", 50),
+                power_level_event.content.get("redact", 50),
             )
         return None, None, None,
 
@@ -325,13 +317,22 @@ class Auth(object):
 
     @log_function
     def _can_send_event(self, event):
-        key = (RoomSendEventLevelEvent.TYPE, "", )
+        key = (RoomPowerLevelsEvent.TYPE, "", )
         send_level_event = event.old_state_events.get(key)
         send_level = None
         if send_level_event:
-            send_level = send_level_event.content.get(event.user_id)
+            send_level = send_level_event.content.get("events", {}).get(
+                event.type
+            )
             if not send_level:
-                send_level = send_level_event.content.get("level", 0)
+                if hasattr(event, "state_key"):
+                    send_level = send_level_event.content.get(
+                        "state_default", 50
+                    )
+                else:
+                    send_level = send_level_event.content.get(
+                        "events_default", 0
+                    )
 
         if send_level:
             send_level = int(send_level)
@@ -350,85 +351,21 @@ class Auth(object):
 
         if user_level < send_level:
             raise AuthError(
-                403, "You don't have permission to post to the room"
-            )
-
-        return True
-
-    def _can_add_state(self, event):
-        key = (RoomAddStateLevelEvent.TYPE, "", )
-        add_level_event = event.old_state_events.get(key)
-        add_level = None
-        if add_level_event:
-            add_level = add_level_event.content.get(event.user_id)
-            if not add_level:
-                add_level = add_level_event.content.get("level", 0)
-
-        if add_level:
-            add_level = int(add_level)
-        else:
-            add_level = 0
-
-        user_level = self._get_power_level_from_event_state(
-            event,
-            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"
+                403, "You don't have permission to post that to the room"
             )
 
         return True
 
-    def _can_replace_state(self, event):
-        user_level = self._get_power_level_from_event_state(
-            event,
-            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
-        )
-
-        key = (event.type, event.state_key, )
-        current_state = event.old_state_events.get(key)
-
-        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"
-                )
-
     def _check_redaction(self, event):
         user_level = self._get_power_level_from_event_state(
             event,
             event.user_id,
         )
 
-        if user_level:
-            user_level = int(user_level)
-        else:
-            user_level = 0
-
         _, _, redact_level = self._get_ops_level_from_event_state(
             event
         )
 
-        if not redact_level:
-            redact_level = 50
-
         if user_level < redact_level:
             raise AuthError(
                 403,
@@ -436,14 +373,9 @@ class Auth(object):
             )
 
     def _check_power_levels(self, event):
-        for k, v in event.content.items():
-            if k == "default":
-                continue
-
-            # FIXME (erikj): We don't want hsob_Ts in content.
-            if k == "hsob_ts":
-                continue
-
+        user_list = event.content.get("users", {})
+        # Validate users
+        for k, v in user_list.items():
             try:
                 self.hs.parse_userid(k)
             except:
@@ -459,72 +391,63 @@ class Auth(object):
 
         if not current_state:
             return
-        else:
-            current_state = current_state[0]
 
         user_level = self._get_power_level_from_event_state(
             event,
             event.user_id,
         )
 
-        if user_level:
-            user_level = int(user_level)
-        else:
-            user_level = 0
+       # Check other levels:
+        levels_to_check = [
+            ("users_default", []),
+            ("events_default", []),
+            ("ban", []),
+            ("redact", []),
+            ("kick", []),
+        ]
+
+        old_list = current_state.content.get("users")
+        for user in set(old_list.keys() + user_list.keys()):
+            levels_to_check.append(
+                (user, ["users"])
+            )
 
-        old_list = current_state.content
+        old_list = current_state.content.get("events")
+        new_list = event.content.get("events")
+        for ev_id in set(old_list.keys() + new_list.keys()):
+            levels_to_check.append(
+                (ev_id, ["events"])
+            )
 
-        # FIXME (erikj)
-        old_people = {k: v for k, v in old_list.items() if k.startswith("@")}
-        new_people = {
-            k: v for k, v in event.content.items()
-            if k.startswith("@")
-        }
+        old_state = current_state.content
+        new_state = event.content
 
-        removed = set(old_people.keys()) - set(new_people.keys())
-        added = set(new_people.keys()) - set(old_people.keys())
-        same = set(old_people.keys()) & set(new_people.keys())
+        for level_to_check, dir in levels_to_check:
+            old_loc = old_state
+            for d in dir:
+                old_loc = old_loc.get(d, {})
 
-        for r in removed:
-            if int(old_list[r]) > user_level:
-                raise AuthError(
-                    403,
-                    "You don't have permission to remove user: %s" % (r, )
-                )
+            new_loc = new_state
+            for d in dir:
+                new_loc = new_loc.get(d, {})
 
-        for n in added:
-            if int(event.content[n]) > user_level:
-                raise AuthError(
-                    403,
-                    "You don't have permission to add ops level greater "
-                    "than your own"
-                )
+            if level_to_check in old_loc:
+                old_level = int(old_loc[level_to_check])
+            else:
+                old_level = None
 
-        for s in same:
-            if int(event.content[s]) != int(old_list[s]):
-                if int(event.content[s]) > user_level:
-                    raise AuthError(
-                        403,
-                        "You don't have permission to add ops level greater "
-                        "than your own"
-                    )
+            if level_to_check in new_loc:
+                new_level = int(new_loc[level_to_check])
+            else:
+                new_level = None
 
-        if "default" in old_list:
-            old_default = int(old_list["default"])
+            if new_level is not None and old_level is not None:
+                if new_level == old_level:
+                    continue
 
-            if old_default > user_level:
+            if old_level > user_level or new_level > user_level:
                 raise AuthError(
                     403,
-                    "You don't have permission to add ops level greater than "
-                    "your own"
+                    "You don't have permission to add ops level greater "
+                    "than your own"
                 )
-
-            if "default" in event.content:
-                new_default = int(event.content["default"])
-
-                if new_default > user_level:
-                    raise AuthError(
-                        403,
-                        "You don't have permission to add ops level greater "
-                        "than your own"
-                    )
diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py
index 98a66144e7..84d3a98365 100644
--- a/synapse/api/events/__init__.py
+++ b/synapse/api/events/__init__.py
@@ -56,12 +56,12 @@ class SynapseEvent(JsonEncodedObject):
         "user_id",  # sender/initiator
         "content",  # HTTP body, JSON
         "state_key",
-        "required_power_level",
         "age_ts",
         "prev_content",
         "replaces_state",
         "redacted_because",
         "origin_server_ts",
+        "auth_chains",
     ]
 
     internal_keys = [
@@ -70,7 +70,6 @@ class SynapseEvent(JsonEncodedObject):
         "destinations",
         "origin",
         "outlier",
-        "power_level",
         "redacted",
         "prev_events",
         "hashes",
diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py
index 9134c82eff..a1ec708a81 100644
--- a/synapse/api/events/factory.py
+++ b/synapse/api/events/factory.py
@@ -16,8 +16,8 @@
 from synapse.api.events.room import (
     RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
     InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent,
-    RoomPowerLevelsEvent, RoomJoinRulesEvent, RoomOpsPowerLevelsEvent,
-    RoomCreateEvent, RoomAddStateLevelEvent, RoomSendEventLevelEvent,
+    RoomPowerLevelsEvent, RoomJoinRulesEvent,
+    RoomCreateEvent,
     RoomRedactionEvent,
 )
 
@@ -39,9 +39,6 @@ class EventFactory(object):
         RoomPowerLevelsEvent,
         RoomJoinRulesEvent,
         RoomCreateEvent,
-        RoomAddStateLevelEvent,
-        RoomSendEventLevelEvent,
-        RoomOpsPowerLevelsEvent,
         RoomRedactionEvent,
     ]
 
diff --git a/synapse/api/events/room.py b/synapse/api/events/room.py
index cd936074fc..25bc883706 100644
--- a/synapse/api/events/room.py
+++ b/synapse/api/events/room.py
@@ -153,28 +153,6 @@ class RoomPowerLevelsEvent(SynapseStateEvent):
     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 {}
-
-
 class RoomAliasesEvent(SynapseStateEvent):
     TYPE = "m.room.aliases"
 
diff --git a/synapse/api/events/utils.py b/synapse/api/events/utils.py
index 31601fd3a9..5fc79105b5 100644
--- a/synapse/api/events/utils.py
+++ b/synapse/api/events/utils.py
@@ -15,7 +15,6 @@
 
 from .room import (
     RoomMemberEvent, RoomJoinRulesEvent, RoomPowerLevelsEvent,
-    RoomAddStateLevelEvent, RoomSendEventLevelEvent, RoomOpsPowerLevelsEvent,
     RoomAliasesEvent, RoomCreateEvent,
 )
 
@@ -52,17 +51,17 @@ def _prune_event_or_pdu(event_type, event):
     elif event_type == RoomJoinRulesEvent.TYPE:
         add_fields("join_rule")
     elif event_type == RoomPowerLevelsEvent.TYPE:
-        # TODO: Actually check these are valid user_ids etc.
-        add_fields("default")
-        for k, v in event.content.items():
-            if k.startswith("@") and isinstance(v, (int, long)):
-                new_content[k] = v
-    elif event_type == RoomAddStateLevelEvent.TYPE:
-        add_fields("level")
-    elif event_type == RoomSendEventLevelEvent.TYPE:
-        add_fields("level")
-    elif event_type == RoomOpsPowerLevelsEvent.TYPE:
-        add_fields("kick_level", "ban_level", "redact_level")
+        add_fields(
+            "users",
+            "users_default",
+            "events",
+            "events_default",
+            "events_default",
+            "state_default",
+            "ban",
+            "kick",
+            "redact",
+        )
     elif event_type == RoomAliasesEvent.TYPE:
         add_fields("aliases")
 
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 55c893eb58..42a6c9f9bf 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -21,8 +21,7 @@ from synapse.api.constants import Membership, JoinRules
 from synapse.api.errors import StoreError, SynapseError
 from synapse.api.events.room import (
     RoomMemberEvent, RoomCreateEvent, RoomPowerLevelsEvent,
-    RoomJoinRulesEvent, RoomAddStateLevelEvent, RoomTopicEvent,
-    RoomSendEventLevelEvent, RoomOpsPowerLevelsEvent, RoomNameEvent,
+    RoomTopicEvent, RoomNameEvent, RoomJoinRulesEvent,
 )
 from synapse.util import stringutils
 from ._base import BaseHandler
@@ -139,7 +138,6 @@ class RoomCreationHandler(BaseHandler):
                 etype=RoomNameEvent.TYPE,
                 room_id=room_id,
                 user_id=user_id,
-                required_power_level=50,
                 content={"name": name},
             )
 
@@ -151,7 +149,6 @@ class RoomCreationHandler(BaseHandler):
                 etype=RoomTopicEvent.TYPE,
                 room_id=room_id,
                 user_id=user_id,
-                required_power_level=50,
                 content={"topic": topic},
             )
 
@@ -196,7 +193,6 @@ class RoomCreationHandler(BaseHandler):
         event_keys = {
             "room_id": room_id,
             "user_id": creator.to_string(),
-            "required_power_level": 100,
         }
 
         def create(etype, **content):
@@ -213,7 +209,21 @@ class RoomCreationHandler(BaseHandler):
 
         power_levels_event = self.event_factory.create_event(
             etype=RoomPowerLevelsEvent.TYPE,
-            content={creator.to_string(): 100, "default": 0},
+            content={
+                "users": {
+                    creator.to_string(): 100,
+                },
+                "users_default": 0,
+                "events": {
+                    RoomNameEvent.TYPE: 100,
+                    RoomPowerLevelsEvent.TYPE: 100,
+                },
+                "events_default": 0,
+                "state_default": 50,
+                "ban": 50,
+                "kick": 50,
+                "redact": 50
+            },
             **event_keys
         )
 
@@ -223,30 +233,10 @@ class RoomCreationHandler(BaseHandler):
             join_rule=join_rule,
         )
 
-        add_state_event = create(
-            etype=RoomAddStateLevelEvent.TYPE,
-            level=100,
-        )
-
-        send_event = create(
-            etype=RoomSendEventLevelEvent.TYPE,
-            level=0,
-        )
-
-        ops = create(
-            etype=RoomOpsPowerLevelsEvent.TYPE,
-            ban_level=50,
-            kick_level=50,
-            redact_level=50,
-        )
-
         return [
             creation_event,
             power_levels_event,
             join_rules_event,
-            add_state_event,
-            send_event,
-            ops,
         ]
 
 
@@ -388,16 +378,6 @@ class RoomMemberHandler(BaseHandler):
         else:
             # This is not a JOIN, so we can handle it normally.
 
-            # 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/__init__.py b/synapse/storage/__init__.py
index 2d62fc2ed0..2a1970914f 100644
--- a/synapse/storage/__init__.py
+++ b/synapse/storage/__init__.py
@@ -17,13 +17,9 @@ from twisted.internet import defer
 
 from synapse.api.events.room import (
     RoomMemberEvent, RoomTopicEvent, FeedbackEvent,
-#   RoomConfigEvent,
     RoomNameEvent,
     RoomJoinRulesEvent,
     RoomPowerLevelsEvent,
-    RoomAddStateLevelEvent,
-    RoomSendEventLevelEvent,
-    RoomOpsPowerLevelsEvent,
     RoomRedactionEvent,
 )
 
@@ -166,14 +162,6 @@ class DataStore(RoomMemberStore, RoomStore,
             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)
-        elif event.type == RoomOpsPowerLevelsEvent.TYPE:
-            self._store_ops_level(txn, event)
         elif event.type == RoomRedactionEvent.TYPE:
             self._store_redaction(txn, event)
 
diff --git a/synapse/storage/room.py b/synapse/storage/room.py
index 7e48ce9cc3..0c83c11ad3 100644
--- a/synapse/storage/room.py
+++ b/synapse/storage/room.py
@@ -148,85 +148,6 @@ class RoomStore(SQLBaseStore):
         else:
             defer.returnValue(None)
 
-    def get_power_level(self, room_id, user_id):
-        return self.runInteraction(
-            "get_power_level",
-            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.runInteraction(
-            "get_ops_levels",
-            self._get_ops_levels,
-            room_id,
-        )
-
-    def _get_ops_levels(self, txn, room_id):
-        sql = (
-            "SELECT ban_level, kick_level, redact_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], rows[0][2])
-        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):
         self._simple_insert_txn(
             txn,
@@ -260,84 +181,6 @@ class RoomStore(SQLBaseStore):
             },
         )
 
-    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"]
-
-        if "redact_level" in event.content:
-            content["redact_level"] = event.content["redact_level"]
-
-        self._simple_insert_txn(
-            txn,
-            "room_ops_levels",
-            content,
-        )
-
 
 class RoomsTable(Table):
     table_name = "rooms"