register_remote_server: Don't allow duplicate hostnames.

This requires a bit of restructuring of the existing code to handle all
the cases correctly.
This commit is contained in:
Mateusz Mandera 2024-01-26 17:06:12 +01:00 committed by Tim Abbott
parent 5e2d620d9f
commit 25f47bd749
2 changed files with 53 additions and 16 deletions

View File

@ -4909,6 +4909,32 @@ class PushBouncerSignupTest(ZulipTestCase):
result, f"Zulip server auth failure: key does not match role {zulip_org_id}" result, f"Zulip server auth failure: key does not match role {zulip_org_id}"
) )
def test_register_duplicate_hostname(self) -> None:
zulip_org_id = str(uuid.uuid4())
zulip_org_key = get_random_string(64)
request = dict(
zulip_org_id=zulip_org_id,
zulip_org_key=zulip_org_key,
hostname="example.com",
contact_email="server-admin@zulip.com",
)
result = self.client_post("/api/v1/remotes/server/register", request)
self.assert_json_success(result)
server = RemoteZulipServer.objects.get(uuid=zulip_org_id)
self.assertEqual(server.hostname, "example.com")
new_zulip_org_id = str(uuid.uuid4())
new_zulip_org_key = get_random_string(64)
request = dict(
zulip_org_id=new_zulip_org_id,
zulip_org_key=new_zulip_org_key,
hostname="example.com",
contact_email="server-admin@zulip.com",
)
result = self.client_post("/api/v1/remotes/server/register", request)
self.assert_json_error(result, "A server with hostname example.com already exists")
class TestUserPushIdentityCompat(ZulipTestCase): class TestUserPushIdentityCompat(ZulipTestCase):
def test_filter_q(self) -> None: def test_filter_q(self) -> None:

View File

@ -137,29 +137,40 @@ def register_remote_server(
except ValidationError as e: except ValidationError as e:
raise JsonableError(e.message) raise JsonableError(e.message)
with transaction.atomic(): try:
remote_server, created = RemoteZulipServer.objects.get_or_create( remote_server = RemoteZulipServer.objects.get(uuid=zulip_org_id)
uuid=zulip_org_id, except RemoteZulipServer.DoesNotExist:
defaults={ remote_server = None
"hostname": hostname,
"contact_email": contact_email, if remote_server is not None:
"api_key": zulip_org_key, if not constant_time_compare(remote_server.api_key, zulip_org_key):
"last_request_datetime": timezone_now(), raise InvalidZulipServerKeyError(zulip_org_id)
},
if remote_server.deactivated:
raise RemoteServerDeactivatedError
if remote_server is None and RemoteZulipServer.objects.filter(hostname=hostname).exists():
raise JsonableError(
_("A server with hostname {hostname} already exists").format(hostname=hostname)
) )
if created:
with transaction.atomic():
if remote_server is None:
created = True
remote_server = RemoteZulipServer.objects.create(
uuid=zulip_org_id,
hostname=hostname,
contact_email=contact_email,
api_key=zulip_org_key,
last_request_datetime=timezone_now(),
)
RemoteZulipServerAuditLog.objects.create( RemoteZulipServerAuditLog.objects.create(
event_type=RemoteZulipServerAuditLog.REMOTE_SERVER_CREATED, event_type=RemoteZulipServerAuditLog.REMOTE_SERVER_CREATED,
server=remote_server, server=remote_server,
event_time=remote_server.last_updated, event_time=remote_server.last_updated,
) )
else: else:
if not constant_time_compare(remote_server.api_key, zulip_org_key): created = False
raise InvalidZulipServerKeyError(zulip_org_id)
if remote_server.deactivated:
raise RemoteServerDeactivatedError
remote_server.hostname = hostname remote_server.hostname = hostname
remote_server.contact_email = contact_email remote_server.contact_email = contact_email
if new_org_key is not None: if new_org_key is not None: