summary refs log tree commit diff
diff options
context:
space:
mode:
authorMark Haines <mark.haines@matrix.org>2017-01-04 13:16:13 +0000
committerMark Haines <mark.haines@matrix.org>2017-01-04 13:16:13 +0000
commitb6df1dc7ec92f39ae1de0dbab3bcfc273994c5bc (patch)
tree105ff6b7d7021760f6b6a41325bc2d3320dc95fa
parentMerge branch 'release-v0.18.6' into develop (diff)
downloadsynapse-markjh/event_auth.tar.xz
-rw-r--r--synapse/api/auth.py15
-rw-r--r--tests/api/generate_test_eventauth.py99
-rw-r--r--tests/api/test_eventauth.py268
3 files changed, 378 insertions, 4 deletions
diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index a99986714d..17ed35d082 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -83,8 +83,8 @@ class Auth(object):
             if not hasattr(event, "room_id"):
                 raise AuthError(500, "Event has no room_id: %s" % event)
 
+            sender_domain = get_domain_from_id(event.sender)
             if do_sig_check:
-                sender_domain = get_domain_from_id(event.sender)
                 event_id_domain = get_domain_from_id(event.event_id)
 
                 is_invite_via_3pid = (
@@ -130,9 +130,15 @@ class Auth(object):
                     "Room %r does not exist" % (event.room_id,)
                 )
 
+            if event.room_id != creation_event.room_id:
+                raise SynapseError(
+                    403, "Event has the wrong room_id: %r != %r" % (
+                        event.room_id, creation_event.room_id
+                    )
+                )
+
             creating_domain = get_domain_from_id(event.room_id)
