From 7c8e6c5fb959122d2ab8b5bcb9ecdcaeff3b6361 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 11 Aug 2021 12:30:04 +0100 Subject: [PATCH 01/13] Add UsernameAvailableRestServlet --- synapse/rest/admin/users.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/synapse/rest/admin/users.py b/synapse/rest/admin/users.py index eef76ab18a94..a81df5a39aa8 100644 --- a/synapse/rest/admin/users.py +++ b/synapse/rest/admin/users.py @@ -1097,3 +1097,28 @@ async def on_DELETE( await self.store.delete_ratelimit_for_user(user_id) return 200, {} + + +class UsernameAvailableRestServlet(RestServlet): + """An admin API to check if a given username is available, regardless of whether registration is enabled. + + Example: + GET /_synapse/admin/v1/username_available?username=foo + 200 OK + { + "available": true + } + """ + + PATTERNS = admin_patterns("/username_available") + + def __init__(self, hs: "HomeServer"): + self.auth = hs.get_auth() + self.registration_handler = hs.get_registration_handler() + + async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: + await assert_requester_is_admin(self.auth, request) + + username = parse_string(request, "username", required=True) + await self.registration_handler.check_username(username) + return 200, {"available": True} From 34494017f3aabd26570dc6bb42db2f9d6952deec Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 11 Aug 2021 12:30:16 +0100 Subject: [PATCH 02/13] Add documentation --- docs/admin_api/user_admin_api.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/admin_api/user_admin_api.md b/docs/admin_api/user_admin_api.md index 160899754ede..362ad4a4e7f1 100644 --- a/docs/admin_api/user_admin_api.md +++ b/docs/admin_api/user_admin_api.md @@ -1013,3 +1013,16 @@ The following parameters should be set in the URL: - `user_id` - The fully qualified MXID: for example, `@user:server.com`. The user must be local. +### Check username availability + +The API is: + +``` +POST /_synapse/admin/v1/username_availabile?username=$localpart +``` + +To use it, you will need to authenticate by providing an `access_token` for a +server admin: [Admin API](../usage/administration/admin_api) + + +The request and response format is the same as the [/_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available) API \ No newline at end of file From eae0a96889cf8b3987044bb3f32449e6df8f68d3 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 11 Aug 2021 12:30:25 +0100 Subject: [PATCH 03/13] Register servlet --- synapse/rest/admin/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synapse/rest/admin/__init__.py b/synapse/rest/admin/__init__.py index abf749b001ab..bc547b64e418 100644 --- a/synapse/rest/admin/__init__.py +++ b/synapse/rest/admin/__init__.py @@ -62,6 +62,7 @@ UserAdminServlet, UserMediaRestServlet, UserMembershipRestServlet, + UsernameAvailableRestServlet, UserRegisterServlet, UserRestServletV2, UsersRestServletV2, @@ -241,6 +242,7 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: ForwardExtremitiesRestServlet(hs).register(http_server) RoomEventContextServlet(hs).register(http_server) RateLimitRestServlet(hs).register(http_server) + UsernameAvailableRestServlet(hs).register(http_server) def register_servlets_for_client_rest_resource( From e865f7983fadc04ccdaa9671feb7989635ba6397 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 11 Aug 2021 12:30:33 +0100 Subject: [PATCH 04/13] Add test case --- tests/rest/admin/test_user.py | 51 ++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/tests/rest/admin/test_user.py b/tests/rest/admin/test_user.py index 42f50c092101..5265ab2af9f7 100644 --- a/tests/rest/admin/test_user.py +++ b/tests/rest/admin/test_user.py @@ -22,7 +22,12 @@ import synapse.rest.admin from synapse.api.constants import UserTypes -from synapse.api.errors import Codes, HttpResponseException, ResourceLimitError +from synapse.api.errors import ( + Codes, + HttpResponseException, + ResourceLimitError, + SynapseError, +) from synapse.api.room_versions import RoomVersions from synapse.rest.client.v1 import login, logout, profile, room from synapse.rest.client.v2_alpha import devices, sync @@ -3406,3 +3411,47 @@ def test_success(self): self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) self.assertNotIn("messages_per_second", channel.json_body) self.assertNotIn("burst_count", channel.json_body) + + +class UsernameAvailableTestCase(unittest.HomeserverTestCase): + + servlets = [ + synapse.rest.admin.register_servlets_for_client_rest_resource, + ] + + def make_homeserver(self, reactor, clock): + + self.url = "/_synapse/admin/v1/username_available" + + self.registration_handler = Mock() + + self.hs = self.setup_test_homeserver() + + return self.hs + + def test_username_available(self): + """ + The endpoint should return a 200 response if the username does not exist + """ + self.registration_handler.check_username = Mock(return_value=True) + url = "%s?username=%s" % (self.url, "foobar") + channel = self.make_request("GET", url) + + self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) + self.assertTrue(channel.json_body["available"]) + + def test_username_unavailable(self): + """ + The endpoint should return a 200 response if the username does not exist + """ + + async def taken_username(): + raise SynapseError(400, "User ID already taken.", errcode=Codes.USER_IN_USE) + + self.registration_handler.check_username = taken_username + + url = "%s?username=%s" % (self.url, "foobar") + channel = self.make_request("GET", url) + + self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) + self.assertEqual(channel.json_body["errcode"], "User ID already taken.") From ef58ca070c90a3e91ed53debf0facc306a434457 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 11 Aug 2021 12:32:10 +0100 Subject: [PATCH 05/13] changelog --- changelog.d/10578.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/10578.feature diff --git a/changelog.d/10578.feature b/changelog.d/10578.feature new file mode 100644 index 000000000000..02397f0009ee --- /dev/null +++ b/changelog.d/10578.feature @@ -0,0 +1 @@ +Add an admin API (`GET /_synapse/admin/username_available`) to check if a username is available (regardless of registration settings). \ No newline at end of file From 8d982715513c5daca2be56bfbb3ed125c4145c37 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 11 Aug 2021 13:26:09 +0100 Subject: [PATCH 06/13] Fix test --- tests/rest/admin/test_user.py | 37 +++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/tests/rest/admin/test_user.py b/tests/rest/admin/test_user.py index 5265ab2af9f7..ba6bcb9af065 100644 --- a/tests/rest/admin/test_user.py +++ b/tests/rest/admin/test_user.py @@ -3414,28 +3414,35 @@ def test_success(self): class UsernameAvailableTestCase(unittest.HomeserverTestCase): - servlets = [ - synapse.rest.admin.register_servlets_for_client_rest_resource, + synapse.rest.admin.register_servlets, + login.register_servlets, ] - def make_homeserver(self, reactor, clock): - + def prepare(self, reactor, clock, hs): + self.register_user("admin", "pass", admin=True) + self.admin_user_tok = self.login("admin", "pass") self.url = "/_synapse/admin/v1/username_available" - self.registration_handler = Mock() + async def check_username(username): + if username == "allowed": + return True + raise SynapseError(400, "User ID already taken.", errcode=Codes.USER_IN_USE) - self.hs = self.setup_test_homeserver() + handler = self.hs.get_registration_handler() + handler.check_username = check_username + def make_homeserver(self, reactor, clock): + self.hs = self.setup_test_homeserver() return self.hs def test_username_available(self): """ The endpoint should return a 200 response if the username does not exist """ - self.registration_handler.check_username = Mock(return_value=True) - url = "%s?username=%s" % (self.url, "foobar") - channel = self.make_request("GET", url) + + url = "%s?username=%s" % (self.url, "allowed") + channel = self.make_request("GET", url, None, self.admin_user_tok) self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) self.assertTrue(channel.json_body["available"]) @@ -3445,13 +3452,13 @@ def test_username_unavailable(self): The endpoint should return a 200 response if the username does not exist """ - async def taken_username(): + async def check_username(username): + print("fibble sibble") raise SynapseError(400, "User ID already taken.", errcode=Codes.USER_IN_USE) - self.registration_handler.check_username = taken_username - - url = "%s?username=%s" % (self.url, "foobar") - channel = self.make_request("GET", url) + url = "%s?username=%s" % (self.url, "disallowed") + channel = self.make_request("GET", url, None, self.admin_user_tok) self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual(channel.json_body["errcode"], "User ID already taken.") + self.assertEqual(channel.json_body["errcode"], "M_USER_IN_USE") + self.assertEqual(channel.json_body["error"], "User ID already taken.") From 9a2226fd906b7576b2878c89aea05f0a291b1e50 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 16 Aug 2021 09:01:11 +0100 Subject: [PATCH 07/13] Add a short description --- docs/admin_api/user_admin_api.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/admin_api/user_admin_api.md b/docs/admin_api/user_admin_api.md index 362ad4a4e7f1..976633af672f 100644 --- a/docs/admin_api/user_admin_api.md +++ b/docs/admin_api/user_admin_api.md @@ -1015,14 +1015,17 @@ The following parameters should be set in the URL: ### Check username availability +Checks to see if a username is available, and valid, for the server. This will +work even if registration is disabled on the server, unlike the Client-Server endpoint counterpart. + The API is: ``` POST /_synapse/admin/v1/username_availabile?username=$localpart ``` +The request and response format is the same as the [/_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available) API. + To use it, you will need to authenticate by providing an `access_token` for a server admin: [Admin API](../usage/administration/admin_api) - -The request and response format is the same as the [/_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available) API \ No newline at end of file From c92de74f53475c4004410be74db0be8c31137dac Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 16 Aug 2021 09:01:23 +0100 Subject: [PATCH 08/13] Remove unused --- tests/rest/admin/test_user.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/rest/admin/test_user.py b/tests/rest/admin/test_user.py index ba6bcb9af065..514f44298177 100644 --- a/tests/rest/admin/test_user.py +++ b/tests/rest/admin/test_user.py @@ -3452,10 +3452,6 @@ def test_username_unavailable(self): The endpoint should return a 200 response if the username does not exist """ - async def check_username(username): - print("fibble sibble") - raise SynapseError(400, "User ID already taken.", errcode=Codes.USER_IN_USE) - url = "%s?username=%s" % (self.url, "disallowed") channel = self.make_request("GET", url, None, self.admin_user_tok) From 55c50a6297f14bd5663a5579c9901b40a6e9a0d5 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 16 Aug 2021 09:01:30 +0100 Subject: [PATCH 09/13] Split out API --- synapse/rest/admin/__init__.py | 2 +- synapse/rest/admin/username_available.py | 55 ++++++++++++++++++++++++ synapse/rest/admin/users.py | 25 ----------- 3 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 synapse/rest/admin/username_available.py diff --git a/synapse/rest/admin/__init__.py b/synapse/rest/admin/__init__.py index bc547b64e418..8a91068092c0 100644 --- a/synapse/rest/admin/__init__.py +++ b/synapse/rest/admin/__init__.py @@ -51,6 +51,7 @@ ) from synapse.rest.admin.server_notice_servlet import SendServerNoticeServlet from synapse.rest.admin.statistics import UserMediaStatisticsRestServlet +from synapse.rest.admin.username_available import UsernameAvailableRestServlet from synapse.rest.admin.users import ( AccountValidityRenewServlet, DeactivateAccountRestServlet, @@ -62,7 +63,6 @@ UserAdminServlet, UserMediaRestServlet, UserMembershipRestServlet, - UsernameAvailableRestServlet, UserRegisterServlet, UserRestServletV2, UsersRestServletV2, diff --git a/synapse/rest/admin/username_available.py b/synapse/rest/admin/username_available.py new file mode 100644 index 000000000000..0753fa7d90fc --- /dev/null +++ b/synapse/rest/admin/username_available.py @@ -0,0 +1,55 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# 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 logging +from http import HTTPStatus +from typing import TYPE_CHECKING, Tuple +from synapse.http.servlet import ( + RestServlet, + parse_string, +) +from synapse.http.site import SynapseRequest +from synapse.rest.admin._base import ( + admin_patterns, + assert_requester_is_admin, +) +from synapse.types import JsonDict + +if TYPE_CHECKING: + from synapse.server import HomeServer + +logger = logging.getLogger(__name__) + +class UsernameAvailableRestServlet(RestServlet): + """An admin API to check if a given username is available, regardless of whether registration is enabled. + + Example: + GET /_synapse/admin/v1/username_available?username=foo + 200 OK + { + "available": true + } + """ + + PATTERNS = admin_patterns("/username_available") + + def __init__(self, hs: "HomeServer"): + self.auth = hs.get_auth() + self.registration_handler = hs.get_registration_handler() + + async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: + await assert_requester_is_admin(self.auth, request) + + username = parse_string(request, "username", required=True) + await self.registration_handler.check_username(username) + return HTTPStatus.OK, {"available": True} diff --git a/synapse/rest/admin/users.py b/synapse/rest/admin/users.py index a81df5a39aa8..eef76ab18a94 100644 --- a/synapse/rest/admin/users.py +++ b/synapse/rest/admin/users.py @@ -1097,28 +1097,3 @@ async def on_DELETE( await self.store.delete_ratelimit_for_user(user_id) return 200, {} - - -class UsernameAvailableRestServlet(RestServlet): - """An admin API to check if a given username is available, regardless of whether registration is enabled. - - Example: - GET /_synapse/admin/v1/username_available?username=foo - 200 OK - { - "available": true - } - """ - - PATTERNS = admin_patterns("/username_available") - - def __init__(self, hs: "HomeServer"): - self.auth = hs.get_auth() - self.registration_handler = hs.get_registration_handler() - - async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: - await assert_requester_is_admin(self.auth, request) - - username = parse_string(request, "username", required=True) - await self.registration_handler.check_username(username) - return 200, {"available": True} From fd68923f176f0d214ff27d5e468945f150b396b3 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 16 Aug 2021 09:04:22 +0100 Subject: [PATCH 10/13] Move tests --- tests/rest/admin/test_user.py | 47 -------------- tests/rest/admin/test_username_available.py | 68 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 47 deletions(-) create mode 100644 tests/rest/admin/test_username_available.py diff --git a/tests/rest/admin/test_user.py b/tests/rest/admin/test_user.py index 514f44298177..e1958a292077 100644 --- a/tests/rest/admin/test_user.py +++ b/tests/rest/admin/test_user.py @@ -3411,50 +3411,3 @@ def test_success(self): self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) self.assertNotIn("messages_per_second", channel.json_body) self.assertNotIn("burst_count", channel.json_body) - - -class UsernameAvailableTestCase(unittest.HomeserverTestCase): - servlets = [ - synapse.rest.admin.register_servlets, - login.register_servlets, - ] - - def prepare(self, reactor, clock, hs): - self.register_user("admin", "pass", admin=True) - self.admin_user_tok = self.login("admin", "pass") - self.url = "/_synapse/admin/v1/username_available" - - async def check_username(username): - if username == "allowed": - return True - raise SynapseError(400, "User ID already taken.", errcode=Codes.USER_IN_USE) - - handler = self.hs.get_registration_handler() - handler.check_username = check_username - - def make_homeserver(self, reactor, clock): - self.hs = self.setup_test_homeserver() - return self.hs - - def test_username_available(self): - """ - The endpoint should return a 200 response if the username does not exist - """ - - url = "%s?username=%s" % (self.url, "allowed") - channel = self.make_request("GET", url, None, self.admin_user_tok) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - self.assertTrue(channel.json_body["available"]) - - def test_username_unavailable(self): - """ - The endpoint should return a 200 response if the username does not exist - """ - - url = "%s?username=%s" % (self.url, "disallowed") - channel = self.make_request("GET", url, None, self.admin_user_tok) - - self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual(channel.json_body["errcode"], "M_USER_IN_USE") - self.assertEqual(channel.json_body["error"], "User ID already taken.") diff --git a/tests/rest/admin/test_username_available.py b/tests/rest/admin/test_username_available.py new file mode 100644 index 000000000000..b32154364ab8 --- /dev/null +++ b/tests/rest/admin/test_username_available.py @@ -0,0 +1,68 @@ +# Copyright 2021 The Matrix.org Foundation C.I.C. +# +# 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 synapse.rest.admin +from synapse.api.errors import ( + Codes, + SynapseError, +) +from synapse.rest.client.v1 import login + +from tests import unittest + +class UsernameAvailableTestCase(unittest.HomeserverTestCase): + servlets = [ + synapse.rest.admin.register_servlets, + login.register_servlets, + ] + + def prepare(self, reactor, clock, hs): + self.register_user("admin", "pass", admin=True) + self.admin_user_tok = self.login("admin", "pass") + self.url = "/_synapse/admin/v1/username_available" + + async def check_username(username): + if username == "allowed": + return True + raise SynapseError(400, "User ID already taken.", errcode=Codes.USER_IN_USE) + + handler = self.hs.get_registration_handler() + handler.check_username = check_username + + def make_homeserver(self, reactor, clock): + self.hs = self.setup_test_homeserver() + return self.hs + + def test_username_available(self): + """ + The endpoint should return a 200 response if the username does not exist + """ + + url = "%s?username=%s" % (self.url, "allowed") + channel = self.make_request("GET", url, None, self.admin_user_tok) + + self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) + self.assertTrue(channel.json_body["available"]) + + def test_username_unavailable(self): + """ + The endpoint should return a 200 response if the username does not exist + """ + + url = "%s?username=%s" % (self.url, "disallowed") + channel = self.make_request("GET", url, None, self.admin_user_tok) + + self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) + self.assertEqual(channel.json_body["errcode"], "M_USER_IN_USE") + self.assertEqual(channel.json_body["error"], "User ID already taken.") From 3c7b0745ee811965836aa6583c0616e8ac715a6d Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 16 Aug 2021 12:39:27 +0100 Subject: [PATCH 11/13] isort isort isort --- tests/rest/admin/test_user.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/rest/admin/test_user.py b/tests/rest/admin/test_user.py index c66b1a1769c2..13fab5579b8c 100644 --- a/tests/rest/admin/test_user.py +++ b/tests/rest/admin/test_user.py @@ -25,11 +25,7 @@ import synapse.rest.admin from synapse.api.constants import UserTypes -from synapse.api.errors import ( - Codes, - HttpResponseException, - ResourceLimitError, -) +from synapse.api.errors import Codes, HttpResponseException, ResourceLimitError from synapse.api.room_versions import RoomVersions from synapse.rest.client.v1 import login, logout, profile, room from synapse.rest.client.v2_alpha import devices, sync From 9a506c8fa4a3bbc02e4aee1e293819064b073c43 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 16 Aug 2021 16:09:10 +0100 Subject: [PATCH 12/13] Update docs/admin_api/user_admin_api.md to include a link for more info Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- docs/admin_api/user_admin_api.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/admin_api/user_admin_api.md b/docs/admin_api/user_admin_api.md index 45ef109992e9..4b5dd4685ab0 100644 --- a/docs/admin_api/user_admin_api.md +++ b/docs/admin_api/user_admin_api.md @@ -1060,8 +1060,12 @@ The following parameters should be set in the URL: ### Check username availability -Checks to see if a username is available, and valid, for the server. This will -work even if registration is disabled on the server, unlike the Client-Server endpoint counterpart. +Checks to see if a username is available, and valid, for the server. See [the client-server +API](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-register-available) +for more information. + +This endpoint will work even if registration is disabled on the server, unlike +`/_matrix/client/r0/register/available`. The API is: From 474068a2090e5c5843f89ae7c026462dbc8d79cb Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 16 Aug 2021 16:11:19 +0100 Subject: [PATCH 13/13] Remove pointless override, move url to module constant --- tests/rest/admin/test_username_available.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/rest/admin/test_username_available.py b/tests/rest/admin/test_username_available.py index 5884b79dfae3..53cbc8ddabaa 100644 --- a/tests/rest/admin/test_username_available.py +++ b/tests/rest/admin/test_username_available.py @@ -24,11 +24,11 @@ class UsernameAvailableTestCase(unittest.HomeserverTestCase): synapse.rest.admin.register_servlets, login.register_servlets, ] + url = "/_synapse/admin/v1/username_available" def prepare(self, reactor, clock, hs): self.register_user("admin", "pass", admin=True) self.admin_user_tok = self.login("admin", "pass") - self.url = "/_synapse/admin/v1/username_available" async def check_username(username): if username == "allowed": @@ -38,10 +38,6 @@ async def check_username(username): handler = self.hs.get_registration_handler() handler.check_username = check_username - def make_homeserver(self, reactor, clock): - self.hs = self.setup_test_homeserver() - return self.hs - def test_username_available(self): """ The endpoint should return a 200 response if the username does not exist