add hostadmin app

This commit is contained in:
j3d1 2023-06-15 20:55:33 +02:00
parent 12191369b7
commit 3cd89b7162
13 changed files with 143 additions and 38 deletions

2
.gitignore vendored
View file

@ -127,3 +127,5 @@ dmypy.json
# Pyre type checker # Pyre type checker
.pyre/ .pyre/
staticfiles/

View file

@ -11,6 +11,7 @@ from rest_framework.response import Response
from authentication.models import ToolshedUser from authentication.models import ToolshedUser
from authentication.signature_auth import SignatureAuthenticationLocal from authentication.signature_auth import SignatureAuthenticationLocal
from hostadmin.models import Domain
router = routers.SimpleRouter() router = routers.SimpleRouter()
@ -68,33 +69,35 @@ def getUserInfo(request):
@permission_classes([]) @permission_classes([])
@authentication_classes([]) @authentication_classes([])
def registerUser(request): def registerUser(request):
username = request.data.get('username') try:
domain = request.data.get('domain') username = request.data.get('username')
password = request.data.get('password') domain = request.data.get('domain')
email = request.data.get('email') password = request.data.get('password')
email = request.data.get('email')
errors = {} errors = {}
if not username: if not username:
errors['username'] = 'Username is required' errors['username'] = 'Username is required'
if not domain: if not domain:
errors['domain'] = 'Domain is required' errors['domain'] = 'Domain is required'
if not password: if not password:
errors['password'] = 'Password is required' errors['password'] = 'Password is required'
if not email: if not email:
errors['email'] = 'Email is required' errors['email'] = 'Email is required'
if ToolshedUser.objects.filter(email=email).exists(): if ToolshedUser.objects.filter(email=email).exists():
errors['email'] = 'Email already exists' errors['email'] = 'Email already exists'
if ToolshedUser.objects.filter(username=username, domain=domain).exists(): if ToolshedUser.objects.filter(username=username, domain=domain).exists():
errors['username'] = 'Username already exists' errors['username'] = 'Username already exists'
if errors: if errors:
return Response({'errors': errors}, status=400) 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 = ToolshedUser.objects.create_user(username, email, '', domain=domain)
user.set_password(password) user.set_password(password)
user.save() user.save()
return Response({'username': user.username, 'domain': user.domain}) 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) return Response({'errors': {'domain': 'Domain does not exist or is not open for registration'}}, status=400)

View file

@ -4,6 +4,7 @@ from django.test import TestCase, Client
from nacl.encoding import HexEncoder from nacl.encoding import HexEncoder
from authentication.models import ToolshedUser, KnownIdentity from authentication.models import ToolshedUser, KnownIdentity
from hostadmin.models import Domain
from nacl.signing import SigningKey from nacl.signing import SigningKey
@ -73,7 +74,8 @@ class UserTestCase(TestCase):
admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '') admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '')
admin.set_password('testpassword') admin.set_password('testpassword')
admin.save() 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 = ToolshedUser.objects.create_user('testuser', 'test@abc.de', '', domain=example_com.name)
self.local_user1.set_password('testpassword2') self.local_user1.set_password('testpassword2')
self.local_user1.save() self.local_user1.save()

View file

