mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
fix(accounts): the admin's Email OTP sent twice at once makes one device
get_user_totp_device looked for the user's TOTP device and made one if there was none; two requests at once could both make one, and both devices' codes then logged in. It now does so under a lock on the user's row.
This commit is contained in:
parent
c290cef21a
commit
45828928ab
2 changed files with 19 additions and 7 deletions
|
|
@ -82,6 +82,14 @@ class AccountsUtilsTestCase(TestCase):
|
|||
device = TOTPDevice.objects.create(user=self.user, confirmed=True)
|
||||
self.assertEqual(get_existing_totp_device(self.user).id, device.id)
|
||||
|
||||
def test_the_device_is_made_under_a_lock_on_the_user(self):
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
with patch.object(QuerySet, "select_for_update", autospec=True, side_effect=QuerySet.select_for_update) as lock:
|
||||
device = get_user_totp_device(self.user)
|
||||
lock.assert_called_once()
|
||||
self.assertEqual(get_user_totp_device(self.user), device)
|
||||
|
||||
@override_settings(OTP_ENABLED=True)
|
||||
def test_the_mail_links_to_the_qr_code_and_holds_no_secret(self):
|
||||
device = get_user_totp_device(self.user)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ import struct
|
|||
from decimal import Decimal
|
||||
|
||||
import segno
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.mail import send_mail
|
||||
from django.core.signing import TimestampSigner
|
||||
from django.db import transaction
|
||||
from django.urls import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext as _
|
||||
|
|
@ -24,13 +26,15 @@ def get_existing_totp_device(user):
|
|||
|
||||
|
||||
def get_user_totp_device(user):
|
||||
devices = devices_for_user(user)
|
||||
for device in devices:
|
||||
if isinstance(device, TOTPDevice):
|
||||
return device
|
||||
|
||||
device = user.totpdevice_set.create()
|
||||
return device
|
||||
"""The user's TOTP device, made if they have none. The user's row is
|
||||
locked meanwhile: two requests at once (a double click) make one device,
|
||||
not two that both log in."""
|
||||
with transaction.atomic():
|
||||
get_user_model().objects.select_for_update().get(pk=user.pk)
|
||||
for device in devices_for_user(user):
|
||||
if isinstance(device, TOTPDevice):
|
||||
return device
|
||||
return user.totpdevice_set.create()
|
||||
|
||||
|
||||
def validate_ssh_key(key):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue