import json from django.test import Client, RequestFactory from nacl.encoding import HexEncoder from nacl.signing import SigningKey from authentication.models import ToolshedUser, KnownIdentity from authentication.tests import UserTestMixin, SignatureAuthClient, DummyExternalUser, ToolshedTestCase class AuthorizationTestCase(ToolshedTestCase): def setUp(self): self.client = Client() self.factory = RequestFactory() from nacl.signing import SigningKey self.key = SigningKey.generate() self.signature = self.key.sign("test".encode('utf-8'), encoder=HexEncoder).signature.decode('utf-8') self.data = json.dumps({'a': 'b'}) self.signature_with_data = self.key.sign( ("test" + self.data).encode('utf-8'), encoder=HexEncoder).signature.decode('utf-8') def test_parse_auth_header(self): request = self.factory.get('/test') from authentication.signature_auth import verify_request with self.assertRaises(ValueError): verify_request(request, "") def test_parse_auth_header2(self): request = self.factory.get('/test', HTTP_AUTHORIZATION="Signature ") from authentication.signature_auth import verify_request with self.assertRaises(ValueError): verify_request(request, "") def test_parse_auth_header3(self): request = self.factory.get('/test', HTTP_AUTHORIZATION="Signature author@domain") from authentication.signature_auth import verify_request with self.assertRaises(ValueError): verify_request(request, "") def test_parse_auth_header4(self): from authentication.signature_auth import verify_request request = self.factory.get('/test', HTTP_AUTHORIZATION="Signature author@domain:" + self.signature) username, domain, signed_data, signature_bytes_hex = verify_request(request, "") self.assertEqual(username, "author") self.assertEqual(domain, "domain") self.assertEqual(signed_data, "http://testserver/test") self.assertEqual(signature_bytes_hex, self.signature) def test_parse_auth_header5(self): from authentication.signature_auth import verify_request request = self.factory.post('/test', self.data, content_type="application/json", HTTP_AUTHORIZATION="Signature author@domain:" + self.signature_with_data) username, domain, signed_data, signature_bytes_hex = verify_request(request, request.body.decode('utf-8')) self.assertEqual(username, "author") self.assertEqual(domain, "domain") self.assertEqual(signed_data, "http://testserver/test" + self.data) self.assertEqual(signature_bytes_hex, self.signature_with_data) def test_parse_auth_header6(self): from authentication.signature_auth import verify_request request = self.factory.put('/test', self.data, content_type="application/json", HTTP_AUTHORIZATION="Signature author@domain:" + self.signature_with_data) username, domain, signed_data, signature_bytes_hex = verify_request(request, request.body.decode('utf-8')) self.assertEqual(username, "author") self.assertEqual(domain, "domain") self.assertEqual(signed_data, "http://testserver/test" + self.data) self.assertEqual(signature_bytes_hex, self.signature_with_data) def test_parse_auth_header7(self): from authentication.signature_auth import verify_request request = self.factory.delete('/test', HTTP_AUTHORIZATION="Signature author@domain:" + self.signature) username, domain, signed_data, signature_bytes_hex = verify_request(request, request.body.decode('utf-8')) self.assertEqual(username, "author") self.assertEqual(domain, "domain") self.assertEqual(signed_data, "http://testserver/test") self.assertEqual(signature_bytes_hex, self.signature) def test_parse_auth_header8(self): from authentication.signature_auth import verify_request request = self.factory.patch('/test', self.data, content_type="application/json", HTTP_AUTHORIZATION="Signature author@domain:" + self.signature_with_data) username, domain, signed_data, signature_bytes_hex = verify_request(request, request.body.decode('utf-8')) self.assertEqual(username, "author") self.assertEqual(domain, "domain") self.assertEqual(signed_data, "http://testserver/test" + self.data) self.assertEqual(signature_bytes_hex, self.signature_with_data) class KnownIdentityTestCase(ToolshedTestCase): key = None def setUp(self): self.key = SigningKey.generate() KnownIdentity.objects.create(username="testuser", domain='external.com', public_key=self.key.verify_key.encode(encoder=HexEncoder).decode('utf-8')) def test_known_identity(self): identity = KnownIdentity.objects.get(username="testuser", domain='external.com') self.assertEqual(identity.username, "testuser") self.assertEqual(identity.domain, "external.com") self.assertEqual(identity.public_key, self.key.verify_key.encode(encoder=HexEncoder).decode('utf-8')) self.assertEqual(str(identity), "testuser@external.com") self.assertTrue(identity.is_authenticated()) def test_known_identity_verify(self): identity = KnownIdentity.objects.get(username="testuser", domain='external.com') message = "Hello world, this is a test message." signed = self.key.sign(message.encode('utf-8'), encoder=HexEncoder).signature self.assertTrue(identity.verify(message, signed.decode('utf-8'))) def test_known_identity_verify_fail(self): identity = KnownIdentity.objects.get(username="testuser", domain='external.com') message = "Hello world, this is a test message." signed = self.key.sign(message.encode('utf-8'), encoder=HexEncoder).signature self.assertFalse(identity.verify(message + "x", signed.decode('utf-8'))) def test_known_identity_verify_fail2(self): identity = KnownIdentity.objects.get(username="testuser", domain='external.com') message = "Hello world, this is a test message." signed = self.key.sign(message.encode('utf-8'), encoder=HexEncoder).signature with self.assertRaises(TypeError): identity.verify(message.encode('utf-8'), signed.decode('utf-8')) def test_known_identity_verify_fail3(self): identity = KnownIdentity.objects.get(username="testuser", domain='external.com') message = "Hello world, this is a test message." signed = self.key.sign(message.encode('utf-8'), encoder=HexEncoder).signature with self.assertRaises(TypeError): identity.verify(message, signed) def test_known_identity_verify_fail4(self): identity = KnownIdentity.objects.get(username="testuser", domain='external.com') message = "Hello world, this is a test message." signed = self.key.sign(message.encode('utf-8'), encoder=HexEncoder).signature with self.assertRaises(TypeError): identity.verify(message, bytes.fromhex(signed.decode('utf-8'))) class UserModelTestCase(UserTestMixin, ToolshedTestCase): def setUp(self): super().setUp() self.prepare_users() def test_admin(self): user = self.f['admin'] self.assertTrue(user.is_superuser) self.assertTrue(user.is_staff) self.assertTrue(user.is_active) self.assertEqual(user.domain, 'localhost') self.assertEqual(user.email, 'testadmin@localhost') self.assertEqual(user.username, 'testadmin') def test_user(self): user = self.f['local_user1'] self.assertFalse(user.is_superuser) self.assertFalse(user.is_staff) self.assertTrue(user.is_active) self.assertEqual(user.domain, 'example.com') self.assertEqual(user.email, 'test1@abc.de') self.assertEqual(user.username, 'testuser1') self.assertEqual(len(user.private_key), 64) self.assertEqual(type(user.public_identity), KnownIdentity) self.assertEqual(user.public_identity.domain, 'example.com') self.assertEqual(user.public_identity.username, 'testuser1') self.assertEqual(len(user.public_identity.public_key), 64) def test_create_existing_user(self): with self.assertRaises(ValueError): ToolshedUser.objects.create_user('testuser1', 'test3@abc.de', 'testpassword', domain='example.com') def test_create_existing_user2(self): key = SigningKey.generate() KnownIdentity.objects.create(username="testuser3", domain='localhost', public_key=key.verify_key.encode(encoder=HexEncoder).decode('utf-8')) with self.assertRaises(ValueError): ToolshedUser.objects.create_user('testuser3', 'test3@abc.de', 'testpassword', domain='localhost') def test_create_reuse_email(self): with self.assertRaises(ValueError): ToolshedUser.objects.create_user('testuser3', 'test1@abc.de', 'testpassword', domain='example.com') def test_create_user_invalid_private_key(self): with self.assertRaises(TypeError): ToolshedUser.objects.create_user('testuser3', 'test3@abc.de', '', domain='example.com', private_key=b'0123456789abcdef0123456789abcdef') with self.assertRaises(ValueError): ToolshedUser.objects.create_user('testuser3', 'test3@abc.de', '', domain='example.com', private_key='7005c4097') with self.assertRaises(ValueError): ToolshedUser.objects.create_user('testuser3', 'test3@abc.de', '', domain='example.com', private_key='0123456789abcdef0123456789abcdef' 'Z123456789abcdef0123456789abcdef') def test_signature(self): user = self.f['local_user1'] message = 'some message' signature = user.sign(message) self.assertEqual(len(signature), 128) self.assertTrue(user.public_identity.verify(message, signature)) def test_signature_fail(self): user = self.f['local_user1'] message = 'some message' signature = user.sign(message) self.assertFalse(user.public_identity.verify(message + 'x', signature)) def test_signature_fail2(self): user = self.f['local_user1'] message = 'some message' signature = user.sign(message) signature = signature[:-2] + 'ee' self.assertFalse(user.public_identity.verify(message, signature)) def test_signature_fail3(self): user1 = self.f['local_user1'] user2 = self.f['local_user2'] message = 'some message' signature = user1.sign(message) self.assertFalse(user2.public_identity.verify(message, signature)) def test_signature_fail4(self): user = self.f['local_user1'] message = 'some message' with self.assertRaises(TypeError): user.sign(message.encode('utf-8')) class UserApiTestCase(UserTestMixin, ToolshedTestCase): def setUp(self): super().setUp() self.prepare_users() self.anonymous_client = Client(SERVER_NAME='testserver') self.client = SignatureAuthClient() def test_user_info(self): reply = self.client.get('/auth/user/', self.f['local_user1']) self.assertEqual(reply.status_code, 200) self.assertEqual(reply.json()['username'], 'testuser1') self.assertEqual(reply.json()['domain'], 'example.com') self.assertEqual(reply.json()['email'], 'test1@abc.de') def test_user_info2(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + str(self.f['local_user1']) + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 200) self.assertEqual(reply.json()['username'], 'testuser1') self.assertEqual(reply.json()['domain'], 'example.com') def test_user_info_fail(self): reply = self.anonymous_client.get('/auth/user/') self.assertEqual(reply.status_code, 403) def test_user_info_fail2(self): reply = self.client.get('/auth/user/', self.f['ext_user1']) self.assertEqual(reply.status_code, 403) def test_user_info_fail3(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver2" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + str(self.f['local_user1']) + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_user_info_fail4(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Auth ' + str(self.f['local_user1']) + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_user_info_fail5(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + str(self.f['local_user1'])} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_user_info_fail6(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + str(self.f['local_user1']) + ':' + signature + 'f'} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_user_info_fail7(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + self.f['local_user1'].username + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_user_info_fail8(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + self.f['local_user1'].username + '@:' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_user_info_fail9(self): target = "/auth/user/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Signature @' + self.f['local_user1'].domain + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) class FriendApiTestCase(UserTestMixin, ToolshedTestCase): def setUp(self): super().setUp() self.prepare_users() self.f['local_user1'].friends.add(self.f['local_user2'].public_identity) self.f['local_user1'].friends.add(self.f['ext_user1'].public_identity) self.f['ext_user1'].friends.add(self.f['local_user1'].public_identity) self.anonymous_client = Client(SERVER_NAME='testserver') self.client = SignatureAuthClient() def test_friend_local(self): reply = self.client.get('/api/friends/', self.f['local_user1']) self.assertEqual(reply.status_code, 200) def test_friend_external(self): reply = self.client.get('/api/friends/', self.f['ext_user1']) self.assertEqual(reply.status_code, 200) def test_friend_fail(self): reply = self.anonymous_client.get('/api/friends/') self.assertEqual(reply.status_code, 403) def test_friend_fail2(self): target = "/api/friends/" signature = self.f['local_user1'].sign("http://testserver2" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + str(self.f['local_user1']) + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_friend_fail3(self): target = "/api/friends/" unknown_user = DummyExternalUser('extuser3', 'external.org', False) signature = unknown_user.sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Signature ' + str(unknown_user) + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) def test_friend_fail4(self): target = "/api/friends/" signature = self.f['local_user1'].sign("http://testserver" + target) header = {'HTTP_AUTHORIZATION': 'Auth ' + str(self.f['local_user1']) + ':' + signature} reply = self.anonymous_client.get(target, **header) self.assertEqual(reply.status_code, 403) class LoginApiTestCase(UserTestMixin, ToolshedTestCase): user = None client = Client(SERVER_NAME='testserver') def setUp(self): super().setUp() self.prepare_users() self.user = self.f['local_user1'] def test_login(self): reply = self.client.post('/auth/token/', {'username': self.user.username + '@' + self.user.domain, 'password': 'testpassword2'}) self.assertEqual(reply.status_code, 200) self.assertTrue('token' in reply.json()) self.assertTrue(len(reply.json()['token']) == 40) self.assertTrue('key' in reply.json()) self.assertEqual(len(reply.json()['key']), 64) def test_login_fail(self): reply = self.client.post('/auth/token/', {'username': self.user.username + '@' + self.user.domain, 'password': 'testpassword3'}) self.assertEqual(reply.status_code, 400) self.assertTrue('token' not in reply.json()) self.assertTrue('error' in reply.json()) def test_login_fail2(self): reply = self.client.post('/auth/token/', {'username': self.user.username, 'password': 'testpassword2'}) self.assertEqual(reply.status_code, 400) self.assertTrue('token' not in reply.json()) self.assertTrue('error' in reply.json()) class RegistrationApiTestCase(UserTestMixin, ToolshedTestCase): client = Client(SERVER_NAME='testserver') def setUp(self): super().setUp() self.prepare_users() def test_registration(self): self.assertEqual(ToolshedUser.objects.all().count(), 3) reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 200) self.assertTrue('username' in reply.json()) self.assertTrue('domain' in reply.json()) user = ToolshedUser.objects.get(username='testuser') self.assertEqual(user.email, 'test@abc.de') self.assertEqual(user.domain, 'example.com') self.assertTrue(user.check_password('testpassword2')) self.assertEqual(ToolshedUser.objects.all().count(), 4) def test_registration_fail(self): reply = self.client.post('/auth/register/', {'username': '', 'password': 'testpassword2', 'domain': 'example.com', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('username' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail2(self): reply = self.client.post('/auth/register/', {'password': 'testpassword2', 'domain': 'example.com', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('username' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail3(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': '', 'domain': 'example.com', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('password' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail4(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'domain': 'example.com', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('password' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail5(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': 'testpassword2', 'domain': '', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('domain' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail6(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': 'testpassword2', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('domain' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail7(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com', 'email': ''}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('email' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail8(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('email' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_existing_user(self): reply = self.client.post('/auth/register/', {'username': 'testuser2', 'password': 'testpassword2', 'domain': 'example.com', 'email': 'test3@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) # TODO: check for sensible error message self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_foreign_domain(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.org', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('domain' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_reuse_email(self): reply = self.client.post('/auth/register/', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com', 'email': 'test2@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) self.assertTrue('email' in reply.json()['errors']) self.assertEqual(ToolshedUser.objects.all().count(), 3)