From 8b906b5d2f40bc44a71d05a31bf923acca7e19a2 Mon Sep 17 00:00:00 2001 From: Mateusz Mandera Date: Sun, 26 Sep 2021 18:54:45 +0200 Subject: [PATCH] request_notes: Set the realm appropriately for the root subdomain. Requests to the root subdomain weren't getting request_notes.realm set even if a realm exists on the root subdomain - which is actually a common scenario, because simply having one organization, on the root subdomain, is the simplest and common way for self-hosted deployments. --- zerver/middleware.py | 24 ++++++++++------- zerver/tests/test_decorators.py | 47 ++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/zerver/middleware.py b/zerver/middleware.py index 649b2e0cc2..037e311a84 100644 --- a/zerver/middleware.py +++ b/zerver/middleware.py @@ -576,16 +576,22 @@ class HostDomainMiddleware(MiddlewareMixin): return None subdomain = get_subdomain(request) - if ( - subdomain != Realm.SUBDOMAIN_FOR_ROOT_DOMAIN - and subdomain != settings.SOCIAL_AUTH_SUBDOMAIN - ): - request_notes = RequestNotes.get_notes(request) - try: - request_notes.realm = get_realm(subdomain) - except Realm.DoesNotExist: - return render(request, "zerver/invalid_realm.html", status=404) + if subdomain == settings.SOCIAL_AUTH_SUBDOMAIN: + # Realms are not supposed to exist on SOCIAL_AUTH_SUBDOMAIN. + return None + + request_notes = RequestNotes.get_notes(request) + try: + request_notes.realm = get_realm(subdomain) request_notes.has_fetched_realm = True + except Realm.DoesNotExist: + if subdomain == Realm.SUBDOMAIN_FOR_ROOT_DOMAIN: + # The root domain is used for creating new + # organizations even if it does not host a realm. + return None + + return render(request, "zerver/invalid_realm.html", status=404) + return None diff --git a/zerver/tests/test_decorators.py b/zerver/tests/test_decorators.py index 65207c98b1..3ee0ee7ea4 100644 --- a/zerver/tests/test_decorators.py +++ b/zerver/tests/test_decorators.py @@ -2,7 +2,7 @@ import base64 import os import re from collections import defaultdict -from typing import Any, Callable, Dict, List, Sequence, Tuple +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple from unittest import mock, skipUnless import orjson @@ -29,6 +29,7 @@ from zerver.decorator import ( from zerver.forms import OurAuthenticationForm from zerver.lib.actions import ( change_user_is_active, + do_create_realm, do_deactivate_realm, do_deactivate_user, do_reactivate_realm, @@ -2061,3 +2062,47 @@ class TestIgnoreUnhashableLRUCache(ZulipTestCase): self.assertEqual(hits, 0) self.assertEqual(misses, 0) self.assertEqual(currsize, 0) + + +class TestRequestNotes(ZulipTestCase): + def test_request_notes_realm(self) -> None: + """ + This test verifies that .realm gets set correctly on the request notes + depending on the subdomain. + """ + + def mock_home(expected_realm: Optional[Realm]) -> Callable[[HttpRequest], HttpResponse]: + def inner(request: HttpRequest) -> HttpResponse: + self.assertEqual(RequestNotes.get_notes(request).realm, expected_realm) + return HttpResponse() + + return inner + + zulip_realm = get_realm("zulip") + with mock.patch("zerver.views.home.home_real", new=mock_home(zulip_realm)): + result = self.client_get("/", subdomain="zulip") + self.assertEqual(result.status_code, 200) + + # When a request is made to the root subdomain and there is no realm on it, + # no realm can be set on the request notes. + with mock.patch("zerver.views.home.home_real", new=mock_home(None)): + result = self.client_get("/", subdomain="") + self.assertEqual(result.status_code, 200) + + root_subdomain_realm = do_create_realm("", "Root Domain") + # Now test that that realm does get set, if it exists, for requests + # to the root subdomain. + with mock.patch("zerver.views.home.home_real", new=mock_home(root_subdomain_realm)): + result = self.client_get("/", subdomain="") + self.assertEqual(result.status_code, 200) + + # Only the root subdomain allows requests to it without having a realm. + # Requests to non-root subdomains get stopped by the middleware and + # an error page is returned before the request hits the view. + with mock.patch("zerver.views.home.home_real") as mock_home_real: + result = self.client_get("/", subdomain="invalid") + self.assertEqual(result.status_code, 404) + self.assert_in_response( + "There is no Zulip organization hosted at this subdomain.", result + ) + mock_home_real.assert_not_called()