mirror of https://github.com/zulip/zulip.git
reset_password: Show user-facing page on rate-limit.
This commit is contained in:
parent
d3ecbf96a8
commit
c8badbd858
|
@ -326,13 +326,13 @@ class ZulipPasswordResetForm(PasswordResetForm):
|
||||||
rate_limit_password_reset_form_by_email(email)
|
rate_limit_password_reset_form_by_email(email)
|
||||||
rate_limit_request_by_ip(request, domain="sends_email_by_ip")
|
rate_limit_request_by_ip(request, domain="sends_email_by_ip")
|
||||||
except RateLimited:
|
except RateLimited:
|
||||||
# TODO: Show an informative, user-facing error message.
|
|
||||||
logging.info(
|
logging.info(
|
||||||
"Too many password reset attempts for email %s from %s",
|
"Too many password reset attempts for email %s from %s",
|
||||||
email,
|
email,
|
||||||
request.META["REMOTE_ADDR"],
|
request.META["REMOTE_ADDR"],
|
||||||
)
|
)
|
||||||
return
|
# The view will handle the RateLimit exception and render an appropriate page
|
||||||
|
raise
|
||||||
|
|
||||||
user: Optional[UserProfile] = None
|
user: Optional[UserProfile] = None
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -227,6 +227,18 @@ class RateLimitTests(ZulipTestCase):
|
||||||
is_json=False,
|
is_json=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@rate_limit_rule(1, 5, domain="sends_email_by_ip")
|
||||||
|
def test_password_reset_rate_limiting(self) -> None:
|
||||||
|
with self.assertLogs(level="INFO") as m:
|
||||||
|
self.do_test_hit_ratelimits(
|
||||||
|
lambda: self.client_post("/accounts/password/reset/", {"email": "new@zulip.com"}),
|
||||||
|
is_json=False,
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
m.output,
|
||||||
|
["INFO:root:Too many password reset attempts for email new@zulip.com from 127.0.0.1"],
|
||||||
|
)
|
||||||
|
|
||||||
# Test whether submitting multiple emails is handled correctly.
|
# Test whether submitting multiple emails is handled correctly.
|
||||||
# The limit is set to 10 per second, so 5 requests with 2 emails
|
# The limit is set to 10 per second, so 5 requests with 2 emails
|
||||||
# submitted in each should be allowed.
|
# submitted in each should be allowed.
|
||||||
|
|
|
@ -51,6 +51,7 @@ from zerver.lib.exceptions import (
|
||||||
JsonableError,
|
JsonableError,
|
||||||
PasswordAuthDisabledError,
|
PasswordAuthDisabledError,
|
||||||
PasswordResetRequiredError,
|
PasswordResetRequiredError,
|
||||||
|
RateLimited,
|
||||||
RealmDeactivatedError,
|
RealmDeactivatedError,
|
||||||
UserDeactivatedError,
|
UserDeactivatedError,
|
||||||
)
|
)
|
||||||
|
@ -992,11 +993,20 @@ def password_reset(request: HttpRequest) -> HttpResponse:
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(redirect_url)
|
return HttpResponseRedirect(redirect_url)
|
||||||
|
|
||||||
|
try:
|
||||||
response = DjangoPasswordResetView.as_view(
|
response = DjangoPasswordResetView.as_view(
|
||||||
template_name="zerver/reset.html",
|
template_name="zerver/reset.html",
|
||||||
form_class=ZulipPasswordResetForm,
|
form_class=ZulipPasswordResetForm,
|
||||||
success_url="/accounts/password/reset/done/",
|
success_url="/accounts/password/reset/done/",
|
||||||
)(request)
|
)(request)
|
||||||
|
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,
|
||||||
|
)
|
||||||
assert isinstance(response, HttpResponse)
|
assert isinstance(response, HttpResponse)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue