mirror of https://github.com/zulip/zulip.git
rate_limit: Show html page when rate limited at /new/ endpoint.
Previously this showed a json error, but this is an endpoint that human users use in the browser, so a proper HTML page is more appropriate.
This commit is contained in:
parent
3ec55e7976
commit
4161e0caeb
|
@ -0,0 +1,23 @@
|
|||
{% extends "zerver/portico.html" %}
|
||||
|
||||
{% block portico_content %}
|
||||
|
||||
<div class="error_page">
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<img src="/static/images/500art.svg" alt=""/>
|
||||
<div class="errorbox">
|
||||
<div class="errorcontent">
|
||||
<h1 class="lead">{{ _("Rate limit exceeded.") }}</h1>
|
||||
<p>
|
||||
{% trans %}You have exceeded the limit for how
|
||||
often a user can perform this action.{% endtrans %}
|
||||
{% trans %}You can try again in {{retry_after}} seconds.{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,5 +1,5 @@
|
|||
import time
|
||||
from typing import Callable
|
||||
from typing import Callable, Optional
|
||||
from unittest import mock, skipUnless
|
||||
|
||||
import DNS
|
||||
|
@ -134,19 +134,29 @@ class RateLimitTests(ZulipTestCase):
|
|||
newlimit = int(result["X-RateLimit-Remaining"])
|
||||
self.assertEqual(limit, newlimit + 1)
|
||||
|
||||
def do_test_hit_ratelimits(self, request_func: Callable[[], HttpResponse]) -> HttpResponse:
|
||||
def do_test_hit_ratelimits(
|
||||
self,
|
||||
request_func: Callable[[], HttpResponse],
|
||||
assert_func: Optional[Callable[[HttpResponse], None]] = None,
|
||||
) -> HttpResponse:
|
||||
def default_assert_func(result: HttpResponse) -> None:
|
||||
self.assertEqual(result.status_code, 429)
|
||||
json = result.json()
|
||||
self.assertEqual(json.get("result"), "error")
|
||||
self.assertIn("API usage exceeded rate limit", json.get("msg"))
|
||||
self.assertEqual(json.get("retry-after"), 0.5)
|
||||
self.assertTrue("Retry-After" in result)
|
||||
self.assertEqual(result["Retry-After"], "0.5")
|
||||
|
||||
if assert_func is None:
|
||||
assert_func = default_assert_func
|
||||
|
||||
start_time = time.time()
|
||||
for i in range(6):
|
||||
with mock.patch("time.time", return_value=(start_time + i * 0.1)):
|
||||
result = request_func()
|
||||
|
||||
self.assertEqual(result.status_code, 429)
|
||||
json = result.json()
|
||||
self.assertEqual(json.get("result"), "error")
|
||||
self.assertIn("API usage exceeded rate limit", json.get("msg"))
|
||||
self.assertEqual(json.get("retry-after"), 0.5)
|
||||
self.assertTrue("Retry-After" in result)
|
||||
self.assertEqual(result["Retry-After"], "0.5")
|
||||
assert_func(result)
|
||||
|
||||
# We simulate waiting a second here, rather than force-clearing our history,
|
||||
# to make sure the rate-limiting code automatically forgives a user
|
||||
|
@ -173,12 +183,17 @@ class RateLimitTests(ZulipTestCase):
|
|||
remove_ratelimit_rule(1, 5, domain="api_by_ip")
|
||||
|
||||
def test_create_realm_rate_limiting(self) -> None:
|
||||
def assert_func(result: HttpResponse) -> None:
|
||||
self.assertEqual(result.status_code, 429)
|
||||
self.assert_in_response("Rate limit exceeded.", result)
|
||||
|
||||
with self.settings(OPEN_REALM_CREATION=True):
|
||||
add_ratelimit_rule(1, 5, domain="create_realm_by_ip")
|
||||
try:
|
||||
RateLimitedIPAddr("127.0.0.1").clear_history()
|
||||
self.do_test_hit_ratelimits(
|
||||
lambda: self.client_post("/new/", {"email": "new@zulip.com"})
|
||||
lambda: self.client_post("/new/", {"email": "new@zulip.com"}),
|
||||
assert_func=assert_func,
|
||||
)
|
||||
finally:
|
||||
remove_ratelimit_rule(1, 5, domain="create_realm_by_ip")
|
||||
|
|
|
@ -45,6 +45,7 @@ from zerver.lib.actions import (
|
|||
lookup_default_stream_groups,
|
||||
)
|
||||
from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm
|
||||
from zerver.lib.exceptions import RateLimited
|
||||
from zerver.lib.onboarding import send_initial_realm_messages, setup_realm_internal_bots
|
||||
from zerver.lib.pysa import mark_sanitized
|
||||
from zerver.lib.send_email import EmailNotDeliveredException, FromAddress, send_email
|
||||
|
@ -590,7 +591,16 @@ def create_realm(request: HttpRequest, creation_key: Optional[str] = None) -> Ht
|
|||
if request.method == "POST":
|
||||
form = RealmCreationForm(request.POST)
|
||||
if form.is_valid():
|
||||
rate_limit_request_by_ip(request, domain="create_realm_by_ip")
|
||||
try:
|
||||
rate_limit_request_by_ip(request, domain="create_realm_by_ip")
|
||||
except RateLimited as e:
|
||||
assert e.secs_to_freedom is not None
|
||||
return render(
|
||||
request,
|
||||
"zerver/rate_limit_exceeded.html",
|
||||
context={"retry_after": int(e.secs_to_freedom)},
|
||||
status=429,
|
||||
)
|
||||
|
||||
email = form.cleaned_data["email"]
|
||||
activation_url = prepare_activation_url(email, request, realm_creation=True)
|
||||
|
|
Loading…
Reference in New Issue