Completing functional conversions

This commit is contained in:
= 2019-09-02 04:56:45 -05:00
parent 3cfd9840e8
commit edb16382f3
Signed by: kiichan
GPG key ID: 619DFD67F0976616
29 changed files with 1798 additions and 28 deletions

1
sqrl/tests/__init__.py Normal file
View file

@ -0,0 +1 @@

View file

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
import unittest
import mock
from django.contrib.auth.backends import ModelBackend
from ..backends import SQRLModelBackend
from ..models import SQRLIdentity
class TestSQRLModelBackend(unittest.TestCase):
@mock.patch.object(ModelBackend, 'authenticate')
def test_authenticate_no_user(self, mock_authenticate):
mock_authenticate.return_value = None
self.assertIsNone(SQRLModelBackend().authenticate(
username='user',
password='password'
))
@mock.patch.object(ModelBackend, 'authenticate')
def test_authenticate_no_sqrl_identity(self, mock_authenticate):
class UserMock(mock.MagicMock):
@property
def sqrl_identity(self):
raise SQRLIdentity.DoesNotExist
user = UserMock()
mock_authenticate.return_value = user
self.assertEqual(SQRLModelBackend().authenticate(
username='user',
password='password'
), user)
@mock.patch.object(ModelBackend, 'authenticate')
def test_authenticate_disabled(self, mock_authenticate):
user = mock.MagicMock()
user.sqrl_identity.is_only_sqrl = True
user.sqrl_identity.is_enabled = True
mock_authenticate.return_value = user
self.assertIsNone(SQRLModelBackend().authenticate(
username='user',
password='password'
))
@mock.patch.object(ModelBackend, 'authenticate')
def test_authenticate_enabled(self, mock_authenticate):
user = mock.MagicMock()
user.sqrl_identity.is_only_sqrl = False
user.sqrl_identity.is_enabled = True
mock_authenticate.return_value = user
self.assertEqual(SQRLModelBackend().authenticate(
username='user',
password='password'
), user)

186
sqrl/tests/test_crypto.py Normal file
View file

@ -0,0 +1,186 @@
# -*- coding: utf-8 -*-
import unittest
from collections import OrderedDict
from django.conf import settings
import os
import ed25519
import mock
from ..crypto import HMAC, Ed25519, generate_randomness
from ..models import SQRLNut
from ..utils import Base64
TESTING_MODULE = 'sqrl.crypto'
class TestHMAC(unittest.TestCase):
def setUp(self):
super(TestHMAC, self).setUp()
self.nut = SQRLNut(session_key='0123456789')
self.data = OrderedDict([
('ver', '1'),
('tif', 0),
('mac', 'value'),
('qry', 'foo?nut=bar'),
])
self.hmac = HMAC(self.nut, self.data)
def test_init(self):
hmac = HMAC(mock.sentinel.nut, mock.sentinel.data)
self.assertEqual(hmac.nut, mock.sentinel.nut)
self.assertEqual(hmac.data, mock.sentinel.data)
def test_sign_data(self):
temp = str(settings.SECRET_KEY)
settings.SECRET_KEY = "foo"
signature = self.hmac.sign_data()
self.assertIsInstance(signature, bytes)
self.assertEqual(len(signature), 20)
self.assertEqual(
signature,
b'R\xfc\xb2\xbd\x12\x85\xae\xb0>\xdd\xed\x16P\xc2\x82\xae\x06\x0c\xc5\xd3'
)
settings.SECRET_KEY = str(temp)
@mock.patch(TESTING_MODULE + '.salted_hmac')
def test_sign_data_mock(self, mock_salted_hmac):
signature = self.hmac.sign_data()
self.assertEqual(
signature,
mock_salted_hmac.return_value.digest.return_value
)
mock_salted_hmac.assert_called_once_with(
self.nut.session_key,
Base64.encode(b'ver=1\r\n'
b'tif=0\r\n'
b'qry=foo?nut=bar\r\n')
)
def test_sign_data_not_dict(self):
with self.assertRaises(AssertionError):
HMAC(mock.sentinel.nut, mock.sentinel.data).sign_data()
@mock.patch.object(HMAC, 'sign_data')
def test_is_signature_valid(self, mock_sign_data):
mock_sign_data.return_value = 'foo_signature'
self.assertTrue(self.hmac.is_signature_valid('foo_signature'))
self.assertFalse(self.hmac.is_signature_valid('foo-signature'))
def test_validation_loop(self):
signature = self.hmac.sign_data()
self.assertTrue(self.hmac.is_signature_valid(signature))
self.assertFalse(self.hmac.is_signature_valid(b'a' + signature[:-1]))
class TestEd25519(unittest.TestCase):
def setUp(self):
super(TestEd25519, self).setUp()
self.signing_key = (b'\xbbH\xdfx\xed\xc5\xdbR\x94\xe4\xff\xa6~5\xbb\xbd\xf2\x16&'
b'\xfc\x89\x8a\xc8\\\\\xeb\xea\x91Db~Hm+b\x88\xf2\x10\xfb:H'
b'\xe4\xfb0\x00\r\xe7n|\xa64\x05m@\xc8\xef"\x07k{O\xf0\xff%')
self.verifying_key = (b'm+b\x88\xf2\x10\xfb:H\xe4\xfb0\x00\r\xe7n|\xa64\x05m@'
b'\xc8\xef"\x07k{O\xf0\xff%')
self.data = b'data'
self.sig = Ed25519(self.verifying_key, self.signing_key, self.data)
def test_init(self):
sig = Ed25519(mock.sentinel.pub_key,
mock.sentinel.priv_key,
mock.sentinel.msg)
self.assertEqual(sig.public_key, mock.sentinel.pub_key)
self.assertEqual(sig.private_key, mock.sentinel.priv_key)
self.assertEqual(sig.msg, mock.sentinel.msg)
def test_sign_data(self):
signature = self.sig.sign_data()
self.assertIsInstance(signature, bytes)
self.assertEqual(len(signature), 64)
self.assertEqual(
signature,
b'\xac\xe0\x81\xc4\xd5\x7f\xd4\xe3\xc1\x03>\x0f\x90\xb5\x9eG<\xe0\xd41'
b'\x1cZ\xd7\x15F\xba\xdeS/\xfa\xbbL\x9bh\x8dn;\xcfP\xb1\x16\x14&d\xde'
b'\x97\x145\x90N[\xb9\xfc\x8e\x8a\x9e\xd2=\xad\x84\xcd\xf1\x93\x06'
)
@mock.patch('ed25519.SigningKey')
def test_sign_data_mock(self, mock_signing_key):
signature = self.sig.sign_data()
self.assertEqual(signature, mock_signing_key.return_value.sign.return_value)
mock_signing_key.assert_called_once_with(self.sig.private_key)
mock_signing_key.return_value.sign.assert_called_once_with(self.sig.msg)
def test_is_signature_valid(self):
signature = self.sig.sign_data()
self.assertTrue(self.sig.is_signature_valid(signature))
self.assertFalse(self.sig.is_signature_valid(b'a' + signature[:-1]))
@mock.patch('ed25519.VerifyingKey')
def test_is_signature_mock(self, mock_verifying_key):
is_valid = self.sig.is_signature_valid(mock.sentinel.signature)
self.assertTrue(is_valid)
mock_verifying_key.assert_called_once_with(self.sig.public_key)
mock_verifying_key.return_value.verify.assert_called_once_with(
mock.sentinel.signature, self.data
)
@mock.patch('ed25519.VerifyingKey')
def test_is_signature_mock_assertion_error(self, mock_verifying_key):
mock_verifying_key.return_value.verify.side_effect = AssertionError
is_valid = self.sig.is_signature_valid(mock.sentinel.signature)
self.assertFalse(is_valid)
mock_verifying_key.assert_called_once_with(self.sig.public_key)
mock_verifying_key.return_value.verify.assert_called_once_with(
mock.sentinel.signature, self.data
)
@mock.patch('ed25519.VerifyingKey')
def test_is_signature_mock_bas_signature_error(self, mock_verifying_key):
mock_verifying_key.return_value.verify.side_effect = ed25519.BadSignatureError
is_valid = self.sig.is_signature_valid(mock.sentinel.signature)
self.assertFalse(is_valid)
mock_verifying_key.assert_called_once_with(self.sig.public_key)
mock_verifying_key.return_value.verify.assert_called_once_with(
mock.sentinel.signature, self.data
)
class TestUtils(unittest.TestCase):
@mock.patch(TESTING_MODULE + '.bytearray', create=True)
@mock.patch.object(Base64, 'encode')
@mock.patch.object(os, 'urandom')
def test_generate_randomness_mock(self, mock_urandom, mock_encode, mock_bytearray):
_mock_bytearray = mock.MagicMock()
def _bytearray(a):
list(a)
return _mock_bytearray(a)
mock_bytearray.side_effect = _bytearray
randomness = generate_randomness()
self.assertEqual(randomness, mock_encode.return_value)
self.assertEqual(mock_urandom.call_count, 1)
mock_urandom.assert_called_with(32)
mock_encode.assert_called_once_with(_mock_bytearray.return_value)
def test_generate_randomness(self):
randomness = generate_randomness()
self.assertIsInstance(randomness, str)

View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
import unittest
import mock
from ..exceptions import TIF, TIFException
TESTING_MODULE = 'sqrl.exceptions'
class TestTIF(unittest.TestCase):
def test_as_hex_string(self):
self.assertEqual(TIF(0x1).as_hex_string(), '1')
self.assertEqual(TIF(0x4).as_hex_string(), '4')
self.assertEqual(TIF(0x88).as_hex_string(), '88')
self.assertEqual(TIF(0x84).as_hex_string(), '84')
def test_breakdown(self):
self.assertDictEqual(TIF(0x34).breakdown(), {
'id_match': False,
'previous_id_match': False,
'ip_match': True,
'sqrl_disabled': False,
'not_supported': True,
'transient_failure': True,
'command_failed': False,
'client_failure': False,
'bad_id_association': False,
})
def test_update(self):
tif = TIF(0x3).update(TIF(0x40))
self.assertIsInstance(tif, TIF)
self.assertEqual(tif, 0x43)
def test_properties(self):
self.assertTrue(TIF(TIF.ID_MATCH).is_id_match)
self.assertTrue(TIF(TIF.PREVIOUS_ID_MATCH).is_previous_id_match)
self.assertTrue(TIF(TIF.IP_MATCH).is_ip_match)
self.assertTrue(TIF(TIF.SQRL_DISABLED).is_sqrl_disabled)
self.assertTrue(TIF(TIF.NOT_SUPPORTED).is_not_supported)
self.assertTrue(TIF(TIF.TRANSIENT_FAILURE).is_transient_failure)
self.assertTrue(TIF(TIF.COMMAND_FAILED).is_command_failed)
self.assertTrue(TIF(TIF.CLIENT_FAILURE).is_client_failure)
self.assertTrue(TIF(TIF.BAD_ID_ASSOCIATION).is_bad_id_association)
class TestTIFException(unittest.TestCase):
@mock.patch(TESTING_MODULE + '.TIF')
def test_init(self, mock_tif):
e = TIFException(mock.sentinel.tif)
self.assertEqual(e.tif, mock_tif.return_value)
mock_tif.assert_called_once_with(mock.sentinel.tif)

196
sqrl/tests/test_fields.py Normal file
View file

