summary refs log tree commit diff
diff options
context:
space:
mode:
authorErik Johnston <erik@matrix.org>2014-11-10 18:24:43 +0000
committerErik Johnston <erik@matrix.org>2014-11-10 18:25:42 +0000
commita8e565eca8cbfcedbdfd812c98a6545c2fc31afd (patch)
treed55fba621e45804cd27829415b0e588b7e9fa51c
parentFix rest.test_events. Convert to use SQLiteMemoryDbPool (diff)
downloadsynapse-a8e565eca8cbfcedbdfd812c98a6545c2fc31afd.tar.xz
Add an EventValidator. Fix bugs in auth ++ storage
-rw-r--r--synapse/api/auth.py16
-rw-r--r--synapse/api/events/__init__.py61
-rw-r--r--synapse/handlers/profile.py11
-rw-r--r--synapse/rest/base.py2
-rw-r--r--synapse/rest/room.py13
-rw-r--r--synapse/server.py5
-rw-r--r--synapse/storage/_base.py2
-rw-r--r--synapse/storage/registration.py6
-rw-r--r--synapse/storage/room.py38
9 files changed, 64 insertions, 90 deletions
diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index a5c6964707..6c2d3db26e 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -70,6 +70,7 @@ class Auth(object):
                         logger.debug("Denying! %s", event)
                     return allowed
 
+                self.check_event_sender_in_room(event)
                 self._can_send_event(event)
 
                 if event.type == RoomPowerLevelsEvent.TYPE:
@@ -83,8 +84,10 @@ class Auth(object):
             else:
                 raise AuthError(500, "Unknown event: %s" % event)
         except AuthError as e:
-            logger.info("Event auth check failed on event %s with msg: %s",
-                        event, e.msg)
+            logger.info(
+                "Event auth check failed on event %s with msg: %s",
+                event, e.msg
+            )
             logger.info("Denying! %s", event)
             if raises:
                 raise e
@@ -277,7 +280,7 @@ class Auth(object):
                 default=[""]
             )[0]
             if user and access_token and ip_addr:
