Initial mfa support (for internal users) #93591
@ -18,7 +18,7 @@ from mfa.models import EncryptedTOTPDevice, devices_for_user
|
|||||||
'django_otp.oath.TOTP.verify',
|
'django_otp.oath.TOTP.verify',
|
||||||
new=lambda _, token, *args, **kwargs: int(token) == 123456,
|
new=lambda _, token, *args, **kwargs: int(token) == 123456,
|
||||||
)
|
)
|
||||||
class TestMfaRequredIfConfigured(TestCase):
|
class TestMfa(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.user = UserFactory(
|
self.user = UserFactory(
|
||||||
confirmed_email_at=timezone.now(),
|
confirmed_email_at=timezone.now(),
|
||||||
@ -134,3 +134,112 @@ class TestMfaRequredIfConfigured(TestCase):
|
|||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
response = client.get(reverse('bid_main:mfa'))
|
response = client.get(reverse('bid_main:mfa'))
|
||||||
self.assertContains(response, '9 recovery codes remaining')
|
self.assertContains(response, '9 recovery codes remaining')
|
||||||
|
|
||||||
|
# test that can't reuse the same code, repeating the steps
|
||||||
|
client = Client()
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'username': self.user.email, 'password': 'hunter2'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
response = client.get('/login?use_recovery=1')
|
||||||
|
match = re.search(
|
||||||
|
r'input type="hidden" name="otp_device" value="([^"]+)"', str(response.content)
|
||||||
|
)
|
||||||
|
otp_device = match.group(1)
|
||||||
|
response = client.post(
|
||||||
|
'/login?use_recovery=1',
|
||||||
|
{'otp_device': otp_device, 'otp_token': recovery_code},
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_disable_mfa(self):
|
||||||
|
# shortcut: create a totp device via a model
|
||||||
|
EncryptedTOTPDevice.objects.create(encrypted_key='00', key='', name='totp', user=self.user)
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'username': self.user.email, 'password': 'hunter2'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
match = re.search(
|
||||||
|
r'input type="hidden" name="otp_device" value="([^"]+)"', str(response.content)
|
||||||
|
)
|
||||||
|
otp_device = match.group(1)
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'otp_device': otp_device, 'otp_token': '123456'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
client.post(reverse('bid_main:mfa_disable'), {'disable_mfa_confirm': 'True'})
|
||||||
|
|
||||||
|
# no mfa requried now
|
||||||
|
client = Client()
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'username': self.user.email, 'password': 'hunter2'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
self.assertContains(response, '<h2>Account</h2>')
|
||||||
|
|
||||||
|
def test_remember_device(self):
|
||||||
|
# shortcut: create a totp device via a model
|
||||||
|
EncryptedTOTPDevice.objects.create(encrypted_key='00', key='', name='totp', user=self.user)
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'username': self.user.email, 'password': 'hunter2'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
match = re.search(
|
||||||
|
r'input type="hidden" name="otp_device" value="([^"]+)"', str(response.content)
|
||||||
|
)
|
||||||
|
otp_device = match.group(1)
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'otp_device': otp_device, 'otp_token': '123456', 'otp_trust_agent': 'True'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
client.get('/logout', follow=True)
|
||||||
|
|
||||||
|
# no mfa requried now, same client
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'username': self.user.email, 'password': 'hunter2'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
self.assertContains(response, '<h2>Account</h2>')
|
||||||
|
|
||||||
|
def test_dont_remember_device(self):
|
||||||
|
# shortcut: create a totp device via a model
|
||||||
|
EncryptedTOTPDevice.objects.create(encrypted_key='00', key='', name='totp', user=self.user)
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'username': self.user.email, 'password': 'hunter2'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
match = re.search(
|
||||||
|
r'input type="hidden" name="otp_device" value="([^"]+)"', str(response.content)
|
||||||
|
)
|
||||||
|
otp_device = match.group(1)
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'otp_device': otp_device, 'otp_token': '123456', 'otp_trust_agent': ''},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
client.get('/logout', follow=True)
|
||||||
|
|
||||||
|
# no mfa requried now, same client
|
||||||
|
response = client.post(
|
||||||
|
'/login',
|
||||||
|
{'username': self.user.email, 'password': 'hunter2'},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
self.assertNotContains(response, '<h2>Account</h2>')
|
||||||
|
Loading…
Reference in New Issue
Block a user