From 489a4cd1cf43f930e5d8fe27a08e02f975944c2d Mon Sep 17 00:00:00 2001
From: Mark Haines <mark.haines@matrix.org>
Date: Mon, 21 Dec 2015 21:10:41 +0000
Subject: Add top level filtering by room id

---
 synapse/api/filtering.py | 63 +++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 33 deletions(-)

(limited to 'synapse/api')

diff --git a/synapse/api/filtering.py b/synapse/api/filtering.py
index bc03d6c287..35faa53746 100644
--- a/synapse/api/filtering.py
+++ b/synapse/api/filtering.py
@@ -62,10 +62,24 @@ class Filtering(object):
                 self._check_definition(user_filter_json[key])
 
         if "room" in user_filter_json:
+            self._check_definition_room_lists(user_filter_json["room"])
             for key in room_level_definitions:
                 if key in user_filter_json["room"]:
                     self._check_definition(user_filter_json["room"][key])
 
+    def _check_definition_room_lists(self, definition):
+        """Check that "rooms" and "not_rooms" are lists of room ids if they
+        are present
+        """
+        # check rooms are valid room IDs
+        room_id_keys = ["rooms", "not_rooms"]
+        for key in room_id_keys:
+            if key in definition:
+                if type(definition[key]) != list:
+                    raise SynapseError(400, "Expected %s to be a list." % key)
+                for room_id in definition[key]:
+                    RoomID.from_string(room_id)
+
     def _check_definition(self, definition):
         """Check if the provided definition is valid.
 
@@ -85,14 +99,7 @@ class Filtering(object):
                 400, "Expected JSON object, not %s" % (definition,)
             )
 
-        # check rooms are valid room IDs
-        room_id_keys = ["rooms", "not_rooms"]
-        for key in room_id_keys:
-            if key in definition:
-                if type(definition[key]) != list:
-                    raise SynapseError(400, "Expected %s to be a list." % key)
-                for room_id in definition[key]:
-                    RoomID.from_string(room_id)
+        self._check_definition_room_lists(definition)
 
         # check senders are valid user IDs
         user_id_keys = ["senders", "not_senders"]
@@ -119,29 +126,19 @@ class FilterCollection(object):
     def __init__(self, filter_json):
         self.filter_json = filter_json
 
-        self.room_timeline_filter = Filter(
-            self.filter_json.get("room", {}).get("timeline", {})
-        )
+        room_filter_json = self.filter_json.get("room", {})
 
-        self.room_state_filter = Filter(
-            self.filter_json.get("room", {}).get("state", {})
-        )
+        self.room_filter = Filter({
+            k: v for k, v in room_filter_json.items()
+            if k in ("rooms", "not_rooms")
+        })
 
-        self.room_ephemeral_filter = Filter(
-            self.filter_json.get("room", {}).get("ephemeral", {})
-        )
-
-        self.room_account_data = Filter(
-            self.filter_json.get("room", {}).get("account_data", {})
-        )
-
-        self.presence_filter = Filter(
-            self.filter_json.get("presence", {})
-        )
-
-        self.account_data = Filter(
-            self.filter_json.get("account_data", {})
-        )
+        self.room_timeline_filter = Filter(room_filter_json.get("timeline", {}))
+        self.room_state_filter = Filter(room_filter_json.get("state", {}))
+        self.room_ephemeral_filter = Filter(room_filter_json.get("ephemeral", {}))
+        self.room_account_data = Filter(room_filter_json.get("account_data", {}))
+        self.presence_filter = Filter(self.filter_json.get("presence", {}))
+        self.account_data = Filter(self.filter_json.get("account_data", {}))
 
         self.include_leave = self.filter_json.get("room", {}).get(
             "include_leave", False
@@ -163,16 +160,16 @@ class FilterCollection(object):
         return self.account_data.filter(events)
 
     def filter_room_state(self, events):
-        return self.room_state_filter.filter(events)
+        return self.room_state_filter.filter(self.room_filter.filter(events))
 
     def filter_room_timeline(self, events):
-        return self.room_timeline_filter.filter(events)
+        return self.room_timeline_filter.filter(self.room_filter.filter(events))
 
     def filter_room_ephemeral(self, events):
-        return self.room_ephemeral_filter.filter(events)
+        return self.room_ephemeral_filter.filter(self.room_filter.filter(events))
 
     def filter_room_account_data(self, events):
-        return self.room_account_data.filter(events)
+        return self.room_account_data.filter(self.room_filter.filter(events))
 
 
 class Filter(object):
-- 
cgit 1.5.1


From 45a9e0ae0c9a4c55d4648802fefe96cc2933304f Mon Sep 17 00:00:00 2001
From: Mark Haines <mark.haines@matrix.org>
Date: Tue, 22 Dec 2015 10:25:46 +0000
Subject: Allow guest access if the user provides a list of rooms in the filter

---
 synapse/api/filtering.py             | 12 ++++++++++++
 synapse/handlers/sync.py             |  1 +
 synapse/rest/client/v2_alpha/sync.py | 10 +++++++++-
 3 files changed, 22 insertions(+), 1 deletion(-)

(limited to 'synapse/api')

diff --git a/synapse/api/filtering.py b/synapse/api/filtering.py
index 35faa53746..8c8c7b642e 100644
--- a/synapse/api/filtering.py
+++ b/synapse/api/filtering.py
@@ -144,6 +144,9 @@ class FilterCollection(object):
             "include_leave", False
         )
 
+    def list_rooms(self):
+        return self.room_filter.list_rooms()
+
     def timeline_limit(self):
         return self.room_timeline_filter.limit()
 
@@ -176,6 +179,15 @@ class Filter(object):
     def __init__(self, filter_json):
         self.filter_json = filter_json
 
+    def list_rooms(self):
+        """The list of room_id strings this filter restricts the output to
+        or None if the this filter doesn't list the room ids.
+        """
+        if "rooms" in self.filter_json:
+            return list(set(self.filter_json["rooms"]))
+        else:
+            return None
+
     def check(self, event):
         """Checks whether the filter matches the given event.
 
