diff --git a/README.rst b/README.rst
index 291a308b64..297e72f1ae 100644
--- a/README.rst
+++ b/README.rst
@@ -258,6 +258,14 @@ During setup of Synapse you need to call python2.7 directly again::
...substituting your host and domain name as appropriate.
+FreeBSD
+-------
+
+Synapse can be installed via FreeBSD Ports or Packages:
+
+ - Ports: ``cd /usr/ports/net/py-matrix-synapse && make install clean``
+ - Packages: ``pkg install py27-matrix-synapse``
+
Windows Install
---------------
Synapse can be installed on Cygwin. It requires the following Cygwin packages:
diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 876869bb74..e36313e2fb 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -528,6 +528,11 @@ class Auth(object):
403,
"Application service cannot masquerade as this user."
)
+ if not (yield self.store.get_user_by_id(user_id)):
+ raise AuthError(
+ 403,
+ "Application service has not registered this user"
+ )
if not user_id:
raise KeyError
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 48a07e4e35..3a26f99a8b 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -880,28 +880,39 @@ class RoomContextHandler(BaseHandler):
(excluding state).
Returns:
- dict
+ dict, or None if the event isn't found
"""
before_limit = math.floor(limit/2.)
after_limit = limit - before_limit
now_token = yield self.hs.get_event_sources().get_current_token()
+ def filter_evts(events):
+ return self._filter_events_for_client(
+ user.to_string(),
+ events,
+ is_guest=is_guest)
+
+ event = yield self.store.get_event(event_id, get_prev_content=True,
+ allow_none=True)
+ if not event:
+ defer.returnValue(None)
+ return
+
+ filtered = yield(filter_evts([event]))
+ if not filtered:
+ raise AuthError(
+ 403,
+ "You don't have permission to access that event."
+ )
+
results = yield self.store.get_events_around(
room_id, event_id, before_limit, after_limit
)
- results["events_before"] = yield self._filter_events_for_client(
- user.to_string(),
- results["events_before"],
- is_guest=is_guest,
- )
-
- results["events_after"] = yield self._filter_events_for_client(
- user.to_string(),
- results["events_after"],
- is_guest=is_guest,
- )
+ results["events_before"] = yield filter_evts(results["events_before"])
+ results["events_after"] = yield filter_evts(results["events_after"])
+ results["event"] = event
if results["events_after"]:
last_event_id = results["events_after"][-1].event_id
diff --git a/synapse/rest/client/v1/push_rule.py b/synapse/rest/client/v1/push_rule.py
index df53824d2d..0cbd9fe08a 100644
--- a/synapse/rest/client/v1/push_rule.py
+++ b/synapse/rest/client/v1/push_rule.py
@@ -51,7 +51,7 @@ class PushRuleRestServlet(ClientV1RestServlet):
content = _parse_json(request)
if 'attr' in spec:
- self.set_rule_attr(requester.user, spec, content)
+ self.set_rule_attr(requester.user.to_string(), spec, content)
defer.returnValue((200, {}))
try:
diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py
index 7496b26735..8b1b2b852d 100644
--- a/synapse/rest/client/v1/room.py
+++ b/synapse/rest/client/v1/room.py
@@ -414,10 +414,16 @@ class RoomEventContext(ClientV1RestServlet):
requester.is_guest,
)
+ if not results:
+ raise SynapseError(
+ 404, "Event not found.", errcode=Codes.NOT_FOUND
+ )
+
time_now = self.clock.time_msec()
results["events_before"] = [
serialize_event(event, time_now) for event in results["events_before"]
]
+ results["event"] = serialize_event(results["event"], time_now)
results["events_after"] = [
serialize_event(event, time_now) for event in results["events_after"]
]
diff --git a/synapse/storage/appservice.py b/synapse/storage/appservice.py
index eab58d9ce9..b5aa55c0a3 100644
--- a/synapse/storage/appservice.py
+++ b/synapse/storage/appservice.py
@@ -15,12 +15,12 @@
import logging
import urllib
import yaml
-from simplejson import JSONDecodeError
import simplejson as json
from twisted.internet import defer
from synapse.api.constants import Membership
from synapse.appservice import ApplicationService, AppServiceTransaction
+from synapse.config._base import ConfigError
from synapse.storage.roommember import RoomsForUser
from synapse.types import UserID
from ._base import SQLBaseStore
@@ -144,66 +144,9 @@ class ApplicationServiceStore(SQLBaseStore):
return rooms_for_user_matching_user_id
- def _parse_services_dict(self, results):
- # SQL results in the form:
- # [
- # {
- # 'regex': "something",
- # 'url': "something",
- # 'namespace': enum,
- # 'as_id': 0,
- # 'token': "something",
- # 'hs_token': "otherthing",
- # 'id': 0
- # }
- # ]
- services = {}
- for res in results:
- as_token = res["token"]
- if as_token is None:
- continue
- if as_token not in services:
- # add the service
- services[as_token] = {
- "id": res["id"],
- "url": res["url"],
- "token": as_token,
- "hs_token": res["hs_token"],
- "sender": res["sender"],
- "namespaces": {
- ApplicationService.NS_USERS: [],
- ApplicationService.NS_ALIASES: [],
- ApplicationService.NS_ROOMS: []
- }
- }
- # add the namespace regex if one exists
- ns_int = res["namespace"]
- if ns_int is None:
- continue
- try:
- services[as_token]["namespaces"][
- ApplicationService.NS_LIST[ns_int]].append(
- json.loads(res["regex"])
- )
- except IndexError:
- logger.error("Bad namespace enum '%s'. %s", ns_int, res)
- except JSONDecodeError:
- logger.error("Bad regex object '%s'", res["regex"])
-
- service_list = []
- for service in services.values():
- service_list.append(ApplicationService(
- token=service["token"],
- url=service["url"],
- namespaces=service["namespaces"],
- hs_token=service["hs_token"],
- sender=service["sender"],
- id=service["id"]
- ))
- return service_list
-
def _load_appservice(self, as_info):
required_string_fields = [
+ # TODO: Add id here when it's stable to release
"url", "as_token", "hs_token", "sender_localpart"
]
for field in required_string_fields:
@@ -245,7 +188,7 @@ class ApplicationServiceStore(SQLBaseStore):
namespaces=as_info["namespaces"],
hs_token=as_info["hs_token"],
sender=user_id,
- id=as_info["as_token"] # the token is the only unique thing here
+ id=as_info["id"] if "id" in as_info else as_info["as_token"],
)
def _populate_appservice_cache(self, config_files):
@@ -256,15 +199,38 @@ class ApplicationServiceStore(SQLBaseStore):
)
return
+ # Dicts of value -> filename
+ seen_as_tokens = {}
+ seen_ids = {}
+
for config_file in config_files:
try:
with open(config_file, 'r') as f:
appservice = self._load_appservice(yaml.load(f))
+ if appservice.id in seen_ids:
+ raise ConfigError(
+ "Cannot reuse ID across application services: "
+ "%s (files: %s, %s)" % (
+ appservice.id, config_file, seen_ids[appservice.id],
+ )
+ )
+ seen_ids[appservice.id] = config_file
+ if appservice.token in seen_as_tokens:
+ raise ConfigError(
+ "Cannot reuse as_token across application services: "
+ "%s (files: %s, %s)" % (
+ appservice.token,
+ config_file,
+ seen_as_tokens[appservice.token],
+ )
+ )
+ seen_as_tokens[appservice.token] = config_file
logger.info("Loaded application service: %s", appservice)
self.services_cache.append(appservice)
except Exception as e:
logger.error("Failed to load appservice from '%s'", config_file)
logger.exception(e)
+ raise
class ApplicationServiceTransactionStore(SQLBaseStore):
diff --git a/tests/appservice/test_appservice.py b/tests/appservice/test_appservice.py
index 191c420c4d..ef48bbc296 100644
--- a/tests/appservice/test_appservice.py
+++ b/tests/appservice/test_appservice.py
@@ -29,6 +29,7 @@ class ApplicationServiceTestCase(unittest.TestCase):
def setUp(self):
self.service = ApplicationService(
+ id="unique_identifier",
url="some_url",
token="some_token",
namespaces={
diff --git a/tests/storage/test_appservice.py b/tests/storage/test_appservice.py
index a5a464640f..5abecdf6e0 100644
--- a/tests/storage/test_appservice.py
+++ b/tests/storage/test_appservice.py
@@ -12,12 +12,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+import tempfile
+from synapse.config._base import ConfigError
from tests import unittest
from twisted.internet import defer
from tests.utils import setup_test_homeserver
from synapse.appservice import ApplicationService, ApplicationServiceState
-from synapse.server import HomeServer
from synapse.storage.appservice import (
ApplicationServiceStore, ApplicationServiceTransactionStore
)
@@ -26,7 +27,6 @@ import json
import os
import yaml
from mock import Mock
-from tests.utils import SQLiteMemoryDbPool, MockClock
class ApplicationServiceStoreTestCase(unittest.TestCase):
@@ -41,9 +41,16 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
self.as_token = "token1"
self.as_url = "some_url"
- self._add_appservice(self.as_token, self.as_url, "some_hs_token", "bob")
- self._add_appservice("token2", "some_url", "some_hs_token", "bob")
- self._add_appservice("token3", "some_url", "some_hs_token", "bob")
+ self.as_id = "as1"
+ self._add_appservice(
+ self.as_token,
+ self.as_id,
+ self.as_url,
+ "some_hs_token",
+ "bob"
+ )
+ self._add_appservice("token2", "as2", "some_url", "some_hs_token", "bob")
+ self._add_appservice("token3", "as3", "some_url", "some_hs_token", "bob")
# must be done after inserts
self.store = ApplicationServiceStore(hs)
@@ -55,9 +62,9 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
except:
pass
- def _add_appservice(self, as_token, url, hs_token, sender):
+ def _add_appservice(self, as_token, id, url, hs_token, sender):
as_yaml = dict(url=url, as_token=as_token, hs_token=hs_token,
- sender_localpart=sender, namespaces={})
+ id=id, sender_localpart=sender, namespaces={})
# use the token as the filename
with open(as_token, 'w') as outfile:
outfile.write(yaml.dump(as_yaml))
@@ -74,6 +81,7 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
self.as_token
)
self.assertEquals(stored_service.token, self.as_token)
+ self.assertEquals(stored_service.id, self.as_id)
self.assertEquals(stored_service.url, self.as_url)
self.assertEquals(
stored_service.namespaces[ApplicationService.NS_ALIASES],
@@ -110,34 +118,34 @@ class ApplicationServiceTransactionStoreTestCase(unittest.TestCase):
{
"token": "token1",
"url": "https://matrix-as.org",
- "id": "token1"
+ "id": "id_1"
},
{
"token": "alpha_tok",
"url": "https://alpha.com",
- "id": "alpha_tok"
+ "id": "id_alpha"
},
{
"token": "beta_tok",
"url": "https://beta.com",
- "id": "beta_tok"
+ "id": "id_beta"
},
{
- "token": "delta_tok",
- "url": "https://delta.com",
- "id": "delta_tok"
+ "token": "gamma_tok",
+ "url": "https://gamma.com",
+ "id": "id_gamma"
},
]
for s in self.as_list:
- yield self._add_service(s["url"], s["token"])
+ yield self._add_service(s["url"], s["token"], s["id"])
self.as_yaml_files = []
self.store = TestTransactionStore(hs)
- def _add_service(self, url, as_token):
+ def _add_service(self, url, as_token, id):
as_yaml = dict(url=url, as_token=as_token, hs_token="something",
- sender_localpart="a_sender", namespaces={})
+ id=id, sender_localpart="a_sender", namespaces={})
# use the token as the filename
with open(as_token, 'w') as outfile:
outfile.write(yaml.dump(as_yaml))
@@ -405,3 +413,64 @@ class TestTransactionStore(ApplicationServiceTransactionStore,
def __init__(self, hs):
super(TestTransactionStore, self).__init__(hs)
+
+
+class ApplicationServiceStoreConfigTestCase(unittest.TestCase):
+
+ def _write_config(self, suffix, **kwargs):
+ vals = {
+ "id": "id" + suffix,
+ "url": "url" + suffix,
+ "as_token": "as_token" + suffix,
+ "hs_token": "hs_token" + suffix,
+ "sender_localpart": "sender_localpart" + suffix,
+ "namespaces": {},
+ }
+ vals.update(kwargs)
+
+ _, path = tempfile.mkstemp(prefix="as_config")
+ with open(path, "w") as f:
+ f.write(yaml.dump(vals))
+ return path
+
+ @defer.inlineCallbacks
+ def test_unique_works(self):
+ f1 = self._write_config(suffix="1")
+ f2 = self._write_config(suffix="2")
+
+ config = Mock(app_service_config_files=[f1, f2])
+ hs = yield setup_test_homeserver(config=config)
+
+ ApplicationServiceStore(hs)
+
+ @defer.inlineCallbacks
+ def test_duplicate_ids(self):
+ f1 = self._write_config(id="id", suffix="1")
+ f2 = self._write_config(id="id", suffix="2")
+
+ config = Mock(app_service_config_files=[f1, f2])
+ hs = yield setup_test_homeserver(config=config)
+
+ with self.assertRaises(ConfigError) as cm:
+ ApplicationServiceStore(hs)
+
+ e = cm.exception
+ self.assertIn(f1, e.message)
+ self.assertIn(f2, e.message)
+ self.assertIn("id", e.message)
+
+ @defer.inlineCallbacks
+ def test_duplicate_as_tokens(self):
+ f1 = self._write_config(as_token="as_token", suffix="1")
+ f2 = self._write_config(as_token="as_token", suffix="2")
+
+ config = Mock(app_service_config_files=[f1, f2])
+ hs = yield setup_test_homeserver(config=config)
+
+ with self.assertRaises(ConfigError) as cm:
+ ApplicationServiceStore(hs)
+
+ e = cm.exception
+ self.assertIn(f1, e.message)
+ self.assertIn(f2, e.message)
+ self.assertIn("as_token", e.message)
|