diff --git a/tenants/templates/tenants/signup.html b/tenants/templates/tenants/signup.html
new file mode 100644
index 0000000..8b0711c
--- /dev/null
+++ b/tenants/templates/tenants/signup.html
@@ -0,0 +1,80 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static suit_tags %}
+{% load url from suit_compat %}
+
+{% block bodyclass %}login{% endblock %}
+
+{% block nav-global %}{% endblock %}
+
+{% block content_title %}{% endblock %}
+
+{% block breadcrumbs %}{% endblock %}
+
+{% block header %}{% endblock %}
+
+{% block content-left %}{% endblock %}
+
+{% block container %}
+
+ {% block content %}
+
+ {% if form.errors and not form.non_field_errors %}
+
+ {% blocktrans count counter=form.errors.items|length %}
+ Please correct the error below.
+ {% plural %}
+ Please correct the errors below.
+ {% endblocktrans %}
+
+ {% endif %}
+
+ {% if form.non_field_errors %}
+ {% for error in form.non_field_errors %}
+
+ {{ error }}
+
+ {% endfor %}
+ {% endif %}
+
+
+ {% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/tenants/urls.py b/tenants/urls.py
new file mode 100644
index 0000000..9aa5438
--- /dev/null
+++ b/tenants/urls.py
@@ -0,0 +1,9 @@
+from django.urls import path
+
+from tenants.views import signup
+
+app_name = 'tenants'
+
+urlpatterns = [
+ path('signup/', signup, name='signup')
+]
diff --git a/tenants/views.py b/tenants/views.py
new file mode 100644
index 0000000..7cfc243
--- /dev/null
+++ b/tenants/views.py
@@ -0,0 +1,62 @@
+from django.contrib.auth import login
+from django.contrib.auth.forms import UserCreationForm
+from django.contrib.auth.models import User, Permission, Group
+from django.db import transaction, IntegrityError
+from django.shortcuts import render, redirect
+from django.urls import reverse
+
+from tenants.models import Tenant
+from winget.models import Package, Version, Installer
+
+
+def signup(request):
+ if request.method == 'POST':
+ form = SignupForm(request.POST)
+ if form.is_valid():
+ try:
+ tenant = form.save()
+ except UserAlreadyExists:
+ form.add_error(
+ 'email', 'A user with this email already exists.'
+ )
+ else:
+ login(request, tenant.user)
+ return redirect(reverse('admin:winget_package_changelist'))
+ else:
+ form = SignupForm()
+ return render(request, 'tenants/signup.html', {'form': form})
+
+
+class SignupForm(UserCreationForm):
+ class Meta:
+ model = User
+ fields = ('email',)
+
+ @transaction.atomic
+ def save(self, commit=True):
+ assert commit, 'commit=False is not implemented.'
+ user = super().save(commit=False)
+ user.username = user.email
+ user.is_staff = True
+ try:
+ user.save()
+ tenant = Tenant.objects.create(user=user)
+ except IntegrityError:
+ raise UserAlreadyExists()
+ user.groups.add(_get_or_create_tenants_group())
+ return tenant
+
+
+def _get_or_create_tenants_group():
+ group, created = Group.objects.get_or_create(name='Tenants')
+ if created:
+ for model in (Package, Version, Installer):
+ for action in ('view', 'add', 'change', 'delete'):
+ codename = f'{action}_{model._meta.model_name}'
+ permission = Permission.objects.get(codename=codename)
+ group.permissions.add(permission)
+ return group
+
+
+class UserAlreadyExists(IntegrityError):
+ pass
diff --git a/wpr/urls.py b/wpr/urls.py
index 622501b..c5e9997 100644
--- a/wpr/urls.py
+++ b/wpr/urls.py
@@ -6,6 +6,7 @@ from django.urls import include, path
urlpatterns = [
path('api/', include('winget.urls')),
path('admin/', admin.site.urls),
+ path('', include('tenants.urls'))
]
if settings.DEBUG: