summary refs log tree commit diff
diff options
context:
space:
mode:
-rwxr-xr-xcontrib/scripts/kick_users.py93
-rwxr-xr-xregister_new_matrix_user7
-rw-r--r--synapse/__init__.py2
-rw-r--r--synapse/api/auth.py23
-rw-r--r--synapse/handlers/typing.py16
-rw-r--r--synapse/python_dependencies.py6
-rw-r--r--tests/handlers/test_typing.py25
-rw-r--r--tests/rest/client/v1/test_typing.py7
8 files changed, 151 insertions, 28 deletions
diff --git a/contrib/scripts/kick_users.py b/contrib/scripts/kick_users.py
new file mode 100755
index 0000000000..5dfaec3ad0
--- /dev/null
+++ b/contrib/scripts/kick_users.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+from argparse import ArgumentParser
+import json
+import requests
+import sys
+import urllib
+
+def _mkurl(template, kws):
+    for key in kws:
+        template = template.replace(key, kws[key])
+    return template
+
+def main(hs, room_id, access_token, user_id_prefix, why):
+    if not why:
+        why = "Automated kick."
+    print "Kicking members on %s in room %s matching %s" % (hs, room_id, user_id_prefix)
+    room_state_url = _mkurl(
+        "$HS/_matrix/client/api/v1/rooms/$ROOM/state?access_token=$TOKEN",
+        {
+            "$HS": hs,
+            "$ROOM": room_id,
+            "$TOKEN": access_token
+        }
+    )
+    print "Getting room state => %s" % room_state_url
+    res = requests.get(room_state_url)
+    print "HTTP %s" % res.status_code
+    state_events = res.json()
+    if "error" in state_events:
+        print "FATAL"
+        print state_events
+        return
+
+    kick_list = []
+    room_name = room_id
+    for event in state_events:
+        if not event["type"] == "m.room.member":
+            if event["type"] == "m.room.name":
+                room_name = event["content"].get("name")
+            continue
+        if not event["content"].get("membership") == "join":
+            continue
+        if event["state_key"].startswith(user_id_prefix):
+            kick_list.append(event["state_key"])
+
+    if len(kick_list) == 0:
+        print "No user IDs match the prefix '%s'" % user_id_prefix
+        return
+
+    print "The following user IDs will be kicked from %s" % room_name
+    for uid in kick_list:
+        print uid
+    doit = raw_input("Continue? [Y]es\n")
+    if len(doit) > 0 and doit.lower() == 'y':
+        print "Kicking members..."
+        # encode them all
+        kick_list = [urllib.quote(uid) for uid in kick_list]
+        for uid in kick_list:
+            kick_url = _mkurl(
+                "$HS/_matrix/client/api/v1/rooms/$ROOM/state/m.room.member/$UID?access_token=$TOKEN",
+                {
+                    "$HS": hs,
+                    "$UID": uid,
+                    "$ROOM": room_id,
+                    "$TOKEN": access_token
+                }
+            )
+            kick_body = {
+                "membership": "leave",
+                "reason": why
+            }
+            print "Kicking %s" % uid
+            res = requests.put(kick_url, data=json.dumps(kick_body))
+            if res.status_code != 200:
+                print "ERROR: HTTP %s" % res.status_code
+            if res.json().get("error"):
+                print "ERROR: JSON %s" % res.json()
+            
+    
+
+if __name__ == "__main__":
+    parser = ArgumentParser("Kick members in a room matching a certain user ID prefix.")
+    parser.add_argument("-u","--user-id",help="The user ID prefix e.g. '@irc_'")
+    parser.add_argument("-t","--token",help="Your access_token")
+    parser.add_argument("-r","--room",help="The room ID to kick members in")
+    parser.add_argument("-s","--homeserver",help="The base HS url e.g. http://matrix.org")
+    parser.add_argument("-w","--why",help="Reason for the kick. Optional.")
+    args = parser.parse_args()
+    if not args.room or not args.token or not args.user_id or not args.homeserver:
+        parser.print_help()
+        sys.exit(1)
+    else:
+        main(args.homeserver, args.room, args.token, args.user_id, args.why)
diff --git a/register_new_matrix_user b/register_new_matrix_user
index daddadc302..4a520bdb5d 100755
--- a/register_new_matrix_user
+++ b/register_new_matrix_user
@@ -49,7 +49,12 @@ def request_registration(user, password, server_location, shared_secret):
         headers={'Content-Type': 'application/json'}
     )
     try:
-        f = urllib2.urlopen(req)
+        if sys.version_info[:3] >= (2, 7, 9):
+            # As of version 2.7.9, urllib2 now checks SSL certs
+            import ssl
+            f = urllib2.urlopen(req, context=ssl.SSLContext(ssl.PROTOCOL_SSLv23))
+        else:
+            f = urllib2.urlopen(req)
         f.read()
         f.close()
         print "Success."
diff --git a/synapse/__init__.py b/synapse/__init__.py
index fd87c7e2d0..56c10a84e9 100644
--- a/synapse/__init__.py
+++ b/synapse/__init__.py
@@ -16,4 +16,4 @@
 """ This is a reference implementation of a Matrix home server.
 """
 
-__version__ = "0.8.1-r3"
+__version__ = "0.8.1-r4"
diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 18f3d117b3..e159e4503f 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -215,17 +215,20 @@ class Auth(object):
         else:
             ban_level = 50  # FIXME (erikj): What should we do here?
 
-        if Membership.INVITE == membership:
-            # TODO (erikj): We should probably handle this more intelligently
-            # PRIVATE join rules.
-
-            # Invites are valid iff caller is in the room and target isn't.
+        if Membership.JOIN != membership:
+            # JOIN is the only action you can perform if you're not in the room
             if not caller_in_room:  # caller isn't joined
                 raise AuthError(
                     403,
                     "%s not in room %s." % (event.user_id, event.room_id,)
                 )
-            elif target_banned:
+
+        if Membership.INVITE == membership:
+            # TODO (erikj): We should probably handle this more intelligently
+            # PRIVATE join rules.
+
+            # Invites are valid iff caller is in the room and target isn't.
+            if target_banned:
                 raise AuthError(
                     403, "%s is banned from the room" % (target_user_id,)
                 )
@@ -251,13 +254,7 @@ class Auth(object):
                 raise AuthError(403, "You are not allowed to join this room")
         elif Membership.LEAVE == membership:
             # TODO (erikj): Implement kicks.
-
-            if not caller_in_room:  # trying to leave a room you aren't joined
-                raise AuthError(
-                    403,
-                    "%s not in room %s." % (target_user_id, event.room_id,)
-                )
-            elif target_banned and user_level < ban_level:
+            if target_banned and user_level < ban_level:
                 raise AuthError(
                     403, "You cannot unban user &s." % (target_user_id,)
                 )
diff --git a/synapse/handlers/typing.py b/synapse/handlers/typing.py
index c2762f92c7..c0b2bd7db0 100644
--- a/synapse/handlers/typing.py
+++ b/synapse/handlers/typing.py
@@ -223,6 +223,7 @@ class TypingNotificationEventSource(object):
     def __init__(self, hs):
         self.hs = hs
         self._handler = None
+        self._room_member_handler = None
 
     def handler(self):
         # Avoid cyclic dependency in handler setup
@@ -230,6 +231,11 @@ class TypingNotificationEventSource(object):
             self._handler = self.hs.get_handlers().typing_notification_handler
         return self._handler
 
+    def room_member_handler(self):
+        if not self._room_member_handler:
+            self._room_member_handler = self.hs.get_handlers().room_member_handler
+        return self._room_member_handler
+
     def _make_event_for(self, room_id):
         typing = self.handler()._room_typing[room_id]
         return {
@@ -240,19 +246,25 @@ class TypingNotificationEventSource(object):
             },
         }
 
+    @defer.inlineCallbacks
     def get_new_events_for_user(self, user, from_key, limit):
         from_key = int(from_key)
         handler = self.handler()
 
+        joined_room_ids = (
+            yield self.room_member_handler().get_joined_rooms_for_user(user)
+        )
+
         events = []
         for room_id in handler._room_serials:
+            if room_id not in joined_room_ids:
+                continue
             if handler._room_serials[room_id] <= from_key:
                 continue
 
-            # TODO: check if user is in room
             events.append(self._make_event_for(room_id))
 
-        return (events, handler._latest_room_serial)
+        defer.returnValue((events, handler._latest_room_serial))
 
     def get_current_key(self):
         return self.handler()._latest_room_serial