@ -0,0 +1,196 @@
# -*- coding: utf-8 -*-
import unittest
from collections import OrderedDict
import mock
from django import forms
from django.urls import Resolver404
from ..fields import (
Base64CharField,
Base64ConditionalPairsField,
Base64Field,
Base64PairsField,
ExtractedNextUrlField,
NextUrlField,
SQRLURLField,
SQRLURLValidator,
TildeMultipleValuesField,
TildeMultipleValuesFieldChoiceField,
)
from ..utils import Base64
TESTING_MODULE = 'sqrl.fields'
class TestNextUrlField(unittest.TestCase):
def test_to_python_empty(self):
self.assertEqual(NextUrlField().to_python(None), '')
@mock.patch(TESTING_MODULE + '.resolve')
def test_to_python_valid(self, mock_resolve):
value = 'http://example.com/path/here/?querystring=here'
self.assertEqual(NextUrlField().to_python(value), '/path/here/')
@mock.patch(TESTING_MODULE + '.resolve')
def test_to_python_invalid(self, mock_resolve):
mock_resolve.side_effect = Resolver404
value = 'http://example.com/path/here/?querystring=here'
with self.assertRaises(forms.ValidationError):
NextUrlField().to_python(value)
class TestExtractedNextUrlField(unittest.TestCase):
def test_to_python_empty(self):
self.assertEqual(ExtractedNextUrlField().to_python(None), '')
def test_to_python_next_not_present(self):
value = 'http://example.com/path/here/?querystring=here'
with self.assertRaises(forms.ValidationError):
ExtractedNextUrlField().to_python(value)
@mock.patch(TESTING_MODULE + '.resolve')
def test_to_python(self, mock_resolve):
value = 'http://example.com/path/here/?next=/next/here/'
self.assertEqual(
ExtractedNextUrlField().to_python(value),
'/next/here/'
)
class TestSQRLURLValidator(unittest.TestCase):
def test_valid(self):
self.assertIsNone(
SQRLURLValidator()('qrl://example.com:8000/sqrl/?nut=hello')
)
self.assertIsNone(
SQRLURLValidator()('sqrl://example.com:8000/sqrl/?nut=hello')
)
def test_invalid(self):
with self.assertRaises(forms.ValidationError):
SQRLURLValidator()('http://example.com:Base64PairsField8000/sqrl/?nut=hello')
class TestSQRLURLField(unittest.TestCase):
def test_default_validators(self):
validator_types = list(map(type, SQRLURLField.default_validators))
self.assertIn(SQRLURLValidator, validator_types)
class TestBase64Field(unittest.TestCase):
def test_empty_value(self):
value = Base64Field().to_python(None)
self.assertEqual(value, b'')
self.assertIsInstance(value, bytes)
def test_value(self):
value = Base64Field().to_python('aGVsbG8')
self.assertEqual(value, b'hello')
self.assertIsInstance(value, bytes)
def test_value_invalid(self):
with self.assertRaises(forms.ValidationError):
Base64Field().to_python('hello')
class TestBase64CharField(unittest.TestCase):
def test_empty_value(self):
value = Base64CharField().to_python(None)
self.assertEqual(value, '')
self.assertIsInstance(value, str)
def test_value(self):
value = Base64CharField().to_python('aGVsbG8')
self.assertEqual(value, 'hello')
self.assertIsInstance(value, str)
def test_value_invalid(self):
with self.assertRaises(forms.ValidationError):
Base64CharField().to_python('z4A')
class TestBase64PairsField(unittest.TestCase):
def test_to_python_empty(self):
value = Base64PairsField().to_python(None)
self.assertEqual(value, OrderedDict())
def test_to_python(self):
value = Base64.encode(
b'ver=1\r\n'
b'foo=bar\r\n'
)
value = Base64PairsField().to_python(value)
self.assertEqual(value, OrderedDict([
('ver', '1'),
('foo', 'bar'),
]))
def test_to_python_not_pars(self):
value = Base64.encode(
b'ver=1\r\n'
b'foo\r\n'
)
with self.assertRaises(forms.ValidationError):
Base64PairsField().to_python(value)
def test_to_python_not_multiline(self):
value = Base64.encode(
b'ver=1'
)
with self.assertRaises(forms.ValidationError):
Base64PairsField().to_python(value)
class TestBase64ConditionalPairsField(unittest.TestCase):
def test_to_python_not_pars(self):
value = Base64.encode(
b'foo'
)
self.assertEqual(Base64ConditionalPairsField().to_python(value), 'foo')
class TestTildeMultipleValuesField(unittest.TestCase):
def test_empty(self):
self.assertListEqual(TildeMultipleValuesField().to_python(None), [])
def test_to_python(self):
self.assertListEqual(
TildeMultipleValuesField().to_python('hello~world'),
['hello', 'world']
)
class TestTildeMultipleValuesFieldChoiceField(unittest.TestCase):
def test_valid(self):
field = TildeMultipleValuesFieldChoiceField(choices=[
('hello', 'hello'),
('world', 'world'),
])
self.assertListEqual(
field.clean('hello~world'),
['hello', 'world']
)
def test_invalid(self):
field = TildeMultipleValuesFieldChoiceField(choices=[
('hello', 'hello'),
])
with self.assertRaises(forms.ValidationError):
field.clean('hello~world')

641
sqrl/tests/test_forms.py Normal file
View file

@ -0,0 +1,641 @@
# -*- coding: utf-8 -*-
import unittest
from collections import OrderedDict
import ed25519
import mock
from django import forms, test
from django.contrib.auth import SESSION_KEY, get_user_model
from django.utils.timezone import now
from ..crypto import HMAC, Ed25519, generate_randomness
from ..forms import PasswordLessUserCreationForm, RequestForm
from ..models import SQRLIdentity, SQRLNut
from ..utils import Base64, Encoder
TESTING_MODULE = 'sqrl.forms'
class TestRequestForm(test.TestCase):
def get_key_pair(self):
signing_key, verifying_key = ed25519.create_keypair()
signing_key = signing_key.to_bytes()
verifying_key = verifying_key.to_bytes()
return signing_key, verifying_key
def _setup(self):
hasattr(self, 'nut') and self.nut.delete()
hasattr(self, 'user') and self.user.delete()
hasattr(self, 'identity') and self.identity.delete()
self.nut = SQRLNut(
nonce=generate_randomness(),
transaction_nonce=generate_randomness(),
session_key=generate_randomness(20),
is_transaction_complete=False,
ip_address='127.0.0.1',
timestamp=now(),
)
self.nut.save()
self.user = get_user_model().objects.create(
username='test_clean_session',
)
self.identity = SQRLIdentity(
user_id=self.user.pk,
public_key=Base64.encode(self.public_key),
server_unlock_key=Base64.encode(self.server_unlock_key),
verify_unlock_key=Base64.encode(self.verify_unlock_key),
is_enabled=True,
is_only_sqrl=False,
)
self.identity.save()
self.server_data = OrderedDict([
('ver', 1),
('nut', self.nut.nonce),
('tif', '8'),
('qry', '/sqrl/auth/?nut=nonce'),
('sfn', 'Test Server'),
])
self.server_data['mac'] = HMAC(self.nut, self.server_data).sign_data()
self.server_data = Encoder.normalize(self.server_data)
self.client_data = OrderedDict([
('ver', 1),
('cmd', self.cmd),
('opt', ['sqrlonly']),
])
if self.include_idk:
self.client_data['idk'] = self.public_key
if self.include_pidk:
self.client_data['pidk'] = self.previous_public_key
if self.include_suk:
self.client_data['suk'] = self.server_unlock_key
if self.include_vuk:
self.client_data['vuk'] = self.verify_unlock_key
self.payload_client_data = Encoder.normalize(OrderedDict(
(k, v if not isinstance(v, list) else '~'.join(v))
for k, v in self.client_data.items()
))
self.data = {
'client': Encoder.base64_dumps(self.client_data),
'server': Encoder.base64_dumps(self.server_data),
}
self.signable_data = (
self.data['client'] + self.data['server']
).encode('ascii')
if self.include_ids:
self.data['ids'] = Ed25519(
self.public_key, self.identity_key, self.signable_data
).sign_data()
if self.include_pids:
self.data['pids'] = Ed25519(
self.previous_public_key, self.previous_identity_key, self.signable_data
).sign_data()
if self.include_urs:
self.data['urs'] = Ed25519(
self.verify_unlock_key, self.unlock_key, self.signable_data
).sign_data()
self.cleaned_data = self.data.copy()
self.cleaned_data.update({
'client': self.client_data,
'server': self.server_data,
})
self.form = RequestForm(self.nut, data=self.data)
def setUp(self):
super(TestRequestForm, self).setUp()
self.cmd = ['query']
self.identity_key, self.public_key = self.get_key_pair()
self.previous_identity_key, self.previous_public_key = self.get_key_pair()
self.unlock_key, self.verify_unlock_key = self.get_key_pair()
self.server_unlock_key = b'hello'
self.include_idk = True
self.include_pidk = True
self.include_suk = True
self.include_vuk = True
self.include_ids = True
self.include_pids = True
self.include_urs = True
self._setup()
def tearDown(self):
self.user and self.user.delete()
self.identity and self.identity.delete()
self.nut and self.nut.delete()
super(TestRequestForm, self).tearDown()
def test_init(self):
form = RequestForm(mock.sentinel.nut)
self.assertEqual(form.nut, mock.sentinel.nut)
self.assertIsNone(form.session)
self.assertIsNone(form.identity)
self.assertIsNone(form.previous_identity)
def test_clean_client(self):
self.form.cleaned_data = {'client': self.payload_client_data}
self.assertEqual(self.form.clean_client(), dict(self.client_data))
def test_clean_client_invalid(self):
self.form.cleaned_data = {
'client': {
'ver': '2',
}
}
with self.assertRaises(forms.ValidationError):
self.form.clean_client()
def test_clean_server_not_dict(self):
self.form.cleaned_data = {'server': mock.sentinel.server_data}
self.assertEqual(self.form.clean_server(), mock.sentinel.server_data)
def test_clean_server(self):
self.form.cleaned_data = {
'server': self.server_data
}
self.assertEqual(
self.form.clean_server(),
self.server_data
)
def test_clean_server_mac_not_base64(self):
self.server_data['mac'] = 'hello'
self.form.cleaned_data = {'server': self.server_data}
with self.assertRaises(forms.ValidationError):
self.form.clean_server()
def test_clean_server_mismatch_nut(self):
self.server_data['nut'] = self.server_data['nut'][::-1]
self.form.cleaned_data = {'server': self.server_data}
with self.assertRaises(forms.ValidationError):
self.form.clean_server()
def test_clean_server_mismatch_missing_mac(self):
del self.server_data['mac']
self.form.cleaned_data = {'server': self.server_data}
with self.assertRaises(forms.ValidationError):
self.form.clean_server()
def test_clean_server_invalid_mac(self):
self.server_data['mac'] = self.server_data['mac'][::-1]
self.form.cleaned_data = {'server': self.server_data}
with self.assertRaises(forms.ValidationError):
self.form.clean_server()
def test_clean_ids(self):
self.form.cleaned_data = {
'client': self.client_data,
'ids': self.data['ids'],
}
self.assertEqual(
self.form.clean_ids(),
self.data['ids']
)
def test_clean_ids_invalid(self):
self.form.cleaned_data = {
'client': self.client_data,
'ids': self.data['ids'][::-1],
}
with self.assertRaises(forms.ValidationError):
self.form.clean_ids()
def test_clean_pids_valid(self):
self.form.cleaned_data = {
'client': self.client_data,
'pids': self.data['pids'],
}
self.assertEqual(
self.form.clean_pids(),
self.data['pids']
)
def test_clean_pids_invalid(self):
self.form.cleaned_data = {
'client': self.client_data,
'pids': self.data['pids'][::-1],
}
with self.assertRaises(forms.ValidationError):
self.form.clean_pids()
def test_clean_pids_missing_pids(self):
self.form.cleaned_data = {
'client': self.client_data,
}
with self.assertRaises(forms.ValidationError):
self.form.clean_pids()
def test_clean_pids_missing_pidk(self):
self.client_data.pop('pidk')
self.form.cleaned_data = {
'client': self.client_data,
'pids': self.data['pids'],
}
with self.assertRaises(forms.ValidationError):
self.form.clean_pids()
def test_clean_urs(self):
self.form.cleaned_data = self.cleaned_data
self.form.identity = self.identity
self.assertEqual(
self.form._clean_urs(),
self.data['urs']
)
def test_clean_urs_invalid(self):
self.form.cleaned_data = {
'client': self.client_data,
'urs': self.data['urs'][::-1],
}
self.form.identity = self.identity
with self.assertRaises(forms.ValidationError):
self.form._clean_urs()
def test_clean_urs_no_suk(self):
self.form.cleaned_data = {
'client': self.client_data,
'urs': self.data['urs'],
}
self.form.identity = self.identity
self.identity.server_unlock_key = None
with self.assertRaises(forms.ValidationError):
self.form._clean_urs()
def test_clean_urs_no_vuk(self):
self.form.cleaned_data = {
'client': self.client_data,
'urs': self.data['urs'],
}
self.form.identity = self.identity
self.identity.verify_unlock_key = None
with self.assertRaises(forms.ValidationError):
self.form._clean_urs()
def test_clean_cmd_query(self):
self.cmd = ['query']
self._setup()
self.form.cleaned_data = {
'client': self.client_data,
}
self.assertIsNone(self.form._clean_client_cmd())
def test_clean_cmd_ident(self):
self.cmd = ['ident']
self._setup()
self.form.cleaned_data = {
'client': self.client_data,
}
self.assertIsNone(self.form._clean_client_cmd())
def test_clean_cmd_ident_no_suk_vuk_without_identity(self):
self.cmd = ['ident']
self.include_suk = None
self.include_vuk = None
self._setup()
self.form.cleaned_data = {
'client': self.client_data,
}
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_ident_suk_vuk_with_identity(self):
self.cmd = ['ident']
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = {
'client': self.client_data,
}
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_ident_with_disable(self):
self.cmd = ['ident', 'disable']
self._setup()
self.form.cleaned_data = {
'client': self.client_data,
}
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_ident_no_urs_with_previous_identity(self):
self.cmd = ['ident']
self._setup()
self.form.previous_identity = self.identity
self.form.cleaned_data = {
'client': self.client_data,
}
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_disable(self):
self.cmd = ['disable']
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = {
'client': self.client_data,
}
self.assertIsNone(self.form._clean_client_cmd())
def test_clean_cmd_disable_no_identity(self):
self.cmd = ['disable']
self._setup()
self.form.cleaned_data = {
'client': self.client_data,
}
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_disable_with_enable(self):
self.cmd = ['disable', 'enable']
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = {
'client': self.client_data,
}
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_enable(self):
self.cmd = ['enable']
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = self.cleaned_data
self.assertIsNone(self.form._clean_client_cmd())
def test_clean_cmd_enable_no_urs(self):
self.cmd = ['enable']
self.include_urs = False
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_enable_no_identity(self):
self.cmd = ['enable']
self._setup()
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_enable_with_disable(self):
self.cmd = ['enable', 'disable']
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_remove(self):
self.cmd = ['remove']
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = self.cleaned_data
self.assertIsNone(self.form._clean_client_cmd())
def test_clean_cmd_remove_no_identity(self):
self.cmd = ['remove']
self._setup()
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_remove_no_urs(self):
self.cmd = ['remove']
self.include_urs = False
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_cmd_remove_with_other_cmd(self):
self.cmd = ['remove', 'ident']
self._setup()
self.form.identity = self.identity
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_client_cmd()
def test_clean_session_empty(self):
self.form.session = {}
self.assertIsNone(self.form._clean_session())
def test_clean_session_user_not_found(self):
assert not get_user_model().objects.filter(pk=1000).first()
self.form.session = {
SESSION_KEY: '1000',
}
self.assertIsNone(self.form._clean_session())
def test_clean_session_user_not_int(self):
self.form.session = {
SESSION_KEY: 'aaa',
}
self.assertIsNone(self.form._clean_session())
def test_clean_session_no_sqrl_identity(self):
self.identity.delete()
self.identity = None
self.form.session = {
SESSION_KEY: str(self.user.pk),
}
self.assertIsNone(self.form._clean_session())
def test_clean_session_public_key_not_matches(self):
self.identity.public_key = self.identity.public_key[::-1]
self.identity.save()
self.form.session = {
SESSION_KEY: str(self.user.pk),
}
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_session()
def test_clean_session_user_code_mismatch(self):
self.form.identity = self.identity
self.form.session = {
SESSION_KEY: str(self.user.pk + 1),
}
self.form.cleaned_data = self.cleaned_data
with self.assertRaises(forms.ValidationError):
self.form._clean_session()
def test_clean(self):
self.form.cleaned_data = self.cleaned_data
actual = self.form.clean()
self.assertEqual(actual, self.cleaned_data)
@mock.patch.object(RequestForm, 'find_identities')
@mock.patch.object(RequestForm, '_clean_client_cmd')
@mock.patch.object(RequestForm, '_clean_urs')
@mock.patch.object(RequestForm, 'find_session')
@mock.patch.object(RequestForm, '_clean_session')
@mock.patch.object(forms.Form, 'clean')
def test_clean_mock(self,
mock_super_clean,
mock_clean_session,
mock_find_session,
mock_clean_urs,
mock_clean_client_cmd,
mock_find_identities):
mock_super_clean.return_value = mock.sentinel.cleaned_data
actual = self.form.clean()
self.assertEqual(actual, mock.sentinel.cleaned_data)
mock_super_clean.assert_called_once_with()
mock_clean_session.assert_called_once_with()
mock_find_session.assert_called_once_with()
mock_clean_urs.assert_called_once_with()
mock_clean_client_cmd.assert_called_once_with()
mock_find_identities.assert_called_once_with()
@mock.patch(TESTING_MODULE + '.SessionMiddleware')
def test_find_session(self, mock_session_middleware):
self.form.find_session()
self.assertEqual(
self.form.session,
mock_session_middleware.return_value.SessionStore.return_value
)
mock_session_middleware.return_value.SessionStore.assert_called_once_with(
self.nut.session_key,
)
def test_find_identities(self):
self.form.cleaned_data = self.cleaned_data
self.form.find_identities()
self.assertIsNotNone(self.form.identity)
self.assertIsInstance(self.form.identity, SQRLIdentity)
self.assertEqual(self.form.identity.public_key, self.identity.public_key)
self.assertIsNone(self.form.previous_identity)
@mock.patch.object(RequestForm, '_get_identity')
def test_find_identities_mock(self, mock_get_identity):
self.form.cleaned_data = self.cleaned_data
mock_get_identity.side_effect = mock.sentinel.identity, mock.sentinel.previous_identity
self.form.find_identities()
self.assertEqual(self.form.identity, mock.sentinel.identity)
self.assertEqual(self.form.previous_identity, mock.sentinel.previous_identity)
mock_get_identity.assert_has_calls([
mock.call(self.public_key),
mock.call(self.previous_public_key),
])
@mock.patch(TESTING_MODULE + '.SQRLIdentity')
def test_get_identity(self, mock_sqrl_identity):
actual = self.form._get_identity(self.public_key)
self.assertEqual(
actual,
mock_sqrl_identity.objects.filter.return_value.first.return_value
)
mock_sqrl_identity.objects.filter.assert_called_once_with(
public_key=Base64.encode(self.public_key)
)
def test_get_identity_no_key(self):
self.assertIsNone(self.form._get_identity(None))
class TestRandomPasswordUserCreationForm(unittest.TestCase):
def test_init(self):
self.assertIn('password1', PasswordLessUserCreationForm.base_fields)
self.assertIn('password2', PasswordLessUserCreationForm.base_fields)
form = PasswordLessUserCreationForm()
self.assertNotIn('password1', form.fields)
self.assertNotIn('password2', form.fields)
def test_save(self):
form = PasswordLessUserCreationForm({'username': 'test'})
self.assertTrue(form.is_valid())
user = form.save()
self.assertEqual(user.username, 'test')
self.assertTrue(user.password.startswith('!'))
user.delete()

