diff --git a/zerver/context_processors.py b/zerver/context_processors.py index 7aba55e633..4c8f6db340 100644 --- a/zerver/context_processors.py +++ b/zerver/context_processors.py @@ -11,6 +11,7 @@ from version import ( ZULIP_VERSION, ) from zerver.decorator import get_client_name +from zerver.lib.exceptions import InvalidSubdomainError from zerver.lib.realm_description import get_realm_rendered_description, get_realm_text_description from zerver.lib.realm_icon import get_realm_icon_url from zerver.lib.send_email import FromAddress @@ -54,6 +55,12 @@ def get_realm_from_request(request: HttpRequest) -> Optional[Realm]: request.realm = None return request.realm +def get_valid_realm_from_request(request: HttpRequest) -> Realm: + realm = get_realm_from_request(request) + if realm is None: + raise InvalidSubdomainError() + return realm + def zulip_default_context(request: HttpRequest) -> Dict[str, Any]: """Context available to all Zulip Jinja2 templates that have a request passed in. Designed to provide the long list of variables at the diff --git a/zerver/lib/exceptions.py b/zerver/lib/exceptions.py index 397eaf6019..c1cad3d84a 100644 --- a/zerver/lib/exceptions.py +++ b/zerver/lib/exceptions.py @@ -46,6 +46,7 @@ class ErrorCode(AbstractEnum): INVALID_API_KEY = () INVALID_ZOOM_TOKEN = () UNAUTHENTICATED_USER = () + NONEXISTENT_SUBDOMAIN = () class JsonableError(Exception): '''A standardized error format we can turn into a nice JSON HTTP response. @@ -277,3 +278,14 @@ class MissingAuthenticationError(JsonableError): # No msg_format is defined since this exception is caught and # converted into json_unauthorized in Zulip's middleware. + +class InvalidSubdomainError(JsonableError): + code = ErrorCode.NONEXISTENT_SUBDOMAIN + http_status_code = 404 + + def __init__(self) -> None: + pass + + @staticmethod + def msg_format() -> str: + return _("Invalid subdomain") diff --git a/zerver/tests/test_message_fetch.py b/zerver/tests/test_message_fetch.py index 2eb7924ab3..52c1f506ed 100644 --- a/zerver/tests/test_message_fetch.py +++ b/zerver/tests/test_message_fetch.py @@ -1212,10 +1212,10 @@ class GetOldMessagesTest(ZulipTestCase): "narrow": orjson.dumps([dict(operator='streams', operand="web-public")]).decode(), } - with mock.patch('zerver.views.message_fetch.get_realm_from_request', return_value=None): + with mock.patch('zerver.context_processors.get_realm', side_effect=Realm.DoesNotExist): result = self.client_get("/json/messages", dict(post_params)) - self.assert_json_error(result, "Invalid subdomain.", - status_code=400) + self.assert_json_error(result, "Invalid subdomain", + status_code=404) def test_unauthenticated_get_messages_without_web_public(self) -> None: """ diff --git a/zerver/views/message_fetch.py b/zerver/views/message_fetch.py index 37ad48897d..713960f0ea 100644 --- a/zerver/views/message_fetch.py +++ b/zerver/views/message_fetch.py @@ -27,7 +27,7 @@ from sqlalchemy.sql import ( union_all, ) -from zerver.context_processors import get_realm_from_request +from zerver.context_processors import get_valid_realm_from_request from zerver.decorator import REQ, has_request_variables from zerver.lib.actions import recipient_for_user_profiles from zerver.lib.addressee import get_user_profiles, get_user_profiles_by_ids @@ -870,10 +870,7 @@ def get_messages_backend(request: HttpRequest, if not is_web_public_compatible(narrow): return json_unauthorized() - realm = get_realm_from_request(request) - if realm is None: - return json_error(_("Invalid subdomain.")) - + realm = get_valid_realm_from_request(request) # We use None to indicate unauthenticated requests as it's more # readable than using AnonymousUser, and the lack of Django # stubs means that mypy can't check AnonymousUser well.