mirror of https://github.com/zulip/zulip.git
middleware: Make HostDomain into a process_request, not process_response.
It is more suited for `process_request`, since it should stop execution of the request if the domain is invalid. This code was likely added as a process_response (inea39fb2556
) because there was already a process_response at the time (added7e786d5426
, and no longer necessary sincedce6b4a40f
). It quiets an unnecessary warning when logging in at a non-existent realm. This stops performing unnecessary work when we are going to throw it away and return a 404. The edge case to this is if the request _creates_ a realm, and is made using the URL of the new realm; this change would prevent the request before it occurs. While this does arise in tests, the tests do not reflect reality -- real requests to /accounts/register/ are made via POST to the same (default) realm, redirected there from `confirm-preregistrationuser`. The tests are adjusted to reflect real behavior. Tweaked by tabbott to add a block comment in HostDomainMiddleware.
This commit is contained in:
parent
816f91ae27
commit
f00ff1ef62
|
@ -343,12 +343,7 @@ class OurAuthenticationForm(AuthenticationForm):
|
||||||
|
|
||||||
if username is not None and password:
|
if username is not None and password:
|
||||||
subdomain = get_subdomain(self.request)
|
subdomain = get_subdomain(self.request)
|
||||||
try:
|
|
||||||
realm = get_realm(subdomain)
|
realm = get_realm(subdomain)
|
||||||
except Realm.DoesNotExist:
|
|
||||||
logging.warning("User %s attempted password login to nonexistent subdomain %s",
|
|
||||||
username, subdomain)
|
|
||||||
raise ValidationError("Realm does not exist")
|
|
||||||
|
|
||||||
return_data: Dict[str, Any] = {}
|
return_data: Dict[str, Any] = {}
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -5,7 +5,6 @@ import traceback
|
||||||
from typing import Any, AnyStr, Callable, Dict, Iterable, List, MutableMapping, Optional, Union
|
from typing import Any, AnyStr, Callable, Dict, Iterable, List, MutableMapping, Optional, Union
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import DisallowedHost
|
|
||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.http import HttpRequest, HttpResponse, StreamingHttpResponse
|
from django.http import HttpRequest, HttpResponse, StreamingHttpResponse
|
||||||
|
@ -385,32 +384,28 @@ class FlushDisplayRecipientCache(MiddlewareMixin):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
class HostDomainMiddleware(MiddlewareMixin):
|
class HostDomainMiddleware(MiddlewareMixin):
|
||||||
def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse:
|
def process_request(self, request: HttpRequest) -> Optional[HttpResponse]:
|
||||||
if getattr(response, "asynchronous", False):
|
# Match against ALLOWED_HOSTS, which is rather permissive;
|
||||||
# This special Tornado "asynchronous" response is
|
# failure will raise DisallowedHost, which is a 400.
|
||||||
# discarded after going through this code path as Tornado
|
|
||||||
# intends to block, so we stop here to avoid unnecessary work.
|
|
||||||
return response
|
|
||||||
|
|
||||||
try:
|
|
||||||
request.get_host()
|
request.get_host()
|
||||||
except DisallowedHost:
|
|
||||||
# If we get a DisallowedHost exception trying to access
|
|
||||||
# the host, (1) the request is failed anyway and so the
|
|
||||||
# below code will do nothing, and (2) the below will
|
|
||||||
# trigger a recursive exception, breaking things, so we
|
|
||||||
# just return here.
|
|
||||||
return response
|
|
||||||
|
|
||||||
if (not request.path.startswith("/static/") and not request.path.startswith("/api/") and
|
# This check is important to avoid doing the extra work of
|
||||||
not request.path.startswith("/json/")):
|
# `get_realm` (which does a database query that could be
|
||||||
|
# problematic for Tornado). Also the error page below is only
|
||||||
|
# appropriate for a page visited in a browser, not the API.
|
||||||
|
#
|
||||||
|
# API authentication will end up checking for an invalid
|
||||||
|
# realm, and throw a JSON-format error if appropriate.
|
||||||
|
if request.path.startswith(("/static/", "/api/", "/json/")):
|
||||||
|
return None
|
||||||
|
|
||||||
subdomain = get_subdomain(request)
|
subdomain = get_subdomain(request)
|
||||||
if subdomain != Realm.SUBDOMAIN_FOR_ROOT_DOMAIN:
|
if subdomain != Realm.SUBDOMAIN_FOR_ROOT_DOMAIN:
|
||||||
try:
|
try:
|
||||||
get_realm(subdomain)
|
request.realm = get_realm(subdomain)
|
||||||
except Realm.DoesNotExist:
|
except Realm.DoesNotExist:
|
||||||
return render(request, "zerver/invalid_realm.html", status=404)
|
return render(request, "zerver/invalid_realm.html", status=404)
|
||||||
return response
|
return None
|
||||||
|
|
||||||
class SetRemoteAddrFromForwardedFor(MiddlewareMixin):
|
class SetRemoteAddrFromForwardedFor(MiddlewareMixin):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -627,12 +627,8 @@ class LoginTest(ZulipTestCase):
|
||||||
self.assert_logged_in_user_id(None)
|
self.assert_logged_in_user_id(None)
|
||||||
|
|
||||||
def test_login_invalid_subdomain(self) -> None:
|
def test_login_invalid_subdomain(self) -> None:
|
||||||
with self.assertLogs(level='WARNING') as warn_log:
|
|
||||||
result = self.login_with_return(self.example_email("hamlet"), "xxx",
|
result = self.login_with_return(self.example_email("hamlet"), "xxx",
|
||||||
subdomain="invalid")
|
subdomain="invalid")
|
||||||
self.assertEqual(warn_log.output, [
|
|
||||||
'WARNING:root:User hamlet@zulip.com attempted password login to nonexistent subdomain invalid'
|
|
||||||
])
|
|
||||||
self.assertEqual(result.status_code, 404)
|
self.assertEqual(result.status_code, 404)
|
||||||
self.assert_in_response("There is no Zulip organization hosted at this subdomain.", result)
|
self.assert_in_response("There is no Zulip organization hosted at this subdomain.", result)
|
||||||
self.assert_logged_in_user_id(None)
|
self.assert_logged_in_user_id(None)
|
||||||
|
@ -2407,9 +2403,7 @@ class RealmCreationTest(ZulipTestCase):
|
||||||
|
|
||||||
result = self.submit_reg_form_for_user(email, password,
|
result = self.submit_reg_form_for_user(email, password,
|
||||||
realm_subdomain = string_id,
|
realm_subdomain = string_id,
|
||||||
realm_name=realm_name,
|
realm_name=realm_name)
|
||||||
# Pass HTTP_HOST for the target subdomain
|
|
||||||
HTTP_HOST=string_id + ".testserver")
|
|
||||||
self.assertEqual(result.status_code, 302)
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
result = self.client_get(result.url, subdomain=string_id)
|
result = self.client_get(result.url, subdomain=string_id)
|
||||||
|
@ -2447,8 +2441,7 @@ class RealmCreationTest(ZulipTestCase):
|
||||||
|
|
||||||
result = self.submit_reg_form_for_user(email, password,
|
result = self.submit_reg_form_for_user(email, password,
|
||||||
realm_subdomain = string_id,
|
realm_subdomain = string_id,
|
||||||
realm_name=realm_name,
|
realm_name=realm_name)
|
||||||
HTTP_HOST=string_id + ".testserver")
|
|
||||||
self.assertEqual(result.status_code, 302)
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
result = self.client_get(result.url, subdomain=string_id)
|
result = self.client_get(result.url, subdomain=string_id)
|
||||||
|
|
Loading…
Reference in New Issue