2FA: Added tests

This commit is contained in:
Raphael Michel
2016-10-08 22:05:24 +02:00
parent d7719d0bc7
commit 2611b7619e
5 changed files with 271 additions and 18 deletions

View File

@@ -1,13 +1,18 @@
import time
from datetime import date, timedelta
from _pytest import monkeypatch
from django.conf import settings
from django.contrib.auth.tokens import (
PasswordResetTokenGenerator, default_token_generator,
)
from django.core import mail as djmail
from django.test import TestCase
from django_otp.oath import TOTP
from django_otp.plugins.otp_totp.models import TOTPDevice
from u2flib_server.jsapi import JSONDict
from pretix.base.models import User
from pretix.base.models import U2FDevice, User
class LoginFormTest(TestCase):
@@ -47,6 +52,18 @@ class LoginFormTest(TestCase):
self.assertEqual(response.status_code, 302)
self.assertIn('/control/events/', response['Location'])
def test_redirect_to_2fa(self):
self.user.require_2fa = True
self.user.save()
response = self.client.post('/control/login?next=/control/events/', {
'email': 'dummy@dummy.dummy',
'password': 'dummy',
})
self.assertEqual(response.status_code, 302)
self.assertIn('/control/login/2fa?next=/control/events/', response['Location'])
assert self.client.session['pretix_auth_2fa_user'] == self.user.pk
assert 'pretix_auth_2fa_time' in self.client.session
def test_logged_in(self):
response = self.client.post('/control/login?next=/control/events/', {
'email': 'dummy@dummy.dummy',
@@ -172,6 +189,95 @@ class RegistrationFormTest(TestCase):
self.assertEqual(response.status_code, 302)
class Login2FAFormTest(TestCase):
def setUp(self):
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy', require_2fa=True)
session = self.client.session
session['pretix_auth_2fa_user'] = self.user.pk
session['pretix_auth_2fa_time'] = str(int(time.time()))
session.save()
def test_invalid_session(self):
session = self.client.session
session['pretix_auth_2fa_user'] = self.user.pk + 12
session['pretix_auth_2fa_time'] = str(int(time.time()))
session.save()
response = self.client.get('/control/login/2fa')
self.assertEqual(response.status_code, 302)
self.assertIn('/control/login', response['Location'])
def test_expired_session(self):
session = self.client.session
session['pretix_auth_2fa_user'] = self.user.pk + 12
session['pretix_auth_2fa_time'] = str(int(time.time()) - 3600)
session.save()
response = self.client.get('/control/login/2fa')
self.assertEqual(response.status_code, 302)
self.assertIn('/control/login', response['Location'])
def test_totp_invalid(self):
response = self.client.get('/control/login/2fa')
assert 'token' in response.rendered_content
d = TOTPDevice.objects.create(user=self.user, name='test')
totp = TOTP(d.bin_key, d.step, d.t0, d.digits, d.drift)
totp.time = time.time()
response = self.client.post('/control/login/2fa'.format(d.pk), {
'token': str(totp.token() + 2)
})
self.assertEqual(response.status_code, 302)
self.assertIn('/control/login/2fa', response['Location'])
def test_totp_valid(self):
response = self.client.get('/control/login/2fa')
assert 'token' in response.rendered_content
d = TOTPDevice.objects.create(user=self.user, name='test')
totp = TOTP(d.bin_key, d.step, d.t0, d.digits, d.drift)
totp.time = time.time()
response = self.client.post('/control/login/2fa?next=/control/events/'.format(d.pk), {
'token': str(totp.token())
})
self.assertEqual(response.status_code, 302)
self.assertIn('/control/events/', response['Location'])
def test_u2f_invalid(self):
def fail(*args, **kwargs):
raise Exception("Failed")
m = monkeypatch.monkeypatch()
m.setattr("u2flib_server.u2f.verify_authenticate", fail)
m.setattr("u2flib_server.u2f.start_authenticate",
lambda *args, **kwargs: JSONDict({'authenticateRequests': []}))
d = U2FDevice.objects.create(user=self.user, name='test', json_data="{}")
response = self.client.get('/control/login/2fa')
assert 'token' in response.rendered_content
response = self.client.post('/control/login/2fa'.format(d.pk), {
'token': '{"response": "true"}'
})
self.assertEqual(response.status_code, 302)
self.assertIn('/control/login/2fa', response['Location'])
m.undo()
def test_u2f_valid(self):
m = monkeypatch.monkeypatch()
m.setattr("u2flib_server.u2f.verify_authenticate", lambda *args, **kwargs: True)
m.setattr("u2flib_server.u2f.start_authenticate",
lambda *args, **kwargs: JSONDict({'authenticateRequests': []}))
d = U2FDevice.objects.create(user=self.user, name='test', json_data="{}")
response = self.client.get('/control/login/2fa')
assert 'token' in response.rendered_content
response = self.client.post('/control/login/2fa'.format(d.pk), {
'token': '{"response": "true"}'
})
self.assertEqual(response.status_code, 302)
self.assertIn('/control/', response['Location'])
m.undo()
class PasswordRecoveryFormTest(TestCase):
def setUp(self):
super().setUp()

View File

@@ -1,6 +1,14 @@
from tests.base import SoupTest, extract_form_fields
import time
from pretix.base.models import User
from _pytest import monkeypatch
from django_otp.oath import TOTP
from django_otp.plugins.otp_static.models import StaticDevice
from django_otp.plugins.otp_totp.models import TOTPDevice
from tests.base import SoupTest, extract_form_fields
from u2flib_server.jsapi import JSONDict
from pretix.base.models import U2FDevice, User
from pretix.testutils.mock import mocker_context
class UserSettingsTest(SoupTest):
@@ -14,7 +22,6 @@ class UserSettingsTest(SoupTest):
def save(self, data):
form_data = self.form_data.copy()
form_data.update(data)
print(form_data)
return self.post_doc('/control/settings', form_data)
def test_set_name(self):
@@ -106,3 +113,145 @@ class UserSettingsTest(SoupTest):
pw = self.user.password
self.user = User.objects.get(pk=self.user.pk)
assert self.user.password == pw
class UserSettings2FATest(SoupTest):
def setUp(self):
super().setUp()
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
self.client.login(email='dummy@dummy.dummy', password='dummy')
def test_enable_require_device(self):
r = self.client.post('/control/settings/2fa/enable', follow=True)
assert 'alert-danger' in r.rendered_content
self.user.refresh_from_db()
assert not self.user.require_2fa
def test_enable(self):
U2FDevice.objects.create(user=self.user, name='Test')
r = self.client.post('/control/settings/2fa/enable', follow=True)
assert 'alert-success' in r.rendered_content
self.user.refresh_from_db()
assert self.user.require_2fa
def test_disable(self):
self.user.require_2fa = True
self.user.save()
r = self.client.post('/control/settings/2fa/disable', follow=True)
assert 'alert-success' in r.rendered_content
self.user.refresh_from_db()
assert not self.user.require_2fa
def test_gen_emergency(self):
self.client.get('/control/settings/2fa/')
d = StaticDevice.objects.get(user=self.user, name='emergency')
assert d.token_set.count() == 10
old_tokens = set(t.token for t in d.token_set.all())
self.client.post('/control/settings/2fa/regenemergency')
new_tokens = set(t.token for t in d.token_set.all())
assert d.token_set.count() == 10
assert old_tokens != new_tokens
def test_delete_u2f(self):
d = U2FDevice.objects.create(user=self.user, name='Test')
self.client.get('/control/settings/2fa/u2f/{}/delete'.format(d.pk))
self.client.post('/control/settings/2fa/u2f/{}/delete'.format(d.pk))
assert not U2FDevice.objects.exists()
def test_delete_totp(self):
d = TOTPDevice.objects.create(user=self.user, name='Test')
self.client.get('/control/settings/2fa/totp/{}/delete'.format(d.pk))
self.client.post('/control/settings/2fa/totp/{}/delete'.format(d.pk))
assert not TOTPDevice.objects.exists()
def test_create_u2f_require_https(self):
r = self.client.post('/control/settings/2fa/add', {
'devicetype': 'u2f',
'name': 'Foo'
})
assert 'alert-danger' in r.rendered_content
def test_create_u2f(self):
with mocker_context() as mocker:
mocker.patch('django.http.request.HttpRequest.is_secure')
self.client.post('/control/settings/2fa/add', {
'devicetype': 'u2f',
'name': 'Foo'
})
d = U2FDevice.objects.first()
assert d.name == 'Foo'
assert not d.confirmed
def test_create_totp(self):
self.client.post('/control/settings/2fa/add', {
'devicetype': 'totp',
'name': 'Foo'
})
d = TOTPDevice.objects.first()
assert d.name == 'Foo'
def test_confirm_totp(self):
self.client.post('/control/settings/2fa/add', {
'devicetype': 'totp',
'name': 'Foo'
}, follow=True)
d = TOTPDevice.objects.first()
totp = TOTP(d.bin_key, d.step, d.t0, d.digits, d.drift)
totp.time = time.time()
r = self.client.post('/control/settings/2fa/totp/{}/confirm'.format(d.pk), {
'token': str(totp.token())
}, follow=True)
d.refresh_from_db()
assert d.confirmed
assert 'alert-success' in r.rendered_content
def test_confirm_totp_failed(self):
self.client.post('/control/settings/2fa/add', {
'devicetype': 'totp',
'name': 'Foo'
}, follow=True)
d = TOTPDevice.objects.first()
totp = TOTP(d.bin_key, d.step, d.t0, d.digits, d.drift)
totp.time = time.time()
r = self.client.post('/control/settings/2fa/totp/{}/confirm'.format(d.pk), {
'token': str(totp.token() - 2)
}, follow=True)
assert 'alert-danger' in r.rendered_content
d.refresh_from_db()
assert not d.confirmed
def test_confirm_u2f_failed(self):
with mocker_context() as mocker:
mocker.patch('django.http.request.HttpRequest.is_secure')
self.client.post('/control/settings/2fa/add', {
'devicetype': 'u2f',
'name': 'Foo'
}, follow=True)
d = U2FDevice.objects.first()
r = self.client.post('/control/settings/2fa/u2f/{}/confirm'.format(d.pk), {
'token': 'FOO'
}, follow=True)
assert 'alert-danger' in r.rendered_content
d.refresh_from_db()
assert not d.confirmed
def test_confirm_u2f_success(self):
with mocker_context() as mocker:
mocker.patch('django.http.request.HttpRequest.is_secure')
self.client.post('/control/settings/2fa/add', {
'devicetype': 'u2f',
'name': 'Foo'
}, follow=True)
m = monkeypatch.monkeypatch()
m.setattr("u2flib_server.u2f.complete_register", lambda *args, **kwargs: (JSONDict({}), None))
d = U2FDevice.objects.first()
r = self.client.post('/control/settings/2fa/u2f/{}/confirm'.format(d.pk), {
'token': 'FOO'
}, follow=True)
d.refresh_from_db()
assert d.confirmed
assert 'alert-success' in r.rendered_content
m.undo()