-                self.store.insert_client_ip(
+                yield self.store.insert_client_ip(
                     user=user,
                     access_token=access_token,
                     device_id=user_info["device_id"],
@@ -349,7 +352,8 @@ class Auth(object):
         if event.type == RoomMemberEvent.TYPE:
             e_type = event.content["membership"]
             if e_type in [Membership.JOIN, Membership.INVITE]:
-                auth_events.append(join_rule_event.event_id)
+                if join_rule_event:
+                    auth_events.append(join_rule_event.event_id)
 
                 if member_event and not is_public:
                     auth_events.append(member_event.event_id)
@@ -405,7 +409,9 @@ class Auth(object):
 
         if user_level < send_level:
             raise AuthError(
-                403, "You don't have permission to post that to the room"
+                403,
+                "You don't have permission to post that to the room. " +
+                "user_level (%d) < send_level (%d)" % (user_level, send_level)
             )
 
         return True
diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py
index f1e53f23ab..1d8bed2906 100644
--- a/synapse/api/events/__init__.py
+++ b/synapse/api/events/__init__.py
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from synapse.api.errors import SynapseError, Codes
 from synapse.util.jsonobject import JsonEncodedObject
 
 
@@ -118,66 +117,6 @@ class SynapseEvent(JsonEncodedObject):
         """
         raise NotImplementedError("get_content_template not implemented.")
 
-    def check_json(self, content, raises=True):
-        """Checks the given JSON content abides by the rules of the template.
-
-        Args:
-            content : A JSON object to check.
-            raises: True to raise a SynapseError if the check fails.
-        Returns:
-            True if the content passes the template. Returns False if the check
-            fails and raises=False.
-        Raises:
-            SynapseError if the check fails and raises=True.
-        """
-        # recursively call to inspect each layer
-        err_msg = self._check_json(content, self.get_content_template())
-        if err_msg:
-            if raises:
-                raise SynapseError(400, err_msg, Codes.BAD_JSON)
-            else:
-                return False
-        else:
-            return True
-
-    def _check_json(self, content, template):
-        """Check content and template matches.
-
-        If the template is a dict, each key in the dict will be validated with
-        the content, else it will just compare the types of content and
-        template. This basic type check is required because this function will
-        be recursively called and could be called with just strs or ints.
-
-        Args:
-            content: The content to validate.
-            template: The validation template.
-        Returns:
-            str: An error message if the validation fails, else None.
-        """
-        if type(content) != type(template):
-            return "Mismatched types: %s" % template
-
-        if type(template) == dict:
-            for key in template:
-                if key not in content:
-                    return "Missing %s key" % key
-
-                if type(content[key]) != type(template[key]):
-                    return "Key %s is of the wrong type (got %s, want %s)" % (
-                        key, type(content[key]), type(template[key]))
-
-                if type(content[key]) == dict:
-                    # we must go deeper
-                    msg = self._check_json(content[key], template[key])
-                    if msg:
-                        return msg
-                elif type(content[key]) == list:
-                    # make sure each item type in content matches the template
-                    for entry in content[key]:
-                        msg = self._check_json(entry, template[key][0])
-                        if msg:
-                            return msg
-
 
 class SynapseStateEvent(SynapseEvent):
 
diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py
index e47814483a..834b37f5f3 100644
--- a/synapse/handlers/profile.py
+++ b/synapse/handlers/profile.py
@@ -152,10 +152,13 @@ class ProfileHandler(BaseHandler):
         if not user.is_mine:
             defer.returnValue(None)
 
-        (displayname, avatar_url) = yield defer.gatherResults([
-            self.store.get_profile_displayname(user.localpart),
-            self.store.get_profile_avatar_url(user.localpart),
-        ])
+        (displayname, avatar_url) = yield defer.gatherResults(
+            [
+                self.store.get_profile_displayname(user.localpart),
+                self.store.get_profile_avatar_url(user.localpart),
+            ],
+            consumeErrors=True
+        )
 
         state["displayname"] = displayname
         state["avatar_url"] = avatar_url
diff --git a/synapse/rest/base.py b/synapse/rest/base.py
index dc784c1527..79fc4dfb84 100644
--- a/synapse/rest/base.py
+++ b/synapse/rest/base.py
@@ -67,6 +67,8 @@ class RestServlet(object):
         self.auth = hs.get_auth()
         self.txns = HttpTransactionStore()
 
+        self.validator = hs.get_event_validator()
+
     def register(self, http_server):
         """ Register this servlet with the given HTTP server. """
         if hasattr(self, "PATTERN"):
diff --git a/synapse/rest/room.py b/synapse/rest/room.py
index 5c9c9d3af4..05da0be090 100644
--- a/synapse/rest/room.py
+++ b/synapse/rest/room.py
@@ -154,6 +154,9 @@ class RoomStateEventRestServlet(RestServlet):
             user_id=user.to_string(),
             state_key=urllib.unquote(state_key)
             )
+
+        self.validator.validate(event)
+
         if event_type == RoomMemberEvent.TYPE:
             # membership events are special
             handler = self.handlers.room_member_handler
@@ -188,6 +191,8 @@ class RoomSendEventRestServlet(RestServlet):
             content=content
         )
 
+        self.validator.validate(event)
+
         msg_handler = self.handlers.message_handler
         yield msg_handler.send_message(event)
 
@@ -253,6 +258,9 @@ class JoinRoomAliasServlet(RestServlet):
                 user_id=user.to_string(),
                 state_key=user.to_string()
             )
+
+            self.validator.validate(event)
+
             handler = self.handlers.room_member_handler
             yield handler.change_membership(event)
             defer.returnValue((200, {}))
@@ -424,6 +432,9 @@ class RoomMembershipRestServlet(RestServlet):
             user_id=user.to_string(),
             state_key=state_key
         )
+
+        self.validator.validate(event)
+
         handler = self.handlers.room_member_handler
         yield handler.change_membership(event)
         defer.returnValue((200, {}))
@@ -461,6 +472,8 @@ class RoomRedactEventRestServlet(RestServlet):
             redacts=urllib.unquote(event_id),
         )
 
+        self.validator.validate(event)
+
         msg_handler = self.handlers.message_handler
         yield msg_handler.send_message(event)
 
diff --git a/synapse/server.py b/synapse/server.py
index d770b20b19..da0a44433a 100644
--- a/synapse/server.py
+++ b/synapse/server.py
@@ -22,6 +22,7 @@
 from synapse.federation import initialize_http_replication
 from synapse.api.events import serialize_event
 from synapse.api.events.factory import EventFactory
+from synapse.api.events.validator import EventValidator
 from synapse.notifier import Notifier
 from synapse.api.auth import Auth
 from synapse.handlers import Handlers
@@ -80,6 +81,7 @@ class BaseHomeServer(object):
         'event_sources',
         'ratelimiter',
         'keyring',
+        'event_validator',
     ]
 
     def __init__(self, hostname, **kwargs):
@@ -223,6 +225,9 @@ class HomeServer(BaseHomeServer):
     def build_keyring(self):
         return Keyring(self)
 
+    def build_event_validator(self):
+        return EventValidator(self)
+
     def register_servlets(self):
         """ Register all servlets associated with this HomeServer.
         """
diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py
index 2df64bdfeb..a1ee0318f6 100644
--- a/synapse/storage/_base.py
+++ b/synapse/storage/_base.py
@@ -212,7 +212,7 @@ class SQLBaseStore(object):
             retcol : string giving the name of the column to return
         """
         return self.runInteraction(
-            "_simple_select_one_onecol_txn",
+            "_simple_select_one_onecol",
             self._simple_select_one_onecol_txn,
             table, keyvalues, retcol, allow_none=allow_none,
         )
diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py
index a2ca6f9a69..1f89d77344 100644
--- a/synapse/storage/registration.py
+++ b/synapse/storage/registration.py
@@ -107,13 +107,17 @@ class RegistrationStore(SQLBaseStore):
             token
         )
 
+    @defer.inlineCallbacks
     def is_server_admin(self, user):
-        return self._simple_select_one_onecol(
+        res = yield self._simple_select_one_onecol(
             table="users",
             keyvalues={"name": user.to_string()},
             retcol="admin",
+            allow_none=True,
         )
 
+        defer.returnValue(res if res else False)
+
     def _query_for_auth(self, txn, token):
         sql = (
             "SELECT users.name, users.admin, access_tokens.device_id "
diff --git a/synapse/storage/room.py b/synapse/storage/room.py
index ca70506d28..cc0513b8d2 100644
--- a/synapse/storage/room.py
+++ b/synapse/storage/room.py
@@ -133,26 +133,28 @@ class RoomStore(SQLBaseStore):
         defer.returnValue(ret)
 
     def _store_room_topic_txn(self, txn, event):
-        self._simple_insert_txn(
-            txn,
-            "topics",
-            {
-                "event_id": event.event_id,
-                "room_id": event.room_id,
-                "topic": event.topic,
-            }
-        )
+        if hasattr(event, "topic"):
+            self._simple_insert_txn(
+                txn,
+                "topics",
+                {
+                    "event_id": event.event_id,
+                    "room_id": event.room_id,
+                    "topic": event.topic,
+                }
+            )
 
     def _store_room_name_txn(self, txn, event):
-        self._simple_insert_txn(
-            txn,
-            "room_names",
-            {
-                "event_id": event.event_id,
-                "room_id": event.room_id,
-                "name": event.name,
-            }
-        )
+        if hasattr(event, "name"):
+            self._simple_insert_txn(
+                txn,
+                "room_names",
+                {
+                    "event_id": event.event_id,
+                    "room_id": event.room_id,
+                    "name": event.name,
+                }
+            )
 
 
 class RoomsTable(Table):