diff --git a/synapse/handlers/sync.py b/synapse/handlers/sync.py
index 7088c20cb4..75ef742328 100644
--- a/synapse/handlers/sync.py
+++ b/synapse/handlers/sync.py
@@ -29,6 +29,7 @@ logger = logging.getLogger(__name__)
 
 SyncConfig = collections.namedtuple("SyncConfig", [
     "user",
+    "is_guest",
     "filter",
 ])
 
diff --git a/synapse/rest/client/v2_alpha/sync.py b/synapse/rest/client/v2_alpha/sync.py
index 697df03dda..35a70ffad1 100644
--- a/synapse/rest/client/v2_alpha/sync.py
+++ b/synapse/rest/client/v2_alpha/sync.py
@@ -85,7 +85,9 @@ class SyncRestServlet(RestServlet):
 
     @defer.inlineCallbacks
     def on_GET(self, request):
-        user, token_id, _ = yield self.auth.get_user_by_req(request)
+        user, token_id, is_guest = yield self.auth.get_user_by_req(
+            request, allow_guest=True
+        )
 
         timeout = parse_integer(request, "timeout", default=0)
         since = parse_string(request, "since")
@@ -118,8 +120,14 @@ class SyncRestServlet(RestServlet):
             except:
                 filter = FilterCollection({})
 
+        if is_guest and filter.list_rooms() is None:
+            raise SynapseError(
+                400, "Guest users must provide a list of rooms in the filter"
+            )
+
         sync_config = SyncConfig(
             user=user,
+            is_guest=is_guest,
             filter=filter,
         )
 
-- 
cgit 1.5.1


From 0ee01383252cafd30ed92e3b655847d07cacee3a Mon Sep 17 00:00:00 2001
From: Mark Haines <mark.haines@matrix.org>
Date: Tue, 22 Dec 2015 15:49:32 +0000
Subject: Include the list of bad room ids in the error

---
 synapse/api/errors.py    | 16 ++++++++++++++++
 synapse/handlers/sync.py | 10 ++++++++--
 2 files changed, 24 insertions(+), 2 deletions(-)

(limited to 'synapse/api')

diff --git a/synapse/api/errors.py b/synapse/api/errors.py
index d4037b3d55..8bc7b9e6db 100644
--- a/synapse/api/errors.py
+++ b/synapse/api/errors.py
@@ -120,6 +120,22 @@ class AuthError(SynapseError):
         super(AuthError, self).__init__(*args, **kwargs)
 
 
+class GuestAccessError(AuthError):
+    """An error raised when a there is a problem with a guest user accessing
+    a room"""
+
+    def __init__(self, rooms, *args, **kwargs):
+        self.rooms = rooms
+        super(GuestAccessError, self).__init__(*args, **kwargs)
+
+    def error_dict(self):
+        return cs_error(
+            self.msg,
+            self.errcode,
+            rooms=self.rooms,
+        )
+
+
 class EventSizeError(SynapseError):
     """An error raised when an event is too big."""
 
diff --git a/synapse/handlers/sync.py b/synapse/handlers/sync.py
index 38c185cd54..feea407ea2 100644
--- a/synapse/handlers/sync.py
+++ b/synapse/handlers/sync.py
@@ -16,7 +16,7 @@
 from ._base import BaseHandler
 
 from synapse.api.constants import Membership, EventTypes
-from synapse.api.errors import AuthError
+from synapse.api.errors import GuestAccessError
 from synapse.util import unwrapFirstError
 
 from twisted.internet import defer
@@ -139,10 +139,16 @@ class SyncHandler(BaseHandler):
         """
 
         if sync_config.is_guest:
+            bad_rooms = []
             for room_id in sync_config.filter.list_rooms():
                 world_readable = yield self._is_world_readable(room_id)
                 if not world_readable:
-                    raise AuthError(403, "Guest access not allowed")
+                    bad_rooms.append(room_id)
+
+            if bad_rooms:
+                raise GuestAccessError(
+                    bad_rooms, 403, "Guest access not allowed"
+                )
 
         if timeout == 0 or since_token is None or full_state:
             # we are going to return immediately, so don't bother calling
-- 
cgit 1.5.1