View file

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import unittest
import mock
from ..managers import SQRLNutManager
class TestSQRLNutManager(unittest.TestCase):
@mock.patch.object(SQRLNutManager, 'get_queryset')
@mock.patch.object(SQRLNutManager, 'create')
def test_replace_or_create(self, mock_create, mock_getqueryset):
actual = SQRLNutManager().replace_or_create(
session_key=mock.sentinel.session_key,
nonce=mock.sentinel.nonce,
transaction_nonce=mock.sentinel.transaction_nonce,
is_transaction_complete=False,
ip_address=mock.sentinel.ip_address,
)
self.assertEqual(actual, mock_create.return_value)
mock_getqueryset.return_value.filter.assert_called_once_with(
session_key=mock.sentinel.session_key
)
mock_getqueryset.return_value.filter.return_value.delete.assert_called_once_with()
mock_create.assert_called_once_with(
session_key=mock.sentinel.session_key,
nonce=mock.sentinel.nonce,
transaction_nonce=mock.sentinel.transaction_nonce,
is_transaction_complete=False,
ip_address=mock.sentinel.ip_address,
)

32
sqrl/tests/test_models.py Normal file
View file

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import unittest
import mock
from ..managers import SQRLNutManager
from ..models import SQRLNut
TESTING_MODULE = 'sqrl.models'
class TestSQRLNut(unittest.TestCase):
def test_objects(self):
self.assertIsInstance(SQRLNut.objects, SQRLNutManager)
def test_str(self):
self.assertEqual(
str(SQRLNut(nonce='nonce')),
'nonce'
)
@mock.patch(TESTING_MODULE + '.generate_randomness')
@mock.patch.object(SQRLNut, 'delete')
@mock.patch.object(SQRLNut, 'save')
def test_renew(self, mock_save, mock_delete, mock_generate_randomness):
nut = SQRLNut(nonce='nonce')
self.assertIsNone(nut.renew())
self.assertEqual(nut.nonce, mock_generate_randomness.return_value)
mock_save.assert_called_once_with()
mock_generate_randomness.assert_called_once_with()

