From 3cd89b7162f1e731d9a2df843653a48018702705 Mon Sep 17 00:00:00 2001 From: jedi Date: Thu, 15 Jun 2023 20:55:33 +0200 Subject: [PATCH] add hostadmin app --- .gitignore | 2 + backend/authentication/api.py | 45 +++++++++++--------- backend/authentication/tests/helpers.py | 4 +- backend/authentication/tests/test_auth.py | 34 ++++++++------- backend/backend/settings.py | 1 + backend/backend/urls.py | 1 + backend/hostadmin/__init__.py | 0 backend/hostadmin/admin.py | 11 +++++ backend/hostadmin/api.py | 27 ++++++++++++ backend/hostadmin/migrations/0001_initial.py | 26 +++++++++++ backend/hostadmin/migrations/__init__.py | 0 backend/hostadmin/models.py | 10 +++++ backend/hostadmin/tests.py | 20 +++++++++ 13 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 backend/hostadmin/__init__.py create mode 100644 backend/hostadmin/admin.py create mode 100644 backend/hostadmin/api.py create mode 100644 backend/hostadmin/migrations/0001_initial.py create mode 100644 backend/hostadmin/migrations/__init__.py create mode 100644 backend/hostadmin/models.py create mode 100644 backend/hostadmin/tests.py diff --git a/.gitignore b/.gitignore index b6e4761..9ed0f77 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,5 @@ dmypy.json # Pyre type checker .pyre/ + +staticfiles/ diff --git a/backend/authentication/api.py b/backend/authentication/api.py index a96f229..abc9019 100644 --- a/backend/authentication/api.py +++ b/backend/authentication/api.py @@ -11,6 +11,7 @@ from rest_framework.response import Response from authentication.models import ToolshedUser from authentication.signature_auth import SignatureAuthenticationLocal +from hostadmin.models import Domain router = routers.SimpleRouter() @@ -68,33 +69,35 @@ def getUserInfo(request): @permission_classes([]) @authentication_classes([]) def registerUser(request): - username = request.data.get('username') - domain = request.data.get('domain') - password = request.data.get('password') - email = request.data.get('email') + try: + username = request.data.get('username') + domain = request.data.get('domain') + password = request.data.get('password') + email = request.data.get('email') - errors = {} - if not username: - errors['username'] = 'Username is required' - if not domain: - errors['domain'] = 'Domain is required' - if not password: - errors['password'] = 'Password is required' - if not email: - errors['email'] = 'Email is required' - if ToolshedUser.objects.filter(email=email).exists(): - errors['email'] = 'Email already exists' - if ToolshedUser.objects.filter(username=username, domain=domain).exists(): - errors['username'] = 'Username already exists' - if errors: - return Response({'errors': errors}, status=400) + errors = {} + if not username: + errors['username'] = 'Username is required' + if not domain: + errors['domain'] = 'Domain is required' + if not password: + errors['password'] = 'Password is required' + if not email: + errors['email'] = 'Email is required' + if ToolshedUser.objects.filter(email=email).exists(): + errors['email'] = 'Email already exists' + if ToolshedUser.objects.filter(username=username, domain=domain).exists(): + errors['username'] = 'Username already exists' + if errors: + return Response({'errors': errors}, status=400) + + Domain.objects.get(name=domain, open_registration=True) - if domain in ['localhost']: user = ToolshedUser.objects.create_user(username, email, '', domain=domain) user.set_password(password) user.save() return Response({'username': user.username, 'domain': user.domain}) - else: + except Domain.DoesNotExist: return Response({'errors': {'domain': 'Domain does not exist or is not open for registration'}}, status=400) diff --git a/backend/authentication/tests/helpers.py b/backend/authentication/tests/helpers.py index 61b1c74..aa2a5d4 100644 --- a/backend/authentication/tests/helpers.py +++ b/backend/authentication/tests/helpers.py @@ -4,6 +4,7 @@ from django.test import TestCase, Client from nacl.encoding import HexEncoder from authentication.models import ToolshedUser, KnownIdentity +from hostadmin.models import Domain from nacl.signing import SigningKey @@ -73,7 +74,8 @@ class UserTestCase(TestCase): admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '') admin.set_password('testpassword') admin.save() - example_com = type('obj', (object,), {'name': 'example.com'}) + example_com = Domain.objects.create(name='example.com', owner=admin, open_registration=True) + example_com.save() self.local_user1 = ToolshedUser.objects.create_user('testuser', 'test@abc.de', '', domain=example_com.name) self.local_user1.set_password('testpassword2') self.local_user1.save() diff --git a/backend/authentication/tests/test_auth.py b/backend/authentication/tests/test_auth.py index bfca93b..1f01c1d 100644 --- a/backend/authentication/tests/test_auth.py +++ b/backend/authentication/tests/test_auth.py @@ -4,6 +4,7 @@ from nacl.signing import SigningKey from authentication.models import ToolshedUser, KnownIdentity from authentication.tests import UserTestCase, SignatureAuthClient +from hostadmin.models import Domain class KnownIdentityTestCase(TestCase): @@ -85,7 +86,7 @@ class UserModelTestCase(UserTestCase): def test_create_existing_user(self): with self.assertRaises(ValueError): - ToolshedUser.objects.create_user('testuser', 'test3@abc.de', '', domain='localhost') + ToolshedUser.objects.create_user('testuser', 'test3@abc.de', '', domain='example.com') def test_create_existing_user2(self): key = SigningKey.generate() @@ -96,17 +97,17 @@ class UserModelTestCase(UserTestCase): def test_create_reuse_email(self): with self.assertRaises(ValueError): - ToolshedUser.objects.create_user('testuser3', 'test@abc.de', '', domain='localhost') + ToolshedUser.objects.create_user('testuser3', 'test@abc.de', '', domain='example.com') def test_create_user_invalid_private_key(self): with self.assertRaises(TypeError): - ToolshedUser.objects.create_user('testuser3', 'test3@abc.de', '', domain='localhost', + 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='localhost', + 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='localhost', + ToolshedUser.objects.create_user('testuser3', 'test3@abc.de', '', domain='example.com', private_key='0123456789abcdef0123456789abcdef' 'Z123456789abcdef0123456789abcdef') @@ -264,7 +265,8 @@ class RegistrationApiTestCase(TestCase): admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '') admin.set_password('testpassword') admin.save() - example_com = type('obj', (object,), {'name': 'localhost'}) + example_com = Domain.objects.create(name='example.com', owner=admin, open_registration=True) + example_com.save() user2 = ToolshedUser.objects.create_user('testuser2', 'test2@abc.de', '', domain=example_com.name) user2.set_password('testpassword3') user2.save() @@ -272,20 +274,20 @@ class RegistrationApiTestCase(TestCase): def test_registration(self): self.assertEqual(ToolshedUser.objects.all().count(), 2) reply = self.client.post('/auth/register/', - {'username': 'testuser', 'password': 'testpassword2', 'domain': 'localhost', + {'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, 'localhost') + self.assertEqual(user.domain, 'example.com') self.assertTrue(user.check_password('testpassword2')) self.assertEqual(ToolshedUser.objects.all().count(), 3) def test_registration_fail(self): reply = self.client.post('/auth/register/', - {'username': '', 'password': 'testpassword2', 'domain': 'localhost', + {'username': '', 'password': 'testpassword2', 'domain': 'example.com', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) @@ -294,7 +296,7 @@ class RegistrationApiTestCase(TestCase): def test_registration_fail2(self): reply = self.client.post('/auth/register/', - {'password': 'testpassword2', 'domain': 'localhost', 'email': 'test@abc.de'}) + {'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']) @@ -302,7 +304,7 @@ class RegistrationApiTestCase(TestCase): def test_registration_fail3(self): reply = self.client.post('/auth/register/', - {'username': 'testuser', 'password': '', 'domain': 'localhost', + {'username': 'testuser', 'password': '', 'domain': 'example.com', 'email': 'test@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) @@ -311,7 +313,7 @@ class RegistrationApiTestCase(TestCase): def test_registration_fail4(self): reply = self.client.post('/auth/register/', - {'username': 'testuser', 'domain': 'localhost', 'email': 'test@abc.de'}) + {'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']) @@ -336,7 +338,7 @@ class RegistrationApiTestCase(TestCase): def test_registration_fail7(self): reply = self.client.post('/auth/register/', - {'username': 'testuser', 'password': 'testpassword2', 'domain': 'localhost', + {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com', 'email': ''}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) @@ -345,7 +347,7 @@ class RegistrationApiTestCase(TestCase): def test_registration_fail8(self): reply = self.client.post('/auth/register/', - {'username': 'testuser', 'password': 'testpassword2', 'domain': 'localhost'}) + {'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']) @@ -353,7 +355,7 @@ class RegistrationApiTestCase(TestCase): def test_registration_existing_user(self): reply = self.client.post('/auth/register/', - {'username': 'testuser2', 'password': 'testpassword2', 'domain': 'localhost', + {'username': 'testuser2', 'password': 'testpassword2', 'domain': 'example.com', 'email': 'test3@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) @@ -371,7 +373,7 @@ class RegistrationApiTestCase(TestCase): def test_registration_reuse_email(self): reply = self.client.post('/auth/register/', - {'username': 'testuser', 'password': 'testpassword2', 'domain': 'localhost', + {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com', 'email': 'test2@abc.de'}) self.assertEqual(reply.status_code, 400) self.assertTrue('errors' in reply.json()) diff --git a/backend/backend/settings.py b/backend/backend/settings.py index fd888c2..a746d7c 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -47,6 +47,7 @@ INSTALLED_APPS = [ 'corsheaders', 'drf_yasg', 'authentication', + 'hostadmin', ] REST_FRAMEWORK = { diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 649a816..9de988a 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -31,5 +31,6 @@ schema_view = get_schema_view( urlpatterns = [ path('djangoadmin/', admin.site.urls), path('auth/', include('authentication.api')), + path('admin/', include('hostadmin.api')), path('docs/', schema_view.with_ui('swagger', cache_timeout=0), name='api-docs'), ] diff --git a/backend/hostadmin/__init__.py b/backend/hostadmin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/hostadmin/admin.py b/backend/hostadmin/admin.py new file mode 100644 index 0000000..84e6155 --- /dev/null +++ b/backend/hostadmin/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from .models import Domain + + +class DomainAdmin(admin.ModelAdmin): + list_display = ('name', 'owner', 'open_registration') + list_filter = ('name', 'owner', 'open_registration') + + +admin.site.register(Domain, DomainAdmin) diff --git a/backend/hostadmin/api.py b/backend/hostadmin/api.py new file mode 100644 index 0000000..6f0319c --- /dev/null +++ b/backend/hostadmin/api.py @@ -0,0 +1,27 @@ +from rest_framework import routers, serializers, viewsets +from rest_framework.authentication import TokenAuthentication +from rest_framework.permissions import IsAuthenticated + +from hostadmin.models import Domain + +router = routers.SimpleRouter() + + +class DomainSerializer(serializers.ModelSerializer): + class Meta: + model = Domain + fields = '__all__' + + +class DomainViewSet(viewsets.ModelViewSet): + queryset = Domain.objects.all() + serializer_class = DomainSerializer + authentication_classes = [TokenAuthentication] + permission_classes = [IsAuthenticated] + + +router.register(r'domains', DomainViewSet, basename='domains') + +urlpatterns = [ + *router.urls, +] diff --git a/backend/hostadmin/migrations/0001_initial.py b/backend/hostadmin/migrations/0001_initial.py new file mode 100644 index 0000000..a6b1ed4 --- /dev/null +++ b/backend/hostadmin/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 4.1.7 on 2023-06-07 20:59 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Domain', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ('open_registration', models.BooleanField(default=False)), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='domains', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/hostadmin/migrations/__init__.py b/backend/hostadmin/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/hostadmin/models.py b/backend/hostadmin/models.py new file mode 100644 index 0000000..1bb2288 --- /dev/null +++ b/backend/hostadmin/models.py @@ -0,0 +1,10 @@ +from django.db import models + + +class Domain(models.Model): + name = models.CharField(max_length=255, unique=True) + owner = models.ForeignKey('authentication.ToolshedUser', on_delete=models.CASCADE, related_name='domains') + open_registration = models.BooleanField(default=False) + + def __str__(self): + return self.name diff --git a/backend/hostadmin/tests.py b/backend/hostadmin/tests.py new file mode 100644 index 0000000..f2f84df --- /dev/null +++ b/backend/hostadmin/tests.py @@ -0,0 +1,20 @@ +from django.test import TestCase + +from authentication.models import ToolshedUser +from hostadmin.models import Domain + + +class DomainTestCase(TestCase): + def setUp(self): + admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '') + admin.set_password('testpassword') + admin.save() + example_com = Domain.objects.create(name='example.com', owner=admin, open_registration=True) + example_com.save() + + def test_domain(self): + example_com = Domain.objects.get(name='example.com') + self.assertEqual(example_com.name, 'example.com') + self.assertEqual(example_com.owner.username, 'admin') + self.assertEqual(example_com.open_registration, True) + self.assertEqual(str(example_com), 'example.com')