@ -4,6 +4,7 @@ from nacl.signing import SigningKey
from authentication.models import ToolshedUser, KnownIdentity from authentication.models import ToolshedUser, KnownIdentity
from authentication.tests import UserTestCase, SignatureAuthClient from authentication.tests import UserTestCase, SignatureAuthClient
from hostadmin.models import Domain
class KnownIdentityTestCase(TestCase): class KnownIdentityTestCase(TestCase):
@ -85,7 +86,7 @@ class UserModelTestCase(UserTestCase):
def test_create_existing_user(self): def test_create_existing_user(self):
with self.assertRaises(ValueError): 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): def test_create_existing_user2(self):
key = SigningKey.generate() key = SigningKey.generate()
@ -96,17 +97,17 @@ class UserModelTestCase(UserTestCase):
def test_create_reuse_email(self): def test_create_reuse_email(self):
with self.assertRaises(ValueError): 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): def test_create_user_invalid_private_key(self):
with self.assertRaises(TypeError): 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') private_key=b'0123456789abcdef0123456789abcdef')
with self.assertRaises(ValueError): 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') private_key='7005c4097')
with self.assertRaises(ValueError): 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' private_key='0123456789abcdef0123456789abcdef'
'Z123456789abcdef0123456789abcdef') 'Z123456789abcdef0123456789abcdef')
@ -264,7 +265,8 @@ class RegistrationApiTestCase(TestCase):
admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '') admin = ToolshedUser.objects.create_superuser('admin', 'admin@localhost', '')
admin.set_password('testpassword') admin.set_password('testpassword')
admin.save() 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 = ToolshedUser.objects.create_user('testuser2', 'test2@abc.de', '', domain=example_com.name)
user2.set_password('testpassword3') user2.set_password('testpassword3')
user2.save() user2.save()
@ -272,20 +274,20 @@ class RegistrationApiTestCase(TestCase):
def test_registration(self): def test_registration(self):
self.assertEqual(ToolshedUser.objects.all().count(), 2) self.assertEqual(ToolshedUser.objects.all().count(), 2)
reply = self.client.post('/auth/register/', reply = self.client.post('/auth/register/',
{'username': 'testuser', 'password': 'testpassword2', 'domain': 'localhost', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com',
'email': 'test@abc.de'}) 'email': 'test@abc.de'})
self.assertEqual(reply.status_code, 200) self.assertEqual(reply.status_code, 200)
self.assertTrue('username' in reply.json()) self.assertTrue('username' in reply.json())
self.assertTrue('domain' in reply.json()) self.assertTrue('domain' in reply.json())
user = ToolshedUser.objects.get(username='testuser') user = ToolshedUser.objects.get(username='testuser')
self.assertEqual(user.email, 'test@abc.de') 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.assertTrue(user.check_password('testpassword2'))
self.assertEqual(ToolshedUser.objects.all().count(), 3) self.assertEqual(ToolshedUser.objects.all().count(), 3)
def test_registration_fail(self): def test_registration_fail(self):
reply = self.client.post('/auth/register/', reply = self.client.post('/auth/register/',
{'username': '', 'password': 'testpassword2', 'domain': 'localhost', {'username': '', 'password': 'testpassword2', 'domain': 'example.com',
'email': 'test@abc.de'}) 'email': 'test@abc.de'})
self.assertEqual(reply.status_code, 400) self.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())
@ -294,7 +296,7 @@ class RegistrationApiTestCase(TestCase):
def test_registration_fail2(self): def test_registration_fail2(self):
reply = self.client.post('/auth/register/', 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.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())
self.assertTrue('username' in reply.json()['errors']) self.assertTrue('username' in reply.json()['errors'])
@ -302,7 +304,7 @@ class RegistrationApiTestCase(TestCase):
def test_registration_fail3(self): def test_registration_fail3(self):
reply = self.client.post('/auth/register/', reply = self.client.post('/auth/register/',
{'username': 'testuser', 'password': '', 'domain': 'localhost', {'username': 'testuser', 'password': '', 'domain': 'example.com',
'email': 'test@abc.de'}) 'email': 'test@abc.de'})
self.assertEqual(reply.status_code, 400) self.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())
@ -311,7 +313,7 @@ class RegistrationApiTestCase(TestCase):
def test_registration_fail4(self): def test_registration_fail4(self):
reply = self.client.post('/auth/register/', 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.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())
self.assertTrue('password' in reply.json()['errors']) self.assertTrue('password' in reply.json()['errors'])
@ -336,7 +338,7 @@ class RegistrationApiTestCase(TestCase):
def test_registration_fail7(self): def test_registration_fail7(self):
reply = self.client.post('/auth/register/', reply = self.client.post('/auth/register/',
{'username': 'testuser', 'password': 'testpassword2', 'domain': 'localhost', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com',
'email': ''}) 'email': ''})
self.assertEqual(reply.status_code, 400) self.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())
@ -345,7 +347,7 @@ class RegistrationApiTestCase(TestCase):
def test_registration_fail8(self): def test_registration_fail8(self):
reply = self.client.post('/auth/register/', 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.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())
self.assertTrue('email' in reply.json()['errors']) self.assertTrue('email' in reply.json()['errors'])
@ -353,7 +355,7 @@ class RegistrationApiTestCase(TestCase):
def test_registration_existing_user(self): def test_registration_existing_user(self):
reply = self.client.post('/auth/register/', reply = self.client.post('/auth/register/',
{'username': 'testuser2', 'password': 'testpassword2', 'domain': 'localhost', {'username': 'testuser2', 'password': 'testpassword2', 'domain': 'example.com',
'email': 'test3@abc.de'}) 'email': 'test3@abc.de'})
self.assertEqual(reply.status_code, 400) self.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())
@ -371,7 +373,7 @@ class RegistrationApiTestCase(TestCase):
def test_registration_reuse_email(self): def test_registration_reuse_email(self):
reply = self.client.post('/auth/register/', reply = self.client.post('/auth/register/',
{'username': 'testuser', 'password': 'testpassword2', 'domain': 'localhost', {'username': 'testuser', 'password': 'testpassword2', 'domain': 'example.com',
'email': 'test2@abc.de'}) 'email': 'test2@abc.de'})
self.assertEqual(reply.status_code, 400) self.assertEqual(reply.status_code, 400)
self.assertTrue('errors' in reply.json()) self.assertTrue('errors' in reply.json())

View file

@ -47,6 +47,7 @@ INSTALLED_APPS = [
'corsheaders', 'corsheaders',
'drf_yasg', 'drf_yasg',
'authentication', 'authentication',
'hostadmin',
] ]
REST_FRAMEWORK = { REST_FRAMEWORK = {

View file

@ -31,5 +31,6 @@ schema_view = get_schema_view(
urlpatterns = [ urlpatterns = [
path('djangoadmin/', admin.site.urls), path('djangoadmin/', admin.site.urls),
path('auth/', include('authentication.api')), path('auth/', include('authentication.api')),
path('admin/', include('hostadmin.api')),
path('docs/', schema_view.with_ui('swagger', cache_timeout=0), name='api-docs'), path('docs/', schema_view.with_ui('swagger', cache_timeout=0), name='api-docs'),
] ]

View file

View file

@ -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)

27
backend/hostadmin/api.py Normal file
View file

@ -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,
]

View file

@ -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)),
],
),
]

View file

View file

@ -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

View file

@ -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')