-            originating_domain = get_domain_from_id(event.sender)
-            if creating_domain != originating_domain:
+            if creating_domain != sender_domain:
                 if not self.can_federate(event, auth_events):
                     raise AuthError(
                         403,
@@ -331,7 +337,8 @@ class Auth(object):
             create = auth_events.get(key)
             if create and event.prev_events[0][0] == create.event_id:
                 if create.content["creator"] == event.state_key:
-                    return True
+                    if event.state_key == event.sender:
+                        return True
 
         target_user_id = event.state_key
 
diff --git a/tests/api/generate_test_eventauth.py b/tests/api/generate_test_eventauth.py
new file mode 100644
index 0000000000..8181f674d2
--- /dev/null
+++ b/tests/api/generate_test_eventauth.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+# Copyright 2015 - 2016 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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 sys
+import json
+import os
+import pprint
+
+testcases = json.load(sys.stdin)
+
+w = sys.stdout.write
+
+w("""
+# -*- coding: utf-8 -*-
+# Generated by """ + os.path.basename(__file__) + """
+
+# Copyright 2015 - 2016 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+from synapse.api.auth import Auth
+from synapse.api.errors import SynapseError
+from tests import unittest
+from tests.utils import setup_test_homeserver
+from twisted.internet import defer
+from synapse.events import FrozenEvent
+
+
+class EventAuthTestCase(unittest.TestCase):
+
+    @defer.inlineCallbacks
+    def setUp(self):
+        self.hs = yield setup_test_homeserver(handlers=None)
+        self.auth = Auth(self.hs)
+""")
+
+indent = " " * 4
+for name, case in sorted(testcases.items()):
+    w("""
+    def test_""" + name[4:] + """(self):
+""")
+    auth_data = case.get("auth_events", {})
+    auth_events = {}
+    if "create" in auth_data:
+        auth_events[("m.room.create", "")] = auth_data["create"]
+    for user_id, member in auth_data.get("member", {}).items():
+        auth_events[("m.room.member", user_id)] = member
+
+    w(indent * 2 + "auth_events = (\n" + indent * 3)
+    data = pprint.pformat(auth_events, width=80 - len(indent * 3))
+    w(data.replace("\n", "\n" + indent * 3))
+    w("\n" + indent * 2 + ")\n")
+    w("""
+        auth_events = {k: FrozenEvent(v) for k, v in auth_events.items()}
+"""[1:])
+
+    w("\n" + indent * 2 + "# Allowed events\n")
+    for allowed in case.get("allowed", ()):
+        reason = allowed.pop("unsigned", {}).pop("allowed", None)
+        if reason is not None:
+            w(indent * 2 + "#Allowed: " + reason)
+        w(indent * 2 + "self.auth.check(FrozenEvent(\n" + indent * 3)
+        data = pprint.pformat(allowed, width=80 - len(indent * 3))
+        w(data.replace("\n", "\n" + indent * 3))
+        w("\n" + indent * 2 + "), auth_events=auth_events, do_sig_check=False)\n")
+
+    w("\n" + indent * 2 + "# Disallowed events\n")
+    for not_allowed in case.get("not_allowed", ()):
+        reason = not_allowed.pop("unsigned", {}).pop("not_allowed", None)
+        if reason is not None:
+            w(indent * 2 + "# " + reason + "\n")
+        w(indent * 2 + "self.assertRaises(SynapseError,")
+        w(" self.auth.check, FrozenEvent(\n" + indent * 3)
+        data = pprint.pformat(not_allowed, width=80 - len(indent * 3))
+        w(data.replace("\n", "\n" + indent * 3))
+        w("\n" + indent * 2 + "), auth_events=auth_events, do_sig_check=False)\n")
diff --git a/tests/api/test_eventauth.py b/tests/api/test_eventauth.py
new file mode 100644
index 0000000000..84d88d3fe9
--- /dev/null
+++ b/tests/api/test_eventauth.py
@@ -0,0 +1,268 @@
+
+# -*- coding: utf-8 -*-
+# Generated by generate_test_eventauth.py
+
+# Copyright 2015 - 2016 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+from synapse.api.auth import Auth
+from synapse.api.errors import SynapseError
+from tests import unittest
+from tests.utils import setup_test_homeserver
+from twisted.internet import defer
+from synapse.events import FrozenEvent
+
+
+class EventAuthTestCase(unittest.TestCase):
+
+    @defer.inlineCallbacks
+    def setUp(self):
+        self.hs = yield setup_test_homeserver(handlers=None)
+        self.auth = Auth(self.hs)
+
+    def test_EmptyRoom(self):
+        auth_events = (
+            {}
+        )
+        auth_events = {k: FrozenEvent(v) for k, v in auth_events.items()}
+
+        # Allowed events
+        self.auth.check(FrozenEvent(
+            {u'content': {u'creator': u'@u1:a'},
+             u'event_id': u'$e1:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'type': u'm.room.create'}
+        ), auth_events=auth_events, do_sig_check=False)
+
+        # Disallowed events
+        # Sent by a different server than the one which made the room_id
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'creator': u'@u1:b'},
+             u'event_id': u'$e2:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:b',
+             u'type': u'm.room.create'}
+        ), auth_events=auth_events, do_sig_check=False)
+        # All non-create events must reference a create event.
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'membership': u'join'},
+             u'event_id': u'$e3:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'@u1:a',
+             u'type': u'm.room.member'}
+        ), auth_events=auth_events, do_sig_check=False)
+
+    def test_FirstJoin(self):
+        auth_events = (
+            {('m.room.create', ''): {u'content': {u'creator': u'@u1:a'},
+                                     u'event_id': u'$e1:a',
+                                     u'room_id': u'!r1:a',
+                                     u'sender': u'@u1:a',
+                                     u'type': u'm.room.create'}}
+        )
+        auth_events = {k: FrozenEvent(v) for k, v in auth_events.items()}
+
+        # Allowed events
+        self.auth.check(FrozenEvent(
+            {u'content': {u'membership': u'join'},
+             u'event_id': u'$e2:a',
+             u'prev_events': [[u'$e1:a', {}]],
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'@u1:a',
+             u'type': u'm.room.member'}
+        ), auth_events=auth_events, do_sig_check=False)
+
+        # Disallowed events
+        # The prev_event is not the create event.
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'membership': u'join'},
+             u'event_id': u'$e3:a',
+             u'prev_events': [[u'$e2:a', {}]],
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'@u1:a',
+             u'type': u'm.room.member'}
+        ), auth_events=auth_events, do_sig_check=False)
+        # The membership key is not join
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'membership': u'invite'},
+             u'event_id': u'$e4:a',
+             u'prev_events': [[u'$e1:a', {}]],
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'@u1:a',
+             u'type': u'm.room.member'}
+        ), auth_events=auth_events, do_sig_check=False)
+        # The room_id doesn't match the create event
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'membership': u'join'},
+             u'event_id': u'$e5:a',
+             u'prev_events': [[u'$e1:a', {}]],
+             u'room_id': u'!r2:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'@u1:a',
+             u'type': u'm.room.member'}
+        ), auth_events=auth_events, do_sig_check=False)
+        # The sender doesn't match the room creator
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'membership': u'join'},
+             u'event_id': u'$e6:a',
+             u'prev_events': [[u'$e1:a', {}]],
+             u'room_id': u'!r1:a',
+             u'sender': u'@u2:a',
+             u'state_key': u'@u1:a',
+             u'type': u'm.room.member'}
+        ), auth_events=auth_events, do_sig_check=False)
+        # The sender doesn't match the state_key
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'membership': u'join'},
+             u'event_id': u'$e7:a',
+             u'prev_events': [[u'$e1:a', {}]],
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'@u2:a',
+             u'type': u'm.room.member'}
+        ), auth_events=auth_events, do_sig_check=False)
+
+    def test_FirstPowerLevels(self):
+        auth_events = (
+            {('m.room.create', ''): {u'content': {u'creator': u'@u1:a'},
+                                     u'event_id': u'$e1:a',
+                                     u'room_id': u'!r1:a',
+                                     u'sender': u'@u1:a',
+                                     u'type': u'm.room.create'},
+             ('m.room.member', u'@u1:a'): {u'content': {u'membership': u'join'},
+                                           u'event_id': u'$e2:a',
+                                           u'room_id': u'!r1:a',
+                                           u'sender': u'@u1:a',
+                                           u'state_key': u'@u1:a',
+                                           u'type': u'm.room.member'},
+             ('m.room.member', u'@u2:a'): {u'content': {u'membership': u'join'},
+                                           u'event_id': u'$e3:a',
+                                           u'room_id': u'!r1:a',
+                                           u'sender': u'@u2:a',
+                                           u'state_key': u'@u2:a',
+                                           u'type': u'm.room.member'}}
+        )
+        auth_events = {k: FrozenEvent(v) for k, v in auth_events.items()}
+
+        # Allowed events
+        self.auth.check(FrozenEvent(
+            {u'content': {u'ban': 50,
+                          u'events': {u'm.room.avatar': 50,
+                                      u'm.room.canonical_alias': 50,
+                                      u'm.room.history_visibility': 100,
+                                      u'm.room.name': 50,
+                                      u'm.room.power_levels': 100},
+                          u'events_default': 0,
+                          u'invite': 0,
+                          u'kick': 50,
+                          u'redact': 50,
+                          u'state_default': 50,
+                          u'users': {u'@u1:a': 100,
+                                     u'@u2:a': 100,
+                                     u'@u3:a': 50},
+                          u'users_default': 0},
+             u'event_id': u'$e3:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'',
+             u'type': u'm.room.power_levels'}
+        ), auth_events=auth_events, do_sig_check=False)
+        self.auth.check(FrozenEvent(
+            {u'content': {u'users': {u'@u1:a': 1000}},
+             u'event_id': u'$e4:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'',
+             u'type': u'm.room.power_levels'}
+        ), auth_events=auth_events, do_sig_check=False)
+
+        # Disallowed events
+        # Only the creator can send the first power level event
+        self.assertRaises(SynapseError, self.auth.check, FrozenEvent(
+            {u'content': {u'users': {u'@u1:a': 1000}},
+             u'event_id': u'$e4:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u2:a',
+             u'state_key': u'',
+             u'type': u'm.room.power_levels'}
+        ), auth_events=auth_events, do_sig_check=False)
+
+    def test_PowerLevels(self):
+        auth_events = (
+            {('m.room.create', ''): {u'content': {u'creator': u'@u1:a'},
+                                     u'event_id': u'$e1:a',
+                                     u'room_id': u'!r1:a',
+                                     u'sender': u'@u1:a',
+                                     u'type': u'm.room.create'},
+             ('m.room.member', u'@u1:a'): {u'content': {u'membership': u'join'},
+                                           u'event_id': u'$e2:a',
+                                           u'room_id': u'!r1:a',
+                                           u'sender': u'@u1:a',
+                                           u'state_key': u'@u1:a',
+                                           u'type': u'm.room.member'},
+             ('m.room.member', u'@u3:a'): {u'content': {u'membership': u'join'},
+                                           u'event_id': u'$e2:a',
+                                           u'room_id': u'!r1:a',
+                                           u'sender': u'@u1:a',
+                                           u'state_key': u'@u1:a',
+                                           u'type': u'm.room.member'}}
+        )
+        auth_events = {k: FrozenEvent(v) for k, v in auth_events.items()}
+
+        # Allowed events
+        self.auth.check(FrozenEvent(
+            {u'content': {u'ban': 50,
+                          u'events': {u'm.room.avatar': 50,
+                                      u'm.room.canonical_alias': 50,
+                                      u'm.room.history_visibility': 100,
+                                      u'm.room.name': 50,
+                                      u'm.room.power_levels': 100},
+                          u'events_default': 0,
+                          u'invite': 0,
+                          u'kick': 50,
+                          u'redact': 50,
+                          u'state_default': 50,
+                          u'users': {u'@u1:a': 100,
+                                     u'@u2:a': 100,
+                                     u'@u3:a': 50},
+                          u'users_default': 0},
+             u'event_id': u'$e4:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'',
+             u'type': u'm.room.power_levels'}
+        ), auth_events=auth_events, do_sig_check=False)
+        self.auth.check(FrozenEvent(
+            {u'content': {u'ban': 50,
+                          u'events': {u'm.room.power_levels': 100},
+                          u'events_default': 0,
+                          u'invite': 0,
+                          u'kick': 50,
+                          u'redact': 50,
+                          u'state_default': 50,
+                          u'users': {u'@u1:a': 100, u'@u2:a': 100},
+                          u'users_default': 0},
+             u'event_id': u'$e5:a',
+             u'room_id': u'!r1:a',
+             u'sender': u'@u1:a',
+             u'state_key': u'',
+             u'type': u'm.room.power_levels'}
+        ), auth_events=auth_events, do_sig_check=False)
+
+        # Disallowed events