1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00

fix(accounts): mailing an OTP QR code waits at most 10 seconds for the SMTP server

EMAIL_TIMEOUT was not set, and Django's SMTP connection then waits for
a server without end: one that accepts the connection and stops
answering held the request's thread for good, also on the login page's
Email OTP form, which anyone may post. The settings template sets 10
seconds. An administrator whose mail fails is told why on the account
page instead of getting an error page.
This commit is contained in:
catborise 2026-11-05 10:49:54 +03:00
parent 686c926a30
commit cbd4374f4f
8 changed files with 37 additions and 14 deletions

View file

@ -30,3 +30,9 @@ class AdminEmailOtpTestCase(TestCase):
response = self.client.post(self.url)
self.assertEqual(response.status_code, 302)
mock_send.assert_called_once()
def test_the_smtp_connection_waits_a_bounded_time(self):
from django.core.mail.backends.smtp import EmailBackend
# None would wait for a server that stopped answering forever
self.assertIsNotNone(EmailBackend().timeout)

View file

@ -85,9 +85,9 @@ class AdminAccountRowTests(TestCase):
with patch("accounts.views.send_email_with_otp") as send:
self.client.post(url)
send.assert_called_once()
with patch("accounts.views.send_email_with_otp", side_effect=SMTPException("refused")), \
self.assertRaises(SMTPException):
self.client.post(url)
with patch("accounts.views.send_email_with_otp", side_effect=SMTPException("refused")):
response = self.client.post(url, follow=True)
self.assertContains(response, "The OTP QR code could not be mailed to bob: refused")
self.assertEqual(rows(), [
("User bob: mail OTP QR code: no email address", False),
("User bob: mail OTP QR code", True),

View file

@ -343,11 +343,16 @@ def admin_email_otp(request, user_id):
user = get_object_or_404(get_user_model(), pk=user_id)
subject = f"User {user.username}"
if user.email != "":
with audit(request, None, subject, "mail OTP QR code"):
send_email_with_otp(user, get_user_totp_device(user))
messages.success(
request, _("OTP QR code was emailed to user %(user)s") % {"user": user}
)
try:
with audit(request, None, subject, "mail OTP QR code"):
send_email_with_otp(user, get_user_totp_device(user))
except OSError as err: # the SMTP server refused it, or did not answer within EMAIL_TIMEOUT
messages.error(request, _("The OTP QR code could not be mailed to %(user)s: %(error)s") % {
"user": user, "error": err})
else:
messages.success(
request, _("OTP QR code was emailed to user %(user)s") % {"user": user}
)
else:
failed(request, None, subject, "mail OTP QR code", "no email address")
messages.error(request, _("User email not set, failed to send QR code"))

Binary file not shown.

View file

@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-11-05 06:39+0000\n"
"POT-Creation-Date: 2026-11-05 07:49+0000\n"
"PO-Revision-Date: 2020-11-04 12:00+0000\n"
"Last-Translator: catborise <muhammetalisag@gmail.com>, 2020\n"
"Language-Team: Turkish (https://www.transifex.com/catborise/teams/110663/tr/)\n"
@ -636,12 +636,17 @@ msgstr "OTP %(email)s ye gönderildi"
msgid "Email OTP"
msgstr "OTP E-Postası"
#: accounts/views.py:349
#: accounts/views.py:350
#, python-format
msgid "The OTP QR code could not be mailed to %(user)s: %(error)s"
msgstr "OTP QR kodu %(user)s kullanıcısına e-postayla gönderilemedi: %(error)s"
#: accounts/views.py:354
#, python-format
msgid "OTP QR code was emailed to user %(user)s"
msgstr "OTP QR kodu %(user)s kullanıcısına gönderildi"
#: accounts/views.py:353
#: accounts/views.py:358
msgid "User email not set, failed to send QR code"
msgstr "Kullanıcı e-postası girilmemiş, QR kod gönderimi başarısız"

Binary file not shown.

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-11-05 06:39+0000\n"
"POT-Creation-Date: 2026-11-05 07:49+0000\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@ -623,12 +623,17 @@ msgstr "OTP 已发送至 %(email)s"
msgid "Email OTP"
msgstr "邮件发送 OTP"
#: accounts/views.py:349
#: accounts/views.py:350
#, python-format
msgid "The OTP QR code could not be mailed to %(user)s: %(error)s"
msgstr "无法将 OTP 二维码通过邮件发送给 %(user)s:%(error)s"
#: accounts/views.py:354
#, python-format
msgid "OTP QR code was emailed to user %(user)s"
msgstr "OTP 二维码已通过邮件发送给用户 %(user)s"
#: accounts/views.py:353
#: accounts/views.py:358
msgid "User email not set, failed to send QR code"
msgstr "未设置用户邮箱,无法发送二维码"

View file

@ -401,6 +401,8 @@ EMAIL_PORT = int(os.getenv("EMAIL_PORT", "587"))
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "")
## password associated with above email-id
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "")
# seconds an SMTP server may take to answer; without it a server that stops answering holds the request for good
EMAIL_TIMEOUT = 10
# LDAP Config
#