Implement username availability checker
Outlined here: https://github.com/vector-im/riot-web/issues/3605#issuecomment-298679388
```HTTP
GET /_matrix/.../register/available
{
"username": "desiredlocalpart123"
}
```
If available, the response looks like
```HTTP
HTTP/1.1 200 OK
{
"available": true
}
```
Otherwise,
```HTTP
HTTP/1.1 429
{
"errcode": "M_LIMIT_EXCEEDED",
"error": "Too Many Requests",
"retry_after_ms": 2000
}
```
or
```HTTP
HTTP/1.1 400
{
"errcode": "M_USER_IN_USE",
"error": "User ID already taken."
}
```
or
```HTTP
HTTP/1.1 400
{
"errcode": "M_INVALID_USERNAME",
"error": "Some reason for username being invalid"
}
```
1 files changed, 36 insertions, 0 deletions
diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py
index 3acf4eacdd..49fee04ec8 100644
--- a/synapse/rest/client/v2_alpha/register.py
+++ b/synapse/rest/client/v2_alpha/register.py
@@ -31,6 +31,7 @@ import logging
import hmac
from hashlib import sha1
from synapse.util.async import run_on_reactor
+from synapse.util.ratelimitutils import FederationRateLimiter
# We ought to be using hmac.compare_digest() but on older pythons it doesn't
@@ -115,6 +116,40 @@ class MsisdnRegisterRequestTokenRestServlet(RestServlet):
defer.returnValue((200, ret))
+class UsernameAvailabilityRestServlet(RestServlet):
+ PATTERNS = client_v2_patterns("/register/available")
+
+ def __init__(self, hs):
+ """
+ Args:
+ hs (synapse.server.HomeServer): server
+ """
+ super(UsernameAvailabilityRestServlet, self).__init__()
+ self.hs = hs
+ self.registration_handler = hs.get_handlers().registration_handler
+ self.ratelimiter = FederationRateLimiter(
+ hs.get_clock(),
+ window_size=2000, # Time window of 2s
+ sleep_limit=1, # Artificially delay requests if rate > sleep_limit/window_size
+ sleep_msec=1000, # Amount of artificial delay to apply
+ reject_limit=1, # Error with 429 if more than reject_limit requests are queued
+ concurrent_requests=1, # Allow 1 request at a time
+ )
+
+ @defer.inlineCallbacks
+ def on_GET(self, request):
+ ip = self.hs.get_ip_from_request(request)
+ with self.ratelimiter.ratelimit(ip) as wait_deferred:
+ yield wait_deferred
+
+ body = parse_json_object_from_request(request)
+ assert_params_in_request(body, ['username'])
+
+ yield self.registration_handler.check_username(body['username'])
+
+ defer.returnValue((200, {"available": True}))
+
+
class RegisterRestServlet(RestServlet):
PATTERNS = client_v2_patterns("/register$")
@@ -555,4 +590,5 @@ class RegisterRestServlet(RestServlet):
def register_servlets(hs, http_server):
EmailRegisterRequestTokenRestServlet(hs).register(http_server)
MsisdnRegisterRequestTokenRestServlet(hs).register(http_server)
+ UsernameAvailabilityRestServlet(hs).register(http_server)
RegisterRestServlet(hs).register(http_server)
|