Allow new users to be registered via the admin API even if the monthly active user limit has been reached (#7263)

This commit is contained in:
Dirk Klimpel 2020-06-05 14:08:49 +02:00 committed by GitHub
parent 2970ce8367
commit 908f9e2d24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 172 additions and 16 deletions

1
changelog.d/7263.bugfix Normal file
View file

@ -0,0 +1 @@
Allow new users to be registered via the admin API even if the monthly active user limit has been reached. Contributed by @dkimpel.

View file

@ -150,6 +150,7 @@ class RegistrationHandler(BaseHandler):
default_display_name=None, default_display_name=None,
address=None, address=None,
bind_emails=[], bind_emails=[],
by_admin=False,
): ):
"""Registers a new client on the server. """Registers a new client on the server.
@ -165,6 +166,8 @@ class RegistrationHandler(BaseHandler):
will be set to this. Defaults to 'localpart'. will be set to this. Defaults to 'localpart'.
address (str|None): the IP address used to perform the registration. address (str|None): the IP address used to perform the registration.
bind_emails (List[str]): list of emails to bind to this account. bind_emails (List[str]): list of emails to bind to this account.
by_admin (bool): True if this registration is being made via the
admin api, otherwise False.
Returns: Returns:
Deferred[str]: user_id Deferred[str]: user_id
Raises: Raises:
@ -172,6 +175,8 @@ class RegistrationHandler(BaseHandler):
""" """
yield self.check_registration_ratelimit(address) yield self.check_registration_ratelimit(address)
# do not check_auth_blocking if the call is coming through the Admin API
if not by_admin:
yield self.auth.check_auth_blocking(threepid=threepid) yield self.auth.check_auth_blocking(threepid=threepid)
if localpart is not None: if localpart is not None:

View file

@ -270,6 +270,7 @@ class UserRestServletV2(RestServlet):
admin=bool(admin), admin=bool(admin),
default_display_name=displayname, default_display_name=displayname,
user_type=user_type, user_type=user_type,
by_admin=True,
) )
if "threepids" in body: if "threepids" in body:
@ -432,6 +433,7 @@ class UserRegisterServlet(RestServlet):
password_hash=password_hash, password_hash=password_hash,
admin=bool(admin), admin=bool(admin),
user_type=user_type, user_type=user_type,
by_admin=True,
) )
result = await register._create_registration_details(user_id, body) result = await register._create_registration_details(user_id, body)

View file

@ -22,9 +22,12 @@ from mock import Mock
import synapse.rest.admin import synapse.rest.admin
from synapse.api.constants import UserTypes from synapse.api.constants import UserTypes
from synapse.api.errors import HttpResponseException, ResourceLimitError
from synapse.rest.client.v1 import login from synapse.rest.client.v1 import login
from synapse.rest.client.v2_alpha import sync
from tests import unittest from tests import unittest
from tests.unittest import override_config
class UserRegisterTestCase(unittest.HomeserverTestCase): class UserRegisterTestCase(unittest.HomeserverTestCase):
@ -320,6 +323,52 @@ class UserRegisterTestCase(unittest.HomeserverTestCase):
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("Invalid user type", channel.json_body["error"]) self.assertEqual("Invalid user type", channel.json_body["error"])
@override_config(
{"limit_usage_by_mau": True, "max_mau_value": 2, "mau_trial_days": 0}
)
def test_register_mau_limit_reached(self):
"""
Check we can register a user via the shared secret registration API
even if the MAU limit is reached.
"""
handler = self.hs.get_registration_handler()
store = self.hs.get_datastore()
# Set monthly active users to the limit
store.get_monthly_active_count = Mock(return_value=self.hs.config.max_mau_value)
# Check that the blocking of monthly active users is working as expected
# The registration of a new user fails due to the limit
self.get_failure(
handler.register_user(localpart="local_part"), ResourceLimitError
)
# Register new user with admin API
request, channel = self.make_request("GET", self.url)
self.render(request)
nonce = channel.json_body["nonce"]
want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
want_mac.update(
nonce.encode("ascii") + b"\x00bob\x00abc123\x00admin\x00support"
)
want_mac = want_mac.hexdigest()
body = json.dumps(
{
"nonce": nonce,
"username": "bob",
"password": "abc123",
"admin": True,
"user_type": UserTypes.SUPPORT,
"mac": want_mac,
}
)
request, channel = self.make_request("POST", self.url, body.encode("utf8"))
self.render(request)
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("@bob:test", channel.json_body["user_id"])
class UsersListTestCase(unittest.HomeserverTestCase): class UsersListTestCase(unittest.HomeserverTestCase):
@ -368,6 +417,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
servlets = [ servlets = [
synapse.rest.admin.register_servlets, synapse.rest.admin.register_servlets,
login.register_servlets, login.register_servlets,
sync.register_servlets,
] ]
def prepare(self, reactor, clock, hs): def prepare(self, reactor, clock, hs):
@ -386,7 +436,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
If the user is not a server admin, an error is returned. If the user is not a server admin, an error is returned.
""" """
self.hs.config.registration_shared_secret = None
url = "/_synapse/admin/v2/users/@bob:test" url = "/_synapse/admin/v2/users/@bob:test"
request, channel = self.make_request( request, channel = self.make_request(
@ -409,7 +458,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
Tests that a lookup for a user that does not exist returns a 404 Tests that a lookup for a user that does not exist returns a 404
""" """
self.hs.config.registration_shared_secret = None
request, channel = self.make_request( request, channel = self.make_request(
"GET", "GET",
@ -425,7 +473,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
Check that a new admin user is created successfully. Check that a new admin user is created successfully.
""" """
self.hs.config.registration_shared_secret = None
url = "/_synapse/admin/v2/users/@bob:test" url = "/_synapse/admin/v2/users/@bob:test"
# Create user (server admin) # Create user (server admin)
@ -473,7 +520,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
Check that a new regular user is created successfully. Check that a new regular user is created successfully.
""" """
self.hs.config.registration_shared_secret = None
url = "/_synapse/admin/v2/users/@bob:test" url = "/_synapse/admin/v2/users/@bob:test"
# Create user # Create user
@ -516,14 +562,114 @@ class UserRestTestCase(unittest.HomeserverTestCase):
self.assertEqual(False, channel.json_body["is_guest"]) self.assertEqual(False, channel.json_body["is_guest"])
self.assertEqual(False, channel.json_body["deactivated"]) self.assertEqual(False, channel.json_body["deactivated"])
@override_config(
{"limit_usage_by_mau": True, "max_mau_value": 2, "mau_trial_days": 0}
)
def test_create_user_mau_limit_reached_active_admin(self):
"""
Check that an admin can register a new user via the admin API
even if the MAU limit is reached.
Admin user was active before creating user.
"""
handler = self.hs.get_registration_handler()
# Sync to set admin user to active
# before limit of monthly active users is reached
request, channel = self.make_request(
"GET", "/sync", access_token=self.admin_user_tok
)
self.render(request)
if channel.code != 200:
raise HttpResponseException(
channel.code, channel.result["reason"], channel.result["body"]
)
# Set monthly active users to the limit
self.store.get_monthly_active_count = Mock(
return_value=self.hs.config.max_mau_value
)
# Check that the blocking of monthly active users is working as expected
# The registration of a new user fails due to the limit
self.get_failure(
handler.register_user(localpart="local_part"), ResourceLimitError
)
# Register new user with admin API
url = "/_synapse/admin/v2/users/@bob:test"
# Create user
body = json.dumps({"password": "abc123", "admin": False})
request, channel = self.make_request(
"PUT",
url,
access_token=self.admin_user_tok,
content=body.encode(encoding="utf_8"),
)
self.render(request)
self.assertEqual(201, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("@bob:test", channel.json_body["name"])
self.assertEqual(False, channel.json_body["admin"])
@override_config(
{"limit_usage_by_mau": True, "max_mau_value": 2, "mau_trial_days": 0}
)
def test_create_user_mau_limit_reached_passive_admin(self):
"""
Check that an admin can register a new user via the admin API
even if the MAU limit is reached.
Admin user was not active before creating user.
"""
handler = self.hs.get_registration_handler()
# Set monthly active users to the limit
self.store.get_monthly_active_count = Mock(
return_value=self.hs.config.max_mau_value
)
# Check that the blocking of monthly active users is working as expected
# The registration of a new user fails due to the limit
self.get_failure(
handler.register_user(localpart="local_part"), ResourceLimitError
)
# Register new user with admin API
url = "/_synapse/admin/v2/users/@bob:test"
# Create user
body = json.dumps({"password": "abc123", "admin": False})
request, channel = self.make_request(
"PUT",
url,
access_token=self.admin_user_tok,
content=body.encode(encoding="utf_8"),
)
self.render(request)
# Admin user is not blocked by mau anymore
self.assertEqual(201, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("@bob:test", channel.json_body["name"])
self.assertEqual(False, channel.json_body["admin"])
@override_config(
{
"email": {
"enable_notifs": True,
"notif_for_new_users": True,
"notif_from": "test@example.com",
},
"public_baseurl": "https://example.com",
}
)
def test_create_user_email_notif_for_new_users(self): def test_create_user_email_notif_for_new_users(self):
""" """
Check that a new regular user is created successfully and Check that a new regular user is created successfully and
got an email pusher. got an email pusher.
""" """
self.hs.config.registration_shared_secret = None
self.hs.config.email_enable_notifs = True
self.hs.config.email_notif_for_new_users = True
url = "/_synapse/admin/v2/users/@bob:test" url = "/_synapse/admin/v2/users/@bob:test"
# Create user # Create user
@ -554,14 +700,21 @@ class UserRestTestCase(unittest.HomeserverTestCase):
self.assertEqual(len(pushers), 1) self.assertEqual(len(pushers), 1)
self.assertEqual("@bob:test", pushers[0]["user_name"]) self.assertEqual("@bob:test", pushers[0]["user_name"])
@override_config(
{
"email": {
"enable_notifs": False,
"notif_for_new_users": False,
"notif_from": "test@example.com",
},
"public_baseurl": "https://example.com",
}
)
def test_create_user_email_no_notif_for_new_users(self): def test_create_user_email_no_notif_for_new_users(self):
""" """
Check that a new regular user is created successfully and Check that a new regular user is created successfully and
got not an email pusher. got not an email pusher.
""" """
self.hs.config.registration_shared_secret = None
self.hs.config.email_enable_notifs = False
self.hs.config.email_notif_for_new_users = False
url = "/_synapse/admin/v2/users/@bob:test" url = "/_synapse/admin/v2/users/@bob:test"
# Create user # Create user
@ -595,7 +748,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
Test setting a new password for another user. Test setting a new password for another user.
""" """
self.hs.config.registration_shared_secret = None
# Change password # Change password
body = json.dumps({"password": "hahaha"}) body = json.dumps({"password": "hahaha"})
@ -614,7 +766,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
Test setting the displayname of another user. Test setting the displayname of another user.
""" """
self.hs.config.registration_shared_secret = None
# Modify user # Modify user
body = json.dumps({"displayname": "foobar"}) body = json.dumps({"displayname": "foobar"})
@ -645,7 +796,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
Test setting threepid for an other user. Test setting threepid for an other user.
""" """
self.hs.config.registration_shared_secret = None
# Delete old and add new threepid to user # Delete old and add new threepid to user
body = json.dumps( body = json.dumps(
@ -711,7 +861,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
""" """
Test setting the admin flag on a user. Test setting the admin flag on a user.
""" """
self.hs.config.registration_shared_secret = None
# Set a user as an admin # Set a user as an admin
body = json.dumps({"admin": True}) body = json.dumps({"admin": True})
@ -743,7 +892,6 @@ class UserRestTestCase(unittest.HomeserverTestCase):
Ensure an account can't accidentally be deactivated by using a str value Ensure an account can't accidentally be deactivated by using a str value
for the deactivated body parameter for the deactivated body parameter
""" """
self.hs.config.registration_shared_secret = None
url = "/_synapse/admin/v2/users/@bob:test" url = "/_synapse/admin/v2/users/@bob:test"
# Create user # Create user