View file

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
import unittest
from collections import OrderedDict
import mock
from django.test.utils import override_settings
from ..crypto import HMAC
from ..response import SQRLHttpResponse
from ..utils import Encoder
TESTING_MODULE = 'sqrl.response'
class TestSQRLHttpResponse(unittest.TestCase):
def setUp(self):
super(TestSQRLHttpResponse, self).setUp()
self.data = OrderedDict([
('ver', 1),
('nut', b'nonce'),
('tif', '8'),
('qry', '/sqrl/auth/?nut=nonce'),
('sfn', 'Test Server'),
])
self.nut = mock.MagicMock(session_key='session')
@override_settings(DEBUG=False)
def test_response(self):
response = SQRLHttpResponse(self.nut, self.data)
expected_data = self.data.copy()
expected_data['mac'] = HMAC(self.nut, self.data).sign_data()
self.assertEqual(
response.content,
Encoder.base64_dumps(expected_data).encode('ascii')
)
self.assertEqual(response['Content-Length'], str(len(response.content)))
self.assertEqual(response['Content-Type'], 'application/sqrl')
@override_settings(DEBUG=False)
def test_response_without_nut(self):
response = SQRLHttpResponse(None, self.data)
self.assertEqual(
response.content,
Encoder.base64_dumps(self.data).encode('ascii')
)
self.assertEqual(response['Content-Length'], str(len(response.content)))
self.assertEqual(response['Content-Type'], 'application/sqrl')
@override_settings(DEBUG=True)
@mock.patch(TESTING_MODULE + '.log')
def test_response_debug(self, mock_log):
response = SQRLHttpResponse(self.nut, self.data)
self.assertEqual(response['X-SQRL-ver'], '1')
self.assertEqual(response['X-SQRL-nut'], 'bm9uY2U')
self.assertEqual(response['X-SQRL-tif'], '8')
self.assertEqual(response['X-SQRL-qry'], '/sqrl/auth/?nut=nonce')
self.assertEqual(response['X-SQRL-sfn'], 'Test Server')
self.assertIn('X-SQRL-mac', response)

