diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 8f32191b57..5321864d51 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -372,11 +372,11 @@ class Auth(object):
}
removed = set(old_people.keys()) - set(new_people.keys())
- added = set(old_people.keys()) - set(new_people.keys())
+ added = set(new_people.keys()) - set(old_people.keys())
same = set(old_people.keys()) & set(new_people.keys())
for r in removed:
- if int(old_list.content[r]) > user_level:
+ if int(old_list[r]) > user_level:
raise AuthError(
403,
"You don't have permission to remove user: %s" % (r, )
diff --git a/synapse/config/captcha.py b/synapse/config/captcha.py
index 8ebcfc3623..4ed9070b9e 100644
--- a/synapse/config/captcha.py
+++ b/synapse/config/captcha.py
@@ -24,6 +24,7 @@ class CaptchaConfig(Config):
self.captcha_ip_origin_is_x_forwarded = (
args.captcha_ip_origin_is_x_forwarded
)
+ self.captcha_bypass_secret = args.captcha_bypass_secret
@classmethod
def add_arguments(cls, parser):
@@ -43,4 +44,8 @@ class CaptchaConfig(Config):
"--captcha_ip_origin_is_x_forwarded", type=bool, default=False,
help="When checking captchas, use the X-Forwarded-For (XFF) header"
+ " as the client IP and not the actual client IP."
- )
\ No newline at end of file
+ )
+ group.add_argument(
+ "--captcha_bypass_secret", type=str,
+ help="A secret key used to bypass the captcha test entirely."
+ )
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index 001c6c110c..f52591d2a3 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -169,7 +169,15 @@ class FederationHandler(BaseHandler):
)
if not backfilled:
- yield self.notifier.on_new_room_event(event)
+ extra_users = []
+ if event.type == RoomMemberEvent.TYPE:
+ target_user_id = event.state_key
+ target_user = self.hs.parse_userid(target_user_id)
+ extra_users.append(target_user)
+
+ yield self.notifier.on_new_room_event(
+ event, extra_users=extra_users
+ )
if event.type == RoomMemberEvent.TYPE:
if event.membership == Membership.JOIN:
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index 14fae689f2..317ef2c80c 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -233,6 +233,22 @@ class MessageHandler(BaseHandler):
yield self._on_new_room_event(event, snapshot)
@defer.inlineCallbacks
+ def get_state_events(self, user_id, room_id):
+ """Retrieve all state events for a given room.
+
+ Args:
+ user_id(str): The user requesting state events.
+ room_id(str): The room ID to get all state events from.
+ Returns:
+ A list of dicts representing state events. [{}, {}, {}]
+ """
+ yield self.auth.check_joined_room(room_id, user_id)
+
+ # TODO: This is duplicating logic from snapshot_all_rooms
+ current_state = yield self.store.get_current_state(room_id)
+ defer.returnValue([self.hs.serialize_event(c) for c in current_state])
+
+ @defer.inlineCallbacks
def snapshot_all_rooms(self, user_id=None, pagin_config=None,
feedback=False):
"""Retrieve a snapshot of all rooms the user is invited or has joined.
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 5bc1280432..4d8727e8af 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -146,17 +146,6 @@ class RoomCreationHandler(BaseHandler):
)
yield handle_event(name_event)
- elif room_alias:
- name = room_alias.to_string()
- name_event = self.event_factory.create_event(
- etype=RoomNameEvent.TYPE,
- room_id=room_id,
- user_id=user_id,
- required_power_level=50,
- content={"name": name},
- )
-
- yield handle_event(name_event)
if "topic" in config:
topic = config["topic"]
diff --git a/synapse/rest/register.py b/synapse/rest/register.py
index af528a44f6..4935e323d9 100644
--- a/synapse/rest/register.py
+++ b/synapse/rest/register.py
@@ -21,6 +21,8 @@ from synapse.api.constants import LoginType
from base import RestServlet, client_path_pattern
import synapse.util.stringutils as stringutils
+from hashlib import sha1
+import hmac
import json
import logging
import urllib
@@ -28,6 +30,16 @@ import urllib
logger = logging.getLogger(__name__)
+# We ought to be using hmac.compare_digest() but on older pythons it doesn't
+# exist. It's a _really minor_ security flaw to use plain string comparison
+# because the timing attack is so obscured by all the other code here it's
+# unlikely to make much difference
+if hasattr(hmac, "compare_digest"):
+ compare_digest = hmac.compare_digest
+else:
+ compare_digest = lambda a, b: a == b
+
+
class RegisterRestServlet(RestServlet):
"""Handles registration with the home server.
@@ -142,6 +154,38 @@ class RegisterRestServlet(RestServlet):
if not self.hs.config.enable_registration_captcha:
raise SynapseError(400, "Captcha not required.")
+ yield self._check_recaptcha(request, register_json, session)
+
+ session[LoginType.RECAPTCHA] = True # mark captcha as done
+ self._save_session(session)
+ defer.returnValue({
+ "next": [LoginType.PASSWORD, LoginType.EMAIL_IDENTITY]
+ })
+
+ @defer.inlineCallbacks
+ def _check_recaptcha(self, request, register_json, session):
+ if ("captcha_bypass_hmac" in register_json and
+ self.hs.config.captcha_bypass_secret):
+ if "user" not in register_json:
+ raise SynapseError(400, "Captcha bypass needs 'user'")
+
+ want = hmac.new(
+ key=self.hs.config.captcha_bypass_secret,
+ msg=register_json["user"],
+ digestmod=sha1,
+ ).hexdigest()
+
+ # str() because otherwise hmac complains that 'unicode' does not
+ # have the buffer interface
+ got = str(register_json["captcha_bypass_hmac"])
+
+ if compare_digest(want, got):
+ session["user"] = register_json["user"]
+ defer.returnValue(None)
+ else:
+ raise SynapseError(400, "Captcha bypass HMAC incorrect",
+ errcode=Codes.CAPTCHA_NEEDED)
+
challenge = None
user_response = None
try:
@@ -166,11 +210,6 @@ class RegisterRestServlet(RestServlet):
challenge,
user_response
)
- session[LoginType.RECAPTCHA] = True # mark captcha as done
- self._save_session(session)
- defer.returnValue({
- "next": [LoginType.PASSWORD, LoginType.EMAIL_IDENTITY]
- })
@defer.inlineCallbacks
def _do_email_identity(self, request, register_json, session):
@@ -195,6 +234,10 @@ class RegisterRestServlet(RestServlet):
# captcha should've been done by this stage!
raise SynapseError(400, "Captcha is required.")
+ if ("user" in session and "user" in register_json and
+ session["user"] != register_json["user"]):
+ raise SynapseError(400, "Cannot change user ID during registration")
+
password = register_json["password"].encode("utf-8")
desired_user_id = (register_json["user"].encode("utf-8") if "user"
in register_json else None)
diff --git a/synapse/rest/room.py b/synapse/rest/room.py
index ecb1e346d9..cf2e7af2e4 100644
--- a/synapse/rest/room.py
+++ b/synapse/rest/room.py
@@ -329,12 +329,13 @@ class RoomStateRestServlet(RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, room_id):
user = yield self.auth.get_user_by_req(request)
- # TODO: Get all the current state for this room and return in the same
- # format as initial sync, that is:
- # [
- # { state event }, { state event }
- # ]
- defer.returnValue((200, []))
+ handler = self.handlers.message_handler
+ # Get all the current state for this room
+ events = yield handler.get_state_events(
+ room_id=urllib.unquote(room_id),
+ user_id=user.to_string(),
+ )
+ defer.returnValue((200, events))
# TODO: Needs unit testing
|