Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

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

Merged
merged 13 commits into from
Jun 5, 2020
1 change: 1 addition & 0 deletions changelog.d/7263.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create user with admin API if MAU limit is reached.
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 3 additions & 1 deletion synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ def register_user(
"""
yield self.check_registration_ratelimit(address)

yield self.auth.check_auth_blocking(threepid=threepid)
# do not check_auth_blocking if the call is coming through the Admin API
if address:
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
yield self.auth.check_auth_blocking(threepid=threepid)
password_hash = None
if password:
password_hash = yield self._auth_handler.hash(password)
Expand Down
9 changes: 7 additions & 2 deletions tests/handlers/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,17 @@ def test_register_mau_blocked(self):
return_value=defer.succeed(self.lots_of_users)
)
self.get_failure(
self.handler.register_user(localpart="local_part"), ResourceLimitError
# for MAU it must have an IP address used to perform the registration
self.handler.register_user(localpart="local_part", address="127.0.0.1"),
ResourceLimitError,
)

self.store.get_monthly_active_count = Mock(
return_value=defer.succeed(self.hs.config.max_mau_value)
)
self.get_failure(
self.handler.register_user(localpart="local_part"), ResourceLimitError
self.handler.register_user(localpart="local_part", address="127.0.0.1"),
ResourceLimitError,
)

def test_auto_create_auto_join_rooms(self):
Expand Down Expand Up @@ -288,10 +291,12 @@ def get_or_create_user(self, requester, localpart, displayname, password_hash=No
token = self.macaroon_generator.generate_access_token(user_id)

if need_register:
# for MAU it must have an IP address used to perform the registration
yield self.handler.register_with_store(
user_id=user_id,
password_hash=password_hash,
create_profile_with_displayname=user.localpart,
address="127.0.0.1",
)
else:
yield self.hs.get_auth_handler().delete_access_tokens_for_user(user_id)
Expand Down
174 changes: 172 additions & 2 deletions tests/rest/admin/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@

from mock import Mock

from twisted.internet import defer

import synapse.rest.admin
from synapse.api.constants import UserTypes
from synapse.api.errors import Codes, HttpResponseException, ResourceLimitError
from synapse.rest.client.v1 import login
from synapse.rest.client.v2_alpha import sync

from tests import unittest

Expand Down Expand Up @@ -320,6 +324,62 @@ def nonce():
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("Invalid user type", channel.json_body["error"])

def test_register_limit_mau(self):
"""
Register user if MAU limit is reached.
"""
handler = self.hs.get_registration_handler()
store = self.hs.get_datastore()

self.hs.config.limit_usage_by_mau = True
self.hs.config.max_mau_value = 2
self.hs.config.mau_trial_days = 0

# Set MAU limit
store.get_monthly_active_count = Mock(
return_value=defer.succeed(self.hs.config.max_mau_value + 1)
)
self.get_failure(
# for MAU it must have an IP address used to perform the registration
handler.register_user(localpart="local_part", address="127.0.0.1"),
ResourceLimitError,
)

store.get_monthly_active_count = Mock(
return_value=defer.succeed(self.hs.config.max_mau_value + 1)
)
self.get_failure(
handler.register_user(localpart="local_part", address="127.0.0.1"),
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):

Expand All @@ -344,7 +404,7 @@ def test_no_auth(self):
self.render(request)

self.assertEqual(401, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual("M_MISSING_TOKEN", channel.json_body["errcode"])
self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"])
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

def test_all_users(self):
"""
Expand All @@ -367,6 +427,7 @@ class UserRestTestCase(unittest.HomeserverTestCase):
servlets = [
synapse.rest.admin.register_servlets,
login.register_servlets,
sync.register_servlets,
]

def prepare(self, reactor, clock, hs):
Expand Down Expand Up @@ -418,7 +479,7 @@ def test_user_does_not_exist(self):
self.render(request)

self.assertEqual(404, channel.code, msg=channel.json_body)
self.assertEqual("M_NOT_FOUND", channel.json_body["errcode"])
self.assertEqual(Codes.NOT_FOUND, channel.json_body["errcode"])
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

def test_create_server_admin(self):
"""
Expand Down Expand Up @@ -514,6 +575,115 @@ def test_create_user(self):
self.assertEqual(False, channel.json_body["is_guest"])
self.assertEqual(False, channel.json_body["deactivated"])

def test_create_user_limit_mau_active_admin(self):
"""
Check that a new regular user is created successfully if MAU limit is reached.
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
Admin user was active before creating user.
"""
self.hs.config.registration_shared_secret = None
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

handler = self.hs.get_registration_handler()

self.hs.config.limit_usage_by_mau = True
self.hs.config.max_mau_value = 2
self.hs.config.mau_trial_days = 0

def _do_sync_for_user(token):
request, channel = self.make_request("GET", "/sync", access_token=token)
self.render(request)

if channel.code != 200:
raise HttpResponseException(
channel.code, channel.result["reason"], channel.result["body"]
).to_synapse_error()

# Sync to set admin user to active
_do_sync_for_user(self.admin_user_tok)
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

self.store.get_monthly_active_count = Mock(
return_value=defer.succeed(self.hs.config.max_mau_value + 1)
)

# Set MAU limit
self.get_failure(
# for MAU it must have an IP address used to perform the registration
handler.register_user(localpart="local_part", address="127.0.0.1"),
ResourceLimitError,
)

self.store.get_monthly_active_count = Mock(
return_value=defer.succeed(self.hs.config.max_mau_value + 1)
)
self.get_failure(
handler.register_user(localpart="local_part", address="127.0.0.1"),
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"])

def test_create_user_limit_mau_passiv_admin(self):
"""
Check that a new regular user is created successfully if MAU limit is reached.
Admin user was not active before creating user and creation fails.
"""
self.hs.config.registration_shared_secret = None

handler = self.hs.get_registration_handler()

self.hs.config.limit_usage_by_mau = True
self.hs.config.max_mau_value = 2
self.hs.config.mau_trial_days = 0

# Set MAU limit
self.store.get_monthly_active_count = Mock(
return_value=defer.succeed(self.hs.config.max_mau_value + 1)
)
self.get_failure(
# for MAU it must have an IP address used to perform the registration
handler.register_user(localpart="local_part", address="127.0.0.1"),
ResourceLimitError,
)

self.store.get_monthly_active_count = Mock(
return_value=defer.succeed(self.hs.config.max_mau_value + 1)
)
self.get_failure(
handler.register_user(localpart="local_part", address="127.0.0.1"),
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(500, int(channel.result["code"]), msg=channel.result["body"])

def test_set_password(self):
"""
Test setting a new password for another user.
Expand Down