116
sqrl/tests/test_sqrl.py Normal file
View file

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
import unittest
import mock
from sqrl.managers import SQRLNutManager
from ..sqrl import SQRLInitialization
TESTING_MODULE = 'sqrl.sqrl'
class TestSQRLInitialization(unittest.TestCase):
def test_init(self):
sqrl = SQRLInitialization(mock.sentinel.request, mock.sentinel.nut)
self.assertEqual(sqrl.request, mock.sentinel.request)
self.assertEqual(sqrl.nut, mock.sentinel.nut)
def test_get_or_create_session_key_exists(self):
m = mock.MagicMock()
self.assertEqual(
SQRLInitialization(m).get_or_create_session_key(),
m.session.session_key
)
self.assertFalse(m.session.create.called)
def test_get_or_create_session_key_create(self):
m = mock.MagicMock()
m.session.session_key = None
self.assertEqual(
SQRLInitialization(m).get_or_create_session_key(),
m.session.session_key
)
m.session.create.assert_called_once_with()
@mock.patch.object(SQRLInitialization, 'generate_nut_kwargs')
@mock.patch.object(SQRLNutManager, 'replace_or_create')
def test_nut(self, mock_replace_or_create, mock_generate_nut_kwargs):
mock_generate_nut_kwargs.return_value = {
'foo': 'bar'
}
sqrl = SQRLInitialization(None)
self.assertEqual(sqrl.nut, mock_replace_or_create.return_value)
mock_replace_or_create.assert_called_once_with(foo='bar')
mock_generate_nut_kwargs.assert_called_once_with()
def test_nut_setter(self):
sqrl = SQRLInitialization(None)
# sanity check
self.assertFalse(hasattr(sqrl, '_nut'))
sqrl.nut = mock.sentinel.nut
self.assertTrue(hasattr(sqrl, '_nut'))
self.assertEqual(sqrl._nut, mock.sentinel.nut)
self.assertEqual(sqrl.nut, mock.sentinel.nut)
@mock.patch.object(SQRLInitialization, 'get_or_create_session_key')
@mock.patch(TESTING_MODULE + '.get_user_ip')
@mock.patch(TESTING_MODULE + '.generate_randomness')
def test_generate_nut_kwargs(self, mock_generate_randomness, mock_get_user_ip,
mock_get_or_create_session_key):
mock_generate_randomness.return_value = 'abc123'
actual = SQRLInitialization(mock.sentinel.request).generate_nut_kwargs()
self.assertDictEqual(
actual, {
'session_key': mock_get_or_create_session_key.return_value,
'nonce': 'abc',
'transaction_nonce': '123',
'is_transaction_complete': False,
'ip_address': mock_get_user_ip.return_value,
}
)
mock_generate_randomness.assert_called_once_with(64)
mock_get_user_ip.assert_called_once_with(mock.sentinel.request)
@mock.patch(TESTING_MODULE + '.reverse')
def test_get_sqrl_url(self, mock_reverse):
actual = SQRLInitialization(None).get_sqrl_url()
self.assertEqual(actual, mock_reverse.return_value)
def test_get_sqrl_url_params(self):
actual = SQRLInitialization(None, mock.MagicMock(nonce='foo&bar')).get_sqrl_url_params()
self.assertEqual(actual, 'nut=foo%26bar')
@mock.patch.object(SQRLInitialization, 'get_sqrl_url_params')
@mock.patch.object(SQRLInitialization, 'get_sqrl_url')
def test_url(self, mock_get_sqrl_url, mock_get_sqrl_url_params):
mock_get_sqrl_url.return_value = '/sqrl/auth/'
mock_get_sqrl_url_params.return_value = 'nut=nonce'
actual = SQRLInitialization(None).url
self.assertEqual(actual, '/sqrl/auth/?nut=nonce')
@mock.patch.object(SQRLInitialization, 'url', new_callable=mock.PropertyMock)
def test_sqrl_url(self, mock_url):
mock_url.return_value = '/sqrl/auth/?nut=nonce'
request = mock.MagicMock()
request.is_secure.return_value = True
request.get_host.return_value = 'example.com:8000'
actual = SQRLInitialization(request).sqrl_url
self.assertEqual(actual, 'sqrl://example.com:8000/sqrl/auth/?nut=nonce')

7
sqrl/tests/test_urls.py Normal file
View file

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from .. import urls # noqa
# nothing to test here however simply importing
# url module evals urls so coverage is higher

80
sqrl/tests/test_utils.py Normal file
View file

@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
import unittest
from collections import OrderedDict
import mock
from ..utils import Base64, Encoder, get_user_ip
TESTING_MODULE = 'sqrl.utils'
class TestBase64(unittest.TestCase):
def test_encode(self):
value = Base64.encode(b'hello')
# normal base64 is aGVsbG8= however = should be missing
self.assertEqual(value, 'aGVsbG8')
self.assertIsInstance(value, str)
def test_encode_not_binary(self):
with self.assertRaises(AssertionError):
Base64.encode('hello')
def test_decode(self):
value = Base64.decode('aGVsbG8')
# normal base64 is aGVsbG8= however = should be missing
self.assertEqual(value, b'hello')
self.assertIsInstance(value, bytes)
def test_decode_not_unicode(self):
with self.assertRaises(AssertionError):
Base64.decode(b'aGVsbG8')
class TestEncode(unittest.TestCase):
def test_base64_dumps(self):
self.assertEqual(Encoder.base64_dumps(5), '5')
self.assertEqual(Encoder.base64_dumps('hello'), 'hello')
self.assertEqual(Encoder.base64_dumps(b'hello'), 'aGVsbG8')
self.assertEqual(Encoder.base64_dumps([b'hello', 'hello']), 'aGVsbG8~hello')
self.assertEqual(
Encoder.base64_dumps(OrderedDict([('a', b'hello'), ('b', 'hello')])),
'YT1hR1ZzYkc4DQpiPWhlbGxvDQo'
)
def test_dumps(self):
self.assertEqual(Encoder.dumps(5), '5')
self.assertEqual(Encoder.dumps('hello'), 'hello')
self.assertEqual(Encoder.dumps(b'hello'), 'aGVsbG8')
self.assertEqual(Encoder.dumps([b'hello', 'hello']), 'aGVsbG8~hello')
self.assertEqual(Encoder.dumps(OrderedDict()), '')
self.assertEqual(
Encoder.dumps(OrderedDict([('a', b'hello'), ('b', 'hello')])),
'a=aGVsbG8\r\nb=hello\r\n'
)
def test_normalize(self):
self.assertEqual(Encoder.normalize(b'hello'), 'aGVsbG8')
self.assertEqual(Encoder.normalize('hello'), 'hello')
self.assertEqual(Encoder.normalize(5), '5')
self.assertEqual(Encoder.normalize([b'hello', 'hello']), ['aGVsbG8', 'hello'])
self.assertEqual(Encoder.normalize(OrderedDict()), '')
self.assertEqual(
Encoder.normalize(OrderedDict([('a', b'hello'), ('b', 'hello')])),
OrderedDict([('a', 'aGVsbG8'), ('b', 'hello')])
)
class TestUtils(unittest.TestCase):
def test_get_user_ip(self):
request = mock.MagicMock(META={'REMOTE_ADDR': mock.sentinel.ip})
self.assertEqual(get_user_ip(request), mock.sentinel.ip)
def test_get_user_ip_proxy(self):
request = mock.MagicMock(META={'HTTP_X_REAL_IP': mock.sentinel.ip})
self.assertEqual(get_user_ip(request), mock.sentinel.ip)