diff --git a/synapse/python_dependencies.py b/synapse/python_dependencies.py
index 6b6d5508b8..dac927d0a7 100644
--- a/synapse/python_dependencies.py
+++ b/synapse/python_dependencies.py
@@ -4,7 +4,7 @@ from distutils.version import LooseVersion
 logger = logging.getLogger(__name__)
 
 REQUIREMENTS = {
-    "syutil>=0.0.3": ["syutil"],
+    "syutil>=0.0.4": ["syutil"],
     "Twisted==14.0.2": ["twisted==14.0.2"],
     "service_identity>=1.0.0": ["service_identity>=1.0.0"],
     "pyopenssl>=0.14": ["OpenSSL>=0.14"],
@@ -43,8 +43,8 @@ DEPENDENCY_LINKS = [
     ),
     github_link(
         project="matrix-org/syutil",
-        version="v0.0.3",
-        egg="syutil-0.0.3",
+        version="v0.0.4",
+        egg="syutil-0.0.4",
     ),
     github_link(
         project="matrix-org/matrix-angular-sdk",
diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py
index bf34b7ccbd..91d4102fee 100644
--- a/tests/handlers/test_typing.py
+++ b/tests/handlers/test_typing.py
@@ -126,6 +126,13 @@ class TypingNotificationsTestCase(unittest.TestCase):
                 return defer.succeed([])
         self.room_member_handler.get_room_members = get_room_members
 
+        def get_joined_rooms_for_user(user):
+            if user in self.room_members:
+                return defer.succeed([self.room_id])
+            else:
+                return defer.succeed([])
+        self.room_member_handler.get_joined_rooms_for_user = get_joined_rooms_for_user
+
         @defer.inlineCallbacks
         def fetch_room_distributions_into(room_id, localusers=None,
                 remotedomains=None, ignore_user=None):
@@ -175,8 +182,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
         ])
 
         self.assertEquals(self.event_source.get_current_key(), 1)
+        events = yield self.event_source.get_new_events_for_user(self.u_apple, 0, None)
         self.assertEquals(
-            self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0],
+            events[0],
             [
                 {"type": "m.typing",
                  "room_id": self.room_id,
@@ -237,8 +245,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
         ])
 
         self.assertEquals(self.event_source.get_current_key(), 1)
+        events = yield self.event_source.get_new_events_for_user(self.u_apple, 0, None)
         self.assertEquals(
-            self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0],
+            events[0],
             [
                 {"type": "m.typing",
                  "room_id": self.room_id,
@@ -292,8 +301,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
         yield put_json.await_calls()
 
         self.assertEquals(self.event_source.get_current_key(), 1)
+        events = yield self.event_source.get_new_events_for_user(self.u_apple, 0, None)
         self.assertEquals(
-            self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0],
+            events[0],
             [
                 {"type": "m.typing",
                  "room_id": self.room_id,
@@ -322,8 +332,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
         self.on_new_user_event.reset_mock()
 
         self.assertEquals(self.event_source.get_current_key(), 1)
+        events = yield self.event_source.get_new_events_for_user(self.u_apple, 0, None)
         self.assertEquals(
-            self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0],
+            events[0],
             [
                 {"type": "m.typing",
                  "room_id": self.room_id,
@@ -340,8 +351,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
         ])
 
         self.assertEquals(self.event_source.get_current_key(), 2)
+        events = yield self.event_source.get_new_events_for_user(self.u_apple, 1, None)
         self.assertEquals(
-            self.event_source.get_new_events_for_user(self.u_apple, 1, None)[0],
+            events[0],
             [
                 {"type": "m.typing",
                  "room_id": self.room_id,
@@ -366,8 +378,9 @@ class TypingNotificationsTestCase(unittest.TestCase):
         self.on_new_user_event.reset_mock()
 
         self.assertEquals(self.event_source.get_current_key(), 3)
+        events = yield self.event_source.get_new_events_for_user(self.u_apple, 0, None)
         self.assertEquals(
-            self.event_source.get_new_events_for_user(self.u_apple, 0, None)[0],
+            events[0],
             [
                 {"type": "m.typing",
                  "room_id": self.room_id,
diff --git a/tests/rest/client/v1/test_typing.py b/tests/rest/client/v1/test_typing.py
index 80f2ec9ddf..7b3bd87439 100644
--- a/tests/rest/client/v1/test_typing.py
+++ b/tests/rest/client/v1/test_typing.py
@@ -34,6 +34,8 @@ class RoomTypingTestCase(RestTestCase):
     """ Tests /rooms/$room_id/typing/$user_id REST API. """
     user_id = "@sid:red"
 
+    user = UserID.from_string(user_id)
+
     @defer.inlineCallbacks
     def setUp(self):
         self.clock = MockClock()
@@ -75,7 +77,7 @@ class RoomTypingTestCase(RestTestCase):
 
         def get_room_members(room_id):
             if room_id == self.room_id:
-                return defer.succeed([UserID.from_string(self.user_id)])
+                return defer.succeed([self.user])
             else:
                 return defer.succeed([])
 
@@ -115,8 +117,9 @@ class RoomTypingTestCase(RestTestCase):
         self.assertEquals(200, code)
 
         self.assertEquals(self.event_source.get_current_key(), 1)
+        events = yield self.event_source.get_new_events_for_user(self.user, 0, None)
         self.assertEquals(
-            self.event_source.get_new_events_for_user(self.user_id, 0, None)[0],
+            events[0],
             [
                 {"type": "m.typing",
                  "room_id": self.room_id,