diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py
index 9989fe8670..de4d23bbb3 100644
--- a/synapse/handlers/_base.py
+++ b/synapse/handlers/_base.py
@@ -42,9 +42,6 @@ class BaseHandler(object):
retry_after_ms=int(1000*(time_allowed - time_now)),
)
-
-class BaseRoomHandler(BaseHandler):
-
@defer.inlineCallbacks
def _on_new_room_event(self, event, snapshot, extra_destinations=[],
extra_users=[]):
diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py
index 1b9e831fc0..4ab00a761a 100644
--- a/synapse/handlers/directory.py
+++ b/synapse/handlers/directory.py
@@ -19,8 +19,10 @@ from ._base import BaseHandler
from synapse.api.errors import SynapseError
from synapse.http.client import HttpClient
+from synapse.api.events.room import RoomAliasesEvent
import logging
+import sqlite3
logger = logging.getLogger(__name__)
@@ -37,7 +39,8 @@ class DirectoryHandler(BaseHandler):
)
@defer.inlineCallbacks
- def create_association(self, room_alias, room_id, servers=None):
+ def create_association(self, user_id, room_alias, room_id, servers=None):
+
# TODO(erikj): Do auth.
if not room_alias.is_mine:
@@ -54,12 +57,37 @@ class DirectoryHandler(BaseHandler):
if not servers:
raise SynapseError(400, "Failed to get server list")
- yield self.store.create_room_alias_association(
- room_alias,
- room_id,
- servers
+
+ try:
+ yield self.store.create_room_alias_association(
+ room_alias,
+ room_id,
+ servers
+ )
+ except sqlite3.IntegrityError:
+ defer.returnValue("Already exists")
+
+ # TODO: Send the room event.
+
+ aliases = yield self.store.get_aliases_for_room(room_id)
+
+ event = self.event_factory.create_event(
+ etype=RoomAliasesEvent.TYPE,
+ state_key=self.hs.hostname,
+ room_id=room_id,
+ user_id=user_id,
+ content={"aliases": aliases},
+ )
+
+ snapshot = yield self.store.snapshot_room(
+ room_id=room_id,
+ user_id=user_id,
)
+ yield self.state_handler.handle_new_event(event, snapshot)
+ yield self._on_new_room_event(event, snapshot, extra_users=[user_id])
+
+
@defer.inlineCallbacks
def get_association(self, room_alias):
room_id = None
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index dad2bbd1a4..87fc04478b 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -19,7 +19,7 @@ from synapse.api.constants import Membership
from synapse.api.events.room import RoomTopicEvent
from synapse.api.errors import RoomError
from synapse.streams.config import PaginationConfig
-from ._base import BaseRoomHandler
+from ._base import BaseHandler
import logging
@@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
-class MessageHandler(BaseRoomHandler):
+class MessageHandler(BaseHandler):
def __init__(self, hs):
super(MessageHandler, self).__init__(hs)
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index c79bb6ff76..b2af09f090 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -796,11 +796,12 @@ class PresenceEventSource(object):
updates = []
# TODO(paul): use a DeferredList ? How to limit concurrency.
for observed_user in cachemap.keys():
- if not (from_key < cachemap[observed_user].serial):
+ cached = cachemap[observed_user]
+ if not (from_key < cached.serial):
continue
if (yield self.is_visible(observer_user, observed_user)):
- updates.append((observed_user, cachemap[observed_user]))
+ updates.append((observed_user, cached))
# TODO(paul): limit
diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py
index bee052274f..0b841d6d3a 100644
--- a/synapse/handlers/register.py
+++ b/synapse/handlers/register.py
@@ -17,7 +17,9 @@
from twisted.internet import defer
from synapse.types import UserID
-from synapse.api.errors import SynapseError, RegistrationError
+from synapse.api.errors import (
+ SynapseError, RegistrationError, InvalidCaptchaError
+)
from ._base import BaseHandler
import synapse.util.stringutils as stringutils
from synapse.http.client import PlainHttpClient
@@ -38,7 +40,8 @@ class RegistrationHandler(BaseHandler):
self.distributor.declare("registered_user")
@defer.inlineCallbacks
- def register(self, localpart=None, password=None, threepidCreds=None):
+ def register(self, localpart=None, password=None, threepidCreds=None,
+ captcha_info={}):
"""Registers a new client on the server.
Args:
@@ -51,10 +54,26 @@ class RegistrationHandler(BaseHandler):
Raises:
RegistrationError if there was a problem registering.
"""
+ if captcha_info:
+ captcha_response = yield self._validate_captcha(
+ captcha_info["ip"],
+ captcha_info["private_key"],
+ captcha_info["challenge"],
+ captcha_info["response"]
+ )
+ if not captcha_response["valid"]:
+ logger.info("Invalid captcha entered from %s. Error: %s",
+ captcha_info["ip"], captcha_response["error_url"])
+ raise InvalidCaptchaError(
+ error_url=captcha_response["error_url"]
+ )
+ else:
+ logger.info("Valid captcha entered from %s", captcha_info["ip"])
if threepidCreds:
for c in threepidCreds:
- logger.info("validating theeepidcred sid %s on id server %s", c['sid'], c['idServer'])
+ logger.info("validating theeepidcred sid %s on id server %s",
+ c['sid'], c['idServer'])
try:
threepid = yield self._threepid_from_creds(c)
except:
@@ -63,7 +82,8 @@ class RegistrationHandler(BaseHandler):
if not threepid:
raise RegistrationError(400, "Couldn't validate 3pid")
- logger.info("got threepid medium %s address %s", threepid['medium'], threepid['address'])
+ logger.info("got threepid medium %s address %s",
+ threepid['medium'], threepid['address'])
password_hash = None
if password:
@@ -131,7 +151,8 @@ class RegistrationHandler(BaseHandler):
# XXX: make this configurable!
trustedIdServers = [ 'matrix.org:8090' ]
if not creds['idServer'] in trustedIdServers:
- logger.warn('%s is not a trusted ID server: rejecting 3pid credentials', creds['idServer'])
+ logger.warn('%s is not a trusted ID server: rejecting 3pid '+
+ 'credentials', creds['idServer'])
defer.returnValue(None)
data = yield httpCli.get_json(
creds['idServer'],
@@ -149,9 +170,44 @@ class RegistrationHandler(BaseHandler):
data = yield httpCli.post_urlencoded_get_json(
creds['idServer'],
"/_matrix/identity/api/v1/3pid/bind",
- { 'sid': creds['sid'], 'clientSecret': creds['clientSecret'], 'mxid':mxid }
+ { 'sid': creds['sid'], 'clientSecret': creds['clientSecret'],
+ 'mxid':mxid }
)
defer.returnValue(data)
+ @defer.inlineCallbacks
+ def _validate_captcha(self, ip_addr, private_key, challenge, response):
+ """Validates the captcha provided.
+
+ Returns:
+ dict: Containing 'valid'(bool) and 'error_url'(str) if invalid.
+
+ """
+ response = yield self._submit_captcha(ip_addr, private_key, challenge,
+ response)
+ # parse Google's response. Lovely format..
+ lines = response.split('\n')
+ json = {
+ "valid": lines[0] == 'true',
+ "error_url": "http://www.google.com/recaptcha/api/challenge?"+
+ "error=%s" % lines[1]
+ }
+ defer.returnValue(json)
+
+ @defer.inlineCallbacks
+ def _submit_captcha(self, ip_addr, private_key, challenge, response):
+ client = PlainHttpClient(self.hs)
+ data = yield client.post_urlencoded_get_raw(
+ "www.google.com:80",
+ "/recaptcha/api/verify",
+ accept_partial=True, # twisted dislikes google's response, no content length.
+ args={
+ 'privatekey': private_key,
+ 'remoteip': ip_addr,
+ 'challenge': challenge,
+ 'response': response
+ }
+ )
+ defer.returnValue(data)
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 8171e9eb45..a0d0f2af16 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -25,14 +25,14 @@ from synapse.api.events.room import (
RoomSendEventLevelEvent, RoomOpsPowerLevelsEvent, RoomNameEvent,
)
from synapse.util import stringutils
-from ._base import BaseRoomHandler
+from ._base import BaseHandler
import logging
logger = logging.getLogger(__name__)
-class RoomCreationHandler(BaseRoomHandler):
+class RoomCreationHandler(BaseHandler):
@defer.inlineCallbacks
def create_room(self, user_id, room_id, config):
@@ -65,6 +65,13 @@ class RoomCreationHandler(BaseRoomHandler):
else:
room_alias = None
+ invite_list = config.get("invite", [])
+ for i in invite_list:
+ try:
+ self.hs.parse_userid(i)
+ except:
+ raise SynapseError(400, "Invalid user_id: %s" % (i,))
+
is_public = config.get("visibility", None) == "public"
if room_id:
@@ -105,7 +112,9 @@ class RoomCreationHandler(BaseRoomHandler):
)
if room_alias:
- yield self.store.create_room_alias_association(
+ directory_handler = self.hs.get_handlers().directory_handler
+ yield directory_handler.create_association(
+ user_id=user_id,
room_id=room_id,
room_alias=room_alias,
servers=[self.hs.hostname],
@@ -132,7 +141,7 @@ class RoomCreationHandler(BaseRoomHandler):
etype=RoomNameEvent.TYPE,
room_id=room_id,
user_id=user_id,
- required_power_level=5,
+ required_power_level=50,
content={"name": name},
)
@@ -143,7 +152,7 @@ class RoomCreationHandler(BaseRoomHandler):
etype=RoomNameEvent.TYPE,
room_id=room_id,
user_id=user_id,
- required_power_level=5,
+ required_power_level=50,
content={"name": name},
)
@@ -155,7 +164,7 @@ class RoomCreationHandler(BaseRoomHandler):
etype=RoomTopicEvent.TYPE,
room_id=room_id,
user_id=user_id,
- required_power_level=5,
+ required_power_level=50,
content={"topic": topic},
)
@@ -176,6 +185,25 @@ class RoomCreationHandler(BaseRoomHandler):
do_auth=False
)
+ content = {"membership": Membership.INVITE}
+ for invitee in invite_list:
+ invite_event = self.event_factory.create_event(
+ etype=RoomMemberEvent.TYPE,
+ state_key=invitee,
+ room_id=room_id,
+ user_id=user_id,
+ content=content
+ )
+
+ yield self.hs.get_handlers().room_member_handler.change_membership(
+ invite_event,
+ do_auth=False
+ )
+
+ yield self.hs.get_handlers().room_member_handler.change_membership(
+ join_event,
+ do_auth=False
+ )
result = {"room_id": room_id}
if room_alias:
result["room_alias"] = room_alias.to_string()
@@ -186,7 +214,7 @@ class RoomCreationHandler(BaseRoomHandler):
event_keys = {
"room_id": room_id,
"user_id": creator.to_string(),
- "required_power_level": 10,
+ "required_power_level": 100,
}
def create(etype, **content):
@@ -203,7 +231,7 @@ class RoomCreationHandler(BaseRoomHandler):
power_levels_event = self.event_factory.create_event(
etype=RoomPowerLevelsEvent.TYPE,
- content={creator.to_string(): 10, "default": 0},
+ content={creator.to_string(): 100, "default": 0},
**event_keys
)
@@ -215,7 +243,7 @@ class RoomCreationHandler(BaseRoomHandler):
add_state_event = create(
etype=RoomAddStateLevelEvent.TYPE,
- level=10,
+ level=100,
)
send_event = create(
@@ -225,8 +253,8 @@ class RoomCreationHandler(BaseRoomHandler):
ops = create(
etype=RoomOpsPowerLevelsEvent.TYPE,
- ban_level=5,
- kick_level=5,
+ ban_level=50,
+ kick_level=50,
)
return [
@@ -239,7 +267,7 @@ class RoomCreationHandler(BaseRoomHandler):
]
-class RoomMemberHandler(BaseRoomHandler):
+class RoomMemberHandler(BaseHandler):
# TODO(paul): This handler currently contains a messy conflation of
# low-level API that works on UserID objects and so on, and REST-level
# API that takes ID strings and returns pagination chunks. These concerns
@@ -560,7 +588,7 @@ class RoomMemberHandler(BaseRoomHandler):
extra_users=[target_user]
)
-class RoomListHandler(BaseRoomHandler):
+class RoomListHandler(BaseHandler):
@defer.inlineCallbacks
def get_public_room_list(self):
|