194
sqrl/tests/test_views.py Normal file
View file

@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
import json
import unittest
import mock
from django import test
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core import serializers
from django.urls import reverse
from django.http import Http404, QueryDict
from django.views.generic import FormView
from ..models import SQRLIdentity, SQRLNut
from ..views import (
SQRL_IDENTITY_SESSION_KEY,
SQRLCompleteRegistrationView,
SQRLStatusView,
)
TESTING_MODULE = 'sqrl.views'
class TestSQRLStatusView(test.TestCase):
def setUp(self):
super(TestSQRLStatusView, self).setUp()
self.view = SQRLStatusView()
self.view.request = mock.MagicMock(GET={})
self.view.request.is_ajax.return_value = True
self.view.kwargs = {
'transaction': '123',
}
self.nut = SQRLNut.objects.create(
nonce='hello',
transaction_nonce='123',
ip_address='127.0.0.1',
is_transaction_complete=True,
)
def tearDown(self):
self.nut and self.nut.delete()
super(TestSQRLStatusView, self).tearDown()
def test_get_success_url(self):
self.assertEqual(
self.view.get_success_url(),
settings.LOGIN_REDIRECT_URL
)
def test_get_success_url_from_querystring(self):
self.view.request.GET['url'] = '?next={}'.format(reverse('sqrl:login'))
self.assertEqual(
self.view.get_success_url(),
reverse('sqrl:login'),
)
def test_get_success_url_complete_registration(self):
self.view.request.GET['url'] = '?next={}'.format(reverse('sqrl:login'))
self.view.request.user.is_authenticated.return_value = False
self.view.request.session = {SQRL_IDENTITY_SESSION_KEY: ''}
self.assertEqual(
self.view.get_success_url(),
reverse('sqrl:complete-registration') + '?next={}'.format(reverse('sqrl:login')),
)
def test_get_object_404(self):
self.nut.delete()
self.nut = None
with self.assertRaises(Http404):
self.view.get_object()
def test_get_object(self):
actual = self.view.get_object()
self.assertIsInstance(actual, SQRLNut)
self.assertEqual(actual.nonce, self.nut.nonce)
self.assertEqual(actual.transaction_nonce, self.nut.transaction_nonce)
def test_post(self):
response = self.view.post(self.view.request, self.nut.transaction_nonce)
self.assertEqual(response.status_code, 200)
self.assertEqual(response['content-type'], 'application/json')
self.assertEqual(json.loads(response.content.decode('utf-8')), {
'transaction_complete': True,
'redirect_to': settings.LOGIN_REDIRECT_URL,
})
def test_post_not_ajax(self):
self.view.request.is_ajax.return_value = False
response = self.view.post(self.view.request, self.nut.transaction_nonce)
self.assertEqual(response.status_code, 405)
class TestSQRLAuthView(test.TestCase):
pass
class TestCompleteRegistrationView(test.TestCase):
def setUp(self):
super(TestCompleteRegistrationView, self).setUp()
self.view = SQRLCompleteRegistrationView()
self.username = 'foobartest'
self.view.request = mock.MagicMock(
method='POST',
POST={
'username': self.username,
},
)
self.identity = SQRLIdentity(
public_key='a' * 43,
verify_unlock_key='b' * 43,
server_unlock_key='c' * 43,
is_enabled=True,
is_only_sqrl=False,
)
self.view.request.session = {
SQRL_IDENTITY_SESSION_KEY: serializers.serialize('json', [self.identity]),
}
def tearDown(self):
SQRLIdentity.objects.filter(public_key=self.identity.public_key).delete()
get_user_model().objects.filter(username=self.username).delete()
super(TestCompleteRegistrationView, self).tearDown()
def test_check_session_for_sqrl_identity_or_404(self):
self.assertIsNone(self.view.check_session_for_sqrl_identity_or_404())
def test_check_session_for_sqrl_identity_or_404_raises(self):
self.view.request.session = {}
with self.assertRaises(Http404):
self.view.check_session_for_sqrl_identity_or_404()
@mock.patch.object(SQRLCompleteRegistrationView, 'check_session_for_sqrl_identity_or_404')
@mock.patch.object(FormView, 'get')
def test_get(self, mock_super_get, mock_check_session_for_sqrl_identity_or_404):
response = self.view.get(self.view.request)
self.assertEqual(response, mock_super_get.return_value)
mock_check_session_for_sqrl_identity_or_404.assert_called_once_with()
@mock.patch.object(SQRLCompleteRegistrationView, 'check_session_for_sqrl_identity_or_404')
@mock.patch.object(FormView, 'post')
def test_post(self, mock_super_post, mock_check_session_for_sqrl_identity_or_404):
response = self.view.post(self.view.request)
self.assertEqual(response, mock_super_post.return_value)
mock_check_session_for_sqrl_identity_or_404.assert_called_once_with()
def test_get_success_url(self):
self.assertEqual(
self.view.get_success_url(),
settings.LOGIN_REDIRECT_URL
)
def test_get_success_url_from_querystring(self):
self.view.request.GET = {'next': reverse('sqrl:manage')}
self.assertEqual(
self.view.get_success_url(),
reverse('sqrl:manage'),
)
@mock.patch(TESTING_MODULE + '.login')
def test_form_valid(self, mock_login):
form = self.view.get_form(self.view.get_form_class())
form.is_valid()
# sanity checks
self.assertFalse(get_user_model().objects.filter(username=self.username).count())
self.assertFalse(SQRLIdentity.objects.filter(public_key=self.identity.public_key).count())
response = self.view.form_valid(form)
user = get_user_model().objects.filter(username=self.username).first()
self.assertIsNotNone(user)
self.assertEqual(user.username, self.username)
self.assertIsInstance(user.sqrl_identity, SQRLIdentity)
self.assertEqual(user.sqrl_identity.public_key, self.identity.public_key)
self.assertEqual(response.status_code, 302)
def test_form_valid_could_not_decode_identity(self):
self.view.request.session[SQRL_IDENTITY_SESSION_KEY] = ''
response = self.view.form_valid(self.view.get_form(self.view.get_form_class()))
self.assertEqual(response.status_code, 500)