diff --git a/README.rst b/README.rst
index 21e3479687..5ff53f2df7 100644
--- a/README.rst
+++ b/README.rst
@@ -101,25 +101,26 @@ header files for python C extensions.
Installing prerequisites on Ubuntu or Debian::
- $ sudo apt-get install build-essential python2.7-dev libffi-dev \
- python-pip python-setuptools sqlite3 \
- libssl-dev python-virtualenv libjpeg-dev
+ sudo apt-get install build-essential python2.7-dev libffi-dev \
+ python-pip python-setuptools sqlite3 \
+ libssl-dev python-virtualenv libjpeg-dev
Installing prerequisites on ArchLinux::
- $ sudo pacman -S base-devel python2 python-pip \
- python-setuptools python-virtualenv sqlite3
+ sudo pacman -S base-devel python2 python-pip \
+ python-setuptools python-virtualenv sqlite3
Installing prerequisites on Mac OS X::
- $ xcode-select --install
- $ sudo pip install virtualenv
+ xcode-select --install
+ sudo easy_install pip
+ sudo pip install virtualenv
To install the synapse homeserver run::
- $ virtualenv -p python2.7 ~/.synapse
- $ source ~/.synapse/bin/activate
- $ pip install --process-dependency-links https://github.com/matrix-org/synapse/tarball/master
+ virtualenv -p python2.7 ~/.synapse
+ source ~/.synapse/bin/activate
+ pip install --process-dependency-links https://github.com/matrix-org/synapse/tarball/master
This installs synapse, along with the libraries it uses, into a virtual
environment under ``~/.synapse``. Feel free to pick a different directory
@@ -132,8 +133,8 @@ above in Docker at https://registry.hub.docker.com/u/silviof/docker-matrix/.
To set up your homeserver, run (in your virtualenv, as before)::
- $ cd ~/.synapse
- $ python -m synapse.app.homeserver \
+ cd ~/.synapse
+ python -m synapse.app.homeserver \
--server-name machine.my.domain.name \
--config-path homeserver.yaml \
--generate-config
@@ -192,9 +193,9 @@ Running Synapse
To actually run your new homeserver, pick a working directory for Synapse to run
(e.g. ``~/.synapse``), and::
- $ cd ~/.synapse
- $ source ./bin/activate
- $ synctl start
+ cd ~/.synapse
+ source ./bin/activate
+ synctl start
Platform Specific Instructions
==============================
@@ -212,12 +213,12 @@ defaults to python 3, but synapse currently assumes python 2.7 by default:
pip may be outdated (6.0.7-1 and needs to be upgraded to 6.0.8-1 )::
- $ sudo pip2.7 install --upgrade pip
+ sudo pip2.7 install --upgrade pip
You also may need to explicitly specify python 2.7 again during the install
request::
- $ pip2.7 install --process-dependency-links \
+ pip2.7 install --process-dependency-links \
https://github.com/matrix-org/synapse/tarball/master
If you encounter an error with lib bcrypt causing an Wrong ELF Class:
@@ -225,13 +226,13 @@ ELFCLASS32 (x64 Systems), you may need to reinstall py-bcrypt to correctly
compile it under the right architecture. (This should not be needed if
installing under virtualenv)::
- $ sudo pip2.7 uninstall py-bcrypt
- $ sudo pip2.7 install py-bcrypt
+ sudo pip2.7 uninstall py-bcrypt
+ sudo pip2.7 install py-bcrypt
During setup of Synapse you need to call python2.7 directly again::
- $ cd ~/.synapse
- $ python2.7 -m synapse.app.homeserver \
+ cd ~/.synapse
+ python2.7 -m synapse.app.homeserver \
--server-name machine.my.domain.name \
--config-path homeserver.yaml \
--generate-config
@@ -279,22 +280,22 @@ Synapse requires pip 1.7 or later, so if your OS provides too old a version and
you get errors about ``error: no such option: --process-dependency-links`` you
may need to manually upgrade it::
- $ sudo pip install --upgrade pip
+ sudo pip install --upgrade pip
If pip crashes mid-installation for reason (e.g. lost terminal), pip may
refuse to run until you remove the temporary installation directory it
created. To reset the installation::
- $ rm -rf /tmp/pip_install_matrix
+ rm -rf /tmp/pip_install_matrix
pip seems to leak *lots* of memory during installation. For instance, a Linux
host with 512MB of RAM may run out of memory whilst installing Twisted. If this
happens, you will have to individually install the dependencies which are
failing, e.g.::
- $ pip install twisted
+ pip install twisted
-On OSX, if you encounter clang: error: unknown argument: '-mno-fused-madd' you
+On OS X, if you encounter clang: error: unknown argument: '-mno-fused-madd' you
will need to export CFLAGS=-Qunused-arguments.
Troubleshooting Running
@@ -310,10 +311,11 @@ correctly, causing all tests to fail with errors about missing "sodium.h". To
fix try re-installing from PyPI or directly from
(https://github.com/pyca/pynacl)::
- $ # Install from PyPI
- $ pip install --user --upgrade --force pynacl
- $ # Install from github
- $ pip install --user https://github.com/pyca/pynacl/tarball/master
+ # Install from PyPI
+ pip install --user --upgrade --force pynacl
+
+ # Install from github
+ pip install --user https://github.com/pyca/pynacl/tarball/master
ArchLinux
~~~~~~~~~
@@ -321,7 +323,7 @@ ArchLinux
If running `$ synctl start` fails with 'returned non-zero exit status 1',
you will need to explicitly call Python2.7 - either running as::
- $ python2.7 -m synapse.app.homeserver --daemonize -c homeserver.yaml
+ python2.7 -m synapse.app.homeserver --daemonize -c homeserver.yaml
...or by editing synctl with the correct python executable.
@@ -331,16 +333,16 @@ Synapse Development
To check out a synapse for development, clone the git repo into a working
directory of your choice::
- $ git clone https://github.com/matrix-org/synapse.git
- $ cd synapse
+ git clone https://github.com/matrix-org/synapse.git
+ cd synapse
Synapse has a number of external dependencies, that are easiest
to install using pip and a virtualenv::
- $ virtualenv env
- $ source env/bin/activate
- $ python synapse/python_dependencies.py | xargs -n1 pip install
- $ pip install setuptools_trial mock
+ virtualenv env
+ source env/bin/activate
+ python synapse/python_dependencies.py | xargs -n1 pip install
+ pip install setuptools_trial mock
This will run a process of downloading and installing all the needed
dependencies into a virtual env.
@@ -348,7 +350,7 @@ dependencies into a virtual env.
Once this is done, you may wish to run Synapse's unit tests, to
check that everything is installed as it should be::
- $ python setup.py test
+ python setup.py test
This should end with a 'PASSED' result::
@@ -389,11 +391,11 @@ IDs:
For the first form, simply pass the required hostname (of the machine) as the
--server-name parameter::
- $ python -m synapse.app.homeserver \
+ python -m synapse.app.homeserver \
--server-name machine.my.domain.name \
--config-path homeserver.yaml \
--generate-config
- $ python -m synapse.app.homeserver --config-path homeserver.yaml
+ python -m synapse.app.homeserver --config-path homeserver.yaml
Alternatively, you can run ``synctl start`` to guide you through the process.
@@ -410,11 +412,11 @@ record would then look something like::
At this point, you should then run the homeserver with the hostname of this
SRV record, as that is the name other machines will expect it to have::
- $ python -m synapse.app.homeserver \
+ python -m synapse.app.homeserver \
--server-name YOURDOMAIN \
--config-path homeserver.yaml \
--generate-config
- $ python -m synapse.app.homeserver --config-path homeserver.yaml
+ python -m synapse.app.homeserver --config-path homeserver.yaml
You may additionally want to pass one or more "-v" options, in order to
@@ -428,7 +430,7 @@ private federation (``localhost:8080``, ``localhost:8081`` and
``localhost:8082``) which you can then access through the webclient running at
http://localhost:8080. Simply run::
- $ demo/start.sh
+ demo/start.sh
This is mainly useful just for development purposes.
@@ -502,10 +504,10 @@ Building Internal API Documentation
Before building internal API documentation install sphinx and
sphinxcontrib-napoleon::
- $ pip install sphinx
- $ pip install sphinxcontrib-napoleon
+ pip install sphinx
+ pip install sphinxcontrib-napoleon
Building internal API documentation::
- $ python setup.py build_sphinx
+ python setup.py build_sphinx
diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py
index 6200e10775..c1095708a0 100644
--- a/synapse/handlers/identity.py
+++ b/synapse/handlers/identity.py
@@ -44,7 +44,7 @@ class IdentityHandler(BaseHandler):
http_client = SimpleHttpClient(self.hs)
# XXX: make this configurable!
# trustedIdServers = ['matrix.org', 'localhost:8090']
- trustedIdServers = ['matrix.org']
+ trustedIdServers = ['matrix.org', 'vector.im']
if 'id_server' in creds:
id_server = creds['id_server']
diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py
index a1288b4252..f81d75017d 100644
--- a/synapse/handlers/register.py
+++ b/synapse/handlers/register.py
@@ -73,7 +73,8 @@ class RegistrationHandler(BaseHandler):
localpart : The local part of the user ID to register. If None,
one will be randomly generated.
password (str) : The password to assign to this user so they can
- login again.
+ login again. This can be None which means they cannot login again
+ via a password (e.g. the user is an application service user).
Returns:
A tuple of (user_id, access_token).
Raises:
diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py
index 0c737d73b8..b5926f9ca6 100644
--- a/synapse/rest/client/v2_alpha/register.py
+++ b/synapse/rest/client/v2_alpha/register.py
@@ -19,7 +19,7 @@ from synapse.api.constants import LoginType
from synapse.api.errors import SynapseError, Codes
from synapse.http.servlet import RestServlet
-from ._base import client_v2_pattern, parse_request_allow_empty
+from ._base import client_v2_pattern, parse_json_dict_from_request
import logging
import hmac
@@ -55,30 +55,55 @@ class RegisterRestServlet(RestServlet):
@defer.inlineCallbacks
def on_POST(self, request):
yield run_on_reactor()
+ body = parse_json_dict_from_request(request)
- body = parse_request_allow_empty(request)
- # we do basic sanity checks here because the auth
- # layer will store these in sessions
+ # we do basic sanity checks here because the auth layer will store these
+ # in sessions. Pull out the username/password provided to us.
+ desired_password = None
if 'password' in body:
- if ((not isinstance(body['password'], str) and
- not isinstance(body['password'], unicode)) or
+ if (not isinstance(body['password'], basestring) or
len(body['password']) > 512):
raise SynapseError(400, "Invalid password")
+ desired_password = body["password"]
+ desired_username = None
if 'username' in body:
- if ((not isinstance(body['username'], str) and
- not isinstance(body['username'], unicode)) or
+ if (not isinstance(body['username'], basestring) or
len(body['username']) > 512):
raise SynapseError(400, "Invalid username")
desired_username = body['username']
- yield self.registration_handler.check_username(desired_username)
-
- is_using_shared_secret = False
- is_application_server = False
- service = None
+ appservice = None
if 'access_token' in request.args:
- service = yield self.auth.get_appservice_by_req(request)
+ appservice = yield self.auth.get_appservice_by_req(request)
+
+ # fork off as soon as possible for ASes and shared secret auth which
+ # have completely different registration flows to normal users
+
+ # == Application Service Registration ==
+ if appservice:
+ result = yield self._do_appservice_registration(
+ desired_username, request.args["access_token"][0]
+ )
+ defer.returnValue((200, result)) # we throw for non 200 responses
+ return
+
+ # == Shared Secret Registration == (e.g. create new user scripts)
+ if 'mac' in body:
+ # FIXME: Should we really be determining if this is shared secret
+ # auth based purely on the 'mac' key?
+ result = yield self._do_shared_secret_registration(
+ desired_username, desired_password, body["mac"]
+ )
+ defer.returnValue((200, result)) # we throw for non 200 responses
+ return
+
+ # == Normal User Registration == (everyone else)
+ if self.hs.config.disable_registration:
+ raise SynapseError(403, "Registration has been disabled")
+
+ if desired_username is not None:
+ yield self.registration_handler.check_username(desired_username)
if self.hs.config.enable_registration_captcha:
flows = [
@@ -91,39 +116,20 @@ class RegisterRestServlet(RestServlet):
[LoginType.EMAIL_IDENTITY]
]
- result = None
- if service:
- is_application_server = True
- params = body
- elif 'mac' in body:
- # Check registration-specific shared secret auth
- if 'username' not in body:
- raise SynapseError(400, "", Codes.MISSING_PARAM)
- self._check_shared_secret_auth(
- body['username'], body['mac']
- )
- is_using_shared_secret = True
- params = body
- else:
- authed, result, params = yield self.auth_handler.check_auth(
- flows, body, self.hs.get_ip_from_request(request)
- )
-
- if not authed:
- defer.returnValue((401, result))
-
- can_register = (
- not self.hs.config.disable_registration
- or is_application_server
- or is_using_shared_secret
+ authed, result, params = yield self.auth_handler.check_auth(
+ flows, body, self.hs.get_ip_from_request(request)
)
- if not can_register:
- raise SynapseError(403, "Registration has been disabled")
+ if not authed:
+ defer.returnValue((401, result))
+ return
+
+ # NB: This may be from the auth handler and NOT from the POST
if 'password' not in params:
- raise SynapseError(400, "", Codes.MISSING_PARAM)
- desired_username = params['username'] if 'username' in params else None
- new_password = params['password']
+ raise SynapseError(400, "Missing password.", Codes.MISSING_PARAM)
+
+ desired_username = params.get("username", None)
+ new_password = params.get("password", None)
(user_id, token) = yield self.registration_handler.register(
localpart=desired_username,
@@ -156,18 +162,21 @@ class RegisterRestServlet(RestServlet):
else:
logger.info("bind_email not specified: not binding email")
- result = {
- "user_id": user_id,
- "access_token": token,
- "home_server": self.hs.hostname,
- }
-
+ result = self._create_registration_details(user_id, token)
defer.returnValue((200, result))
def on_OPTIONS(self, _):
return 200, {}
- def _check_shared_secret_auth(self, username, mac):
+ @defer.inlineCallbacks
+ def _do_appservice_registration(self, username, as_token):
+ (user_id, token) = yield self.registration_handler.appservice_register(
+ username, as_token
+ )
+ defer.returnValue(self._create_registration_details(user_id, token))
+
+ @defer.inlineCallbacks
+ def _do_shared_secret_registration(self, username, password, mac):
if not self.hs.config.registration_shared_secret:
raise SynapseError(400, "Shared secret registration is not enabled")
@@ -183,13 +192,23 @@ class RegisterRestServlet(RestServlet):
digestmod=sha1,
).hexdigest()
- if compare_digest(want_mac, got_mac):
- return True
- else:
+ if not compare_digest(want_mac, got_mac):
raise SynapseError(
403, "HMAC incorrect",
)
+ (user_id, token) = yield self.registration_handler.register(
+ localpart=username, password=password
+ )
+ defer.returnValue(self._create_registration_details(user_id, token))
+
+ def _create_registration_details(self, user_id, token):
+ return {
+ "user_id": user_id,
+ "access_token": token,
+ "home_server": self.hs.hostname,
+ }
+
def register_servlets(hs, http_server):
RegisterRestServlet(hs).register(http_server)
diff --git a/synapse/rest/media/v1/base_resource.py b/synapse/rest/media/v1/base_resource.py
index c43ae0314b..84e1961a21 100644
--- a/synapse/rest/media/v1/base_resource.py
+++ b/synapse/rest/media/v1/base_resource.py
@@ -244,43 +244,52 @@ class BaseMediaResource(Resource):
)
return
- scales = set()
- crops = set()
- for r_width, r_height, r_method, r_type in requirements:
- if r_method == "scale":
- t_width, t_height = thumbnailer.aspect(r_width, r_height)
- scales.add((
- min(m_width, t_width), min(m_height, t_height), r_type,
+ local_thumbnails = []
+
+ def generate_thumbnails():
+ scales = set()
+ crops = set()
+ for r_width, r_height, r_method, r_type in requirements:
+ if r_method == "scale":
+ t_width, t_height = thumbnailer.aspect(r_width, r_height)
+ scales.add((
+ min(m_width, t_width), min(m_height, t_height), r_type,
+ ))
+ elif r_method == "crop":
+ crops.add((r_width, r_height, r_type))
+
+ for t_width, t_height, t_type in scales:
+ t_method = "scale"
+ t_path = self.filepaths.local_media_thumbnail(
+ media_id, t_width, t_height, t_type, t_method
+ )
+ self._makedirs(t_path)
+ t_len = thumbnailer.scale(t_path, t_width, t_height, t_type)
+
+ local_thumbnails.append((
+ media_id, t_width, t_height, t_type, t_method, t_len
))
- elif r_method == "crop":
- crops.add((r_width, r_height, r_type))
- for t_width, t_height, t_type in scales:
- t_method = "scale"
- t_path = self.filepaths.local_media_thumbnail(
- media_id, t_width, t_height, t_type, t_method
- )
- self._makedirs(t_path)
- t_len = thumbnailer.scale(t_path, t_width, t_height, t_type)
- yield self.store.store_local_thumbnail(
- media_id, t_width, t_height, t_type, t_method, t_len
- )
+ for t_width, t_height, t_type in crops:
+ if (t_width, t_height, t_type) in scales:
+ # If the aspect ratio of the cropped thumbnail matches a purely
+ # scaled one then there is no point in calculating a separate
+ # thumbnail.
+ continue
+ t_method = "crop"
+ t_path = self.filepaths.local_media_thumbnail(
+ media_id, t_width, t_height, t_type, t_method
+ )
+ self._makedirs(t_path)
+ t_len = thumbnailer.crop(t_path, t_width, t_height, t_type)
+ local_thumbnails.append((
+ media_id, t_width, t_height, t_type, t_method, t_len
+ ))
- for t_width, t_height, t_type in crops:
- if (t_width, t_height, t_type) in scales:
- # If the aspect ratio of the cropped thumbnail matches a purely
- # scaled one then there is no point in calculating a separate
- # thumbnail.
- continue
- t_method = "crop"
- t_path = self.filepaths.local_media_thumbnail(
- media_id, t_width, t_height, t_type, t_method
- )
- self._makedirs(t_path)
- t_len = thumbnailer.crop(t_path, t_width, t_height, t_type)
- yield self.store.store_local_thumbnail(
- media_id, t_width, t_height, t_type, t_method, t_len
- )
+ yield threads.deferToThread(generate_thumbnails)
+
+ for l in local_thumbnails:
+ yield self.store.store_local_thumbnail(*l)
defer.returnValue({
"width": m_width,
diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py
index f1265541ba..f74b81b7a2 100644
--- a/synapse/storage/_base.py
+++ b/synapse/storage/_base.py
@@ -58,7 +58,7 @@ cache_counter = metrics.register_cache(
class Cache(object):
- def __init__(self, name, max_entries=1000, keylen=1, lru=False):
+ def __init__(self, name, max_entries=1000, keylen=1, lru=True):
if lru:
self.cache = LruCache(max_size=max_entries)
self.max_entries = None
@@ -142,7 +142,7 @@ class CacheDescriptor(object):
which can be used to insert values into the cache specifically, without
calling the calculation function.
"""
- def __init__(self, orig, max_entries=1000, num_args=1, lru=False,
+ def __init__(self, orig, max_entries=1000, num_args=1, lru=True,
inlineCallbacks=False):
self.orig = orig
@@ -210,7 +210,7 @@ class CacheDescriptor(object):
return wrapped
-def cached(max_entries=1000, num_args=1, lru=False):
+def cached(max_entries=1000, num_args=1, lru=True):
return lambda orig: CacheDescriptor(
orig,
max_entries=max_entries,
diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py
index 4db07f6fb4..55dd3f6cfb 100644
--- a/synapse/storage/roommember.py
+++ b/synapse/storage/roommember.py
@@ -78,7 +78,7 @@ class RoomMemberStore(SQLBaseStore):
lambda events: events[0] if events else None
)
- @cached()
+ @cached(max_entries=5000)
def get_users_in_room(self, room_id):
def f(txn):
@@ -154,7 +154,7 @@ class RoomMemberStore(SQLBaseStore):
RoomsForUser(**r) for r in self.cursor_to_dict(txn)
]
- @cached()
+ @cached(max_entries=5000)
def get_joined_hosts_for_room(self, room_id):
return self.runInteraction(
"get_joined_hosts_for_room",
diff --git a/tests/rest/client/v2_alpha/test_register.py b/tests/rest/client/v2_alpha/test_register.py
new file mode 100644
index 0000000000..66fd25964d
--- /dev/null
+++ b/tests/rest/client/v2_alpha/test_register.py
@@ -0,0 +1,134 @@
+from synapse.rest.client.v2_alpha.register import RegisterRestServlet
+from synapse.api.errors import SynapseError
+from twisted.internet import defer
+from mock import Mock, MagicMock
+from tests import unittest
+import json
+
+
+class RegisterRestServletTestCase(unittest.TestCase):
+
+ def setUp(self):
+ # do the dance to hook up request data to self.request_data
+ self.request_data = ""
+ self.request = Mock(
+ content=Mock(read=Mock(side_effect=lambda: self.request_data)),
+ )
+ self.request.args = {}
+
+ self.appservice = None
+ self.auth = Mock(get_appservice_by_req=Mock(
+ side_effect=lambda x: defer.succeed(self.appservice))
+ )
+
+ self.auth_result = (False, None, None)
+ self.auth_handler = Mock(
+ check_auth=Mock(side_effect=lambda x,y,z: self.auth_result)
+ )
+ self.registration_handler = Mock()
+ self.identity_handler = Mock()
+ self.login_handler = Mock()
+
+ # do the dance to hook it up to the hs global
+ self.handlers = Mock(
+ auth_handler=self.auth_handler,
+ registration_handler=self.registration_handler,
+ identity_handler=self.identity_handler,
+ login_handler=self.login_handler
+ )
+ self.hs = Mock()
+ self.hs.hostname = "superbig~testing~thing.com"
+ self.hs.get_auth = Mock(return_value=self.auth)
+ self.hs.get_handlers = Mock(return_value=self.handlers)
+ self.hs.config.disable_registration = False
+
+ # init the thing we're testing
+ self.servlet = RegisterRestServlet(self.hs)
+
+ @defer.inlineCallbacks
+ def test_POST_appservice_registration_valid(self):
+ user_id = "@kermit:muppet"
+ token = "kermits_access_token"
+ self.request.args = {
+ "access_token": "i_am_an_app_service"
+ }
+ self.request_data = json.dumps({
+ "username": "kermit"
+ })
+ self.appservice = {
+ "id": "1234"
+ }
+ self.registration_handler.appservice_register = Mock(
+ return_value=(user_id, token)
+ )
+ result = yield self.servlet.on_POST(self.request)
+ self.assertEquals(result, (200, {
+ "user_id": user_id,
+ "access_token": token,
+ "home_server": self.hs.hostname
+ }))
+
+ @defer.inlineCallbacks
+ def test_POST_appservice_registration_invalid(self):
+ self.request.args = {
+ "access_token": "i_am_an_app_service"
+ }
+ self.request_data = json.dumps({
+ "username": "kermit"
+ })
+ self.appservice = None # no application service exists
+ result = yield self.servlet.on_POST(self.request)
+ self.assertEquals(result, (401, None))
+
+ def test_POST_bad_password(self):
+ self.request_data = json.dumps({
+ "username": "kermit",
+ "password": 666
+ })
+ d = self.servlet.on_POST(self.request)
+ return self.assertFailure(d, SynapseError)
+
+ def test_POST_bad_username(self):
+ self.request_data = json.dumps({
+ "username": 777,
+ "password": "monkey"
+ })
+ d = self.servlet.on_POST(self.request)
+ return self.assertFailure(d, SynapseError)
+
+ @defer.inlineCallbacks
+ def test_POST_user_valid(self):
+ user_id = "@kermit:muppet"
+ token = "kermits_access_token"
+ self.request_data = json.dumps({
+ "username": "kermit",
+ "password": "monkey"
+ })
+ self.registration_handler.check_username = Mock(return_value=True)
+ self.auth_result = (True, None, {
+ "username": "kermit",
+ "password": "monkey"
+ })
+ self.registration_handler.register = Mock(return_value=(user_id, token))
+
+ result = yield self.servlet.on_POST(self.request)
+ self.assertEquals(result, (200, {
+ "user_id": user_id,
+ "access_token": token,
+ "home_server": self.hs.hostname
+ }))
+
+ def test_POST_disabled_registration(self):
+ self.hs.config.disable_registration = True
+ self.request_data = json.dumps({
+ "username": "kermit",
+ "password": "monkey"
+ })
+ self.registration_handler.check_username = Mock(return_value=True)
+ self.auth_result = (True, None, {
+ "username": "kermit",
+ "password": "monkey"
+ })
+ self.registration_handler.register = Mock(return_value=("@user:id", "t"))
+ d = self.servlet.on_POST(self.request)
+ return self.assertFailure(d, SynapseError)
\ No newline at end of file
|