From 568c091438e4267d1f5083ba95a3fdadc954fd89 Mon Sep 17 00:00:00 2001 From: Birger Schacht Date: Wed, 27 Nov 2024 13:25:43 +0100 Subject: [PATCH] feat(invite): add an `invite` app, that provides invite tokens The `invite` app provides the logic to create tokens for user signup. A token can be created in the Django Admin interface and the used in the `/invite/` route to create a user account. Once used, the token is deleted. Closes: #1293 --- apis_core/invite/admin.py | 5 ++++ apis_core/invite/migrations/0001_initial.py | 27 +++++++++++++++++++++ apis_core/invite/migrations/__init__.py | 0 apis_core/invite/models.py | 10 ++++++++ apis_core/invite/templates/invite.html | 15 ++++++++++++ apis_core/invite/urls.py | 7 ++++++ apis_core/invite/views.py | 25 +++++++++++++++++++ apis_core/urls.py | 4 +++ 8 files changed, 93 insertions(+) create mode 100644 apis_core/invite/admin.py create mode 100644 apis_core/invite/migrations/0001_initial.py create mode 100644 apis_core/invite/migrations/__init__.py create mode 100644 apis_core/invite/models.py create mode 100644 apis_core/invite/templates/invite.html create mode 100644 apis_core/invite/urls.py create mode 100644 apis_core/invite/views.py diff --git a/apis_core/invite/admin.py b/apis_core/invite/admin.py new file mode 100644 index 000000000..15729771e --- /dev/null +++ b/apis_core/invite/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +from .models import InviteToken + +admin.site.register(InviteToken) diff --git a/apis_core/invite/migrations/0001_initial.py b/apis_core/invite/migrations/0001_initial.py new file mode 100644 index 000000000..b90065021 --- /dev/null +++ b/apis_core/invite/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.2 on 2024-11-27 12:21 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="InviteToken", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ], + ), + ] diff --git a/apis_core/invite/migrations/__init__.py b/apis_core/invite/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apis_core/invite/models.py b/apis_core/invite/models.py new file mode 100644 index 000000000..20b18bc93 --- /dev/null +++ b/apis_core/invite/models.py @@ -0,0 +1,10 @@ +import uuid + +from django.db import models + + +class InviteToken(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + + def __str__(self): + return str(self.id) diff --git a/apis_core/invite/templates/invite.html b/apis_core/invite/templates/invite.html new file mode 100644 index 000000000..b547e4272 --- /dev/null +++ b/apis_core/invite/templates/invite.html @@ -0,0 +1,15 @@ +{% extends basetemplate|default:"base.html" %} +{% load crispy_forms_tags %} + +{% block content %} +
+
+
+ {% csrf_token %} + {{ form|crispy }} + + +
+
+
+{% endblock content %} diff --git a/apis_core/invite/urls.py b/apis_core/invite/urls.py new file mode 100644 index 000000000..85c3f6a17 --- /dev/null +++ b/apis_core/invite/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from apis_core.invite.views import Invite + +urlpatterns = [ + path("invite/", Invite.as_view()), +] diff --git a/apis_core/invite/views.py b/apis_core/invite/views.py new file mode 100644 index 000000000..184c267a2 --- /dev/null +++ b/apis_core/invite/views.py @@ -0,0 +1,25 @@ +from django.contrib import messages +from django.contrib.auth.forms import UserCreationForm +from django.shortcuts import get_object_or_404 +from django.urls import reverse +from django.views.generic.edit import CreateView + +from apis_core.invite.models import InviteToken + + +class Invite(CreateView): + form_class = UserCreationForm + template_name = "invite.html" + + def setup(self, *args, **kwargs): + super().setup(*args, **kwargs) + self.invite = get_object_or_404(InviteToken, id=kwargs.get("invite")) + + def form_valid(self, form): + ret = super().form_valid(form) + self.invite.delete() + messages.success(self.request, f"Created user {self.object}") + return ret + + def get_success_url(self): + return reverse("apis_core:login") diff --git a/apis_core/urls.py b/apis_core/urls.py index cce850760..bc6fc2281 100644 --- a/apis_core/urls.py +++ b/apis_core/urls.py @@ -54,6 +54,10 @@ urlpatterns.append(path("", include("apis_core.documentation.urls"))) +if "apis_core.invite" in settings.INSTALLED_APPS: + urlpatterns.append(path("", include("apis_core.invite.urls"))) + + urlpatterns.append(path("api/", include(router.urls))) urlpatterns.append(path("api-auth/", include("rest_framework.urls")))