stages/invitation: add invitation name

closes #2583

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2022-03-26 18:32:45 +01:00
parent 933919c647
commit c7a83e6182
18 changed files with 220 additions and 16 deletions

View File

@ -54,6 +54,7 @@ class InvitationSerializer(ModelSerializer):
model = Invitation
fields = [
"pk",
"name",
"expires",
"fixed_data",
"created_by",
@ -67,8 +68,8 @@ class InvitationViewSet(UsedByMixin, ModelViewSet):
queryset = Invitation.objects.all()
serializer_class = InvitationSerializer
ordering = ["-expires"]
search_fields = ["created_by__username", "expires"]
filterset_fields = ["created_by__username", "expires"]
search_fields = ["name", "created_by__username", "expires"]
filterset_fields = ["name", "created_by__username", "expires"]
def perform_create(self, serializer: InvitationSerializer):
serializer.save(created_by=self.request.user)

View File

@ -0,0 +1,122 @@
# Generated by Django 4.0.3 on 2022-03-26 17:24
import uuid
import django.db.models.deletion
from django.apps.registry import Apps
from django.conf import settings
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.core.models
def migrate_add_name(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Invitation = apps.get_model("authentik_stages_invitation", "invitation")
for invite in Invitation.objects.using(db_alias).all():
invite.name = invite.pk.hex
invite.save()
class Migration(migrations.Migration):
replaces = [
("authentik_stages_invitation", "0001_initial"),
("authentik_stages_invitation", "0002_auto_20201225_2143"),
("authentik_stages_invitation", "0003_auto_20201227_1210"),
("authentik_stages_invitation", "0004_invitation_single_use"),
("authentik_stages_invitation", "0005_auto_20210901_1211"),
("authentik_stages_invitation", "0006_invitation_name"),
]
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("authentik_flows", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="InvitationStage",
fields=[
(
"stage_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_flows.stage",
),
),
(
"continue_flow_without_invitation",
models.BooleanField(
default=False,
help_text="If this flag is set, this Stage will jump to the next Stage when no Invitation is given. By default this Stage will cancel the Flow when no invitation is given.",
),
),
],
options={
"verbose_name": "Invitation Stage",
"verbose_name_plural": "Invitation Stages",
},
bases=("authentik_flows.stage",),
),
migrations.CreateModel(
name="Invitation",
fields=[
(
"invite_uuid",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
(
"expires",
models.DateTimeField(default=authentik.core.models.default_token_duration),
),
(
"fixed_data",
models.JSONField(
blank=True,
default=dict,
help_text="Optional fixed data to enforce on user enrollment.",
),
),
(
"created_by",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
(
"single_use",
models.BooleanField(
default=False,
help_text="When enabled, the invitation will be deleted after usage.",
),
),
("expiring", models.BooleanField(default=True)),
("name", models.SlugField(default="")),
],
options={
"verbose_name": "Invitation",
"verbose_name_plural": "Invitations",
},
),
migrations.RunPython(
code=migrate_add_name,
),
migrations.AlterField(
model_name="invitation",
name="name",
field=models.SlugField(),
preserve_default=False,
),
]

View File

@ -0,0 +1,38 @@
# Generated by Django 4.0.3 on 2022-03-26 17:22
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_add_name(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Invitation = apps.get_model("authentik_stages_invitation", "invitation")
for invite in Invitation.objects.using(db_alias).all():
invite.name = invite.pk.hex
invite.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_invitation", "0005_auto_20210901_1211"),
]
operations = [
migrations.AddField(
model_name="invitation",
name="name",
field=models.SlugField(default=""),
preserve_default=False,
),
migrations.RunPython(migrate_add_name),
migrations.AlterField(
model_name="invitation",
name="name",
field=models.SlugField(),
preserve_default=False,
),
]

View File

@ -52,6 +52,8 @@ class Invitation(ExpiringModel):
invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.SlugField()
single_use = models.BooleanField(
default=False,
help_text=_("When enabled, the invitation will be deleted after usage."),

View File

@ -16924,6 +16924,10 @@ paths:
schema:
type: string
format: date-time
- in: query
name: name
schema:
type: string
- name: ordering
required: false
in: query
@ -16973,6 +16977,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/InvitationRequest'
required: true
security:
- authentik: []
responses:
@ -17031,6 +17036,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/InvitationRequest'
required: true
security:
- authentik: []
responses:
@ -22105,6 +22111,10 @@ components:
format: uuid
readOnly: true
title: Invite uuid
name:
type: string
maxLength: 50
pattern: ^[-a-zA-Z0-9_]+$
expires:
type: string
format: date-time
@ -22120,11 +22130,17 @@ components:
description: When enabled, the invitation will be deleted after usage.
required:
- created_by
- name
- pk
InvitationRequest:
type: object
description: Invitation Serializer
properties:
name:
type: string
minLength: 1
maxLength: 50
pattern: ^[-a-zA-Z0-9_]+$
expires:
type: string
format: date-time
@ -22134,6 +22150,8 @@ components:
single_use:
type: boolean
description: When enabled, the invitation will be deleted after usage.
required:
- name
InvitationStage:
type: object
description: InvitationStage Serializer
@ -27141,6 +27159,11 @@ components:
type: object
description: Invitation Serializer
properties:
name:
type: string
minLength: 1
maxLength: 50
pattern: ^[-a-zA-Z0-9_]+$
expires:
type: string
format: date-time

View File

@ -2350,7 +2350,6 @@ msgid "How to connect"
msgstr "So verbinden Sie sich"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3048,6 +3047,8 @@ msgstr "Meine Anwendungen"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2383,7 +2383,6 @@ msgid "How to connect"
msgstr "How to connect"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3093,6 +3092,8 @@ msgstr "My applications"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2341,7 +2341,6 @@ msgid "How to connect"
msgstr "Cómo conectarse"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3041,6 +3040,8 @@ msgstr "Mis solicitudes"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2367,7 +2367,6 @@ msgid "How to connect"
msgstr ""
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3072,6 +3071,8 @@ msgstr "Mes applications"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2338,7 +2338,6 @@ msgid "How to connect"
msgstr "Jak się połączyć"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3038,6 +3037,8 @@ msgstr "Moje aplikacje"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2375,7 +2375,6 @@ msgid "How to connect"
msgstr ""
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3083,6 +3082,8 @@ msgstr ""
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2341,7 +2341,6 @@ msgid "How to connect"
msgstr "Nasıl bağlanır"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3042,6 +3041,8 @@ msgstr "Uygulamalarım"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2338,7 +2338,6 @@ msgid "How to connect"
msgstr "如何连接"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3037,6 +3036,8 @@ msgstr "我的应用"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2338,7 +2338,6 @@ msgid "How to connect"
msgstr "如何连接"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3037,6 +3036,8 @@ msgstr "我的应用"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -2338,7 +2338,6 @@ msgid "How to connect"
msgstr "如何连接"
#: src/elements/forms/DeleteBulkForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/users/RelatedUserList.ts
#: src/pages/users/UserListPage.ts
msgid "ID"
@ -3037,6 +3036,8 @@ msgstr "我的应用"
#: src/pages/stages/dummy/DummyStageForm.ts
#: src/pages/stages/email/EmailStageForm.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
#: src/pages/stages/invitation/InvitationForm.ts
#: src/pages/stages/invitation/InvitationListPage.ts
#: src/pages/stages/invitation/InvitationStageForm.ts
#: src/pages/stages/password/PasswordStageForm.ts
#: src/pages/stages/prompt/PromptStageForm.ts

View File

@ -44,6 +44,15 @@ export class InvitationForm extends ModelForm<Invitation, string> {
renderForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal">
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input
type="text"
value="${this.instance?.name || ""}"
class="pf-c-form-control"
required
data-ak-slug="true"
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${t`Expires`} ?required=${true} name="expires">
<input
type="datetime-local"

View File

@ -65,7 +65,7 @@ export class InvitationListPage extends TablePage<Invitation> {
columns(): TableColumn[] {
return [
new TableColumn(t`ID`, "pk"),
new TableColumn(t`Name`, "name"),
new TableColumn(t`Created by`, "created_by"),
new TableColumn(t`Expiry`),
new TableColumn(t`Actions`),
@ -96,7 +96,7 @@ export class InvitationListPage extends TablePage<Invitation> {
row(item: Invitation): TemplateResult[] {
return [
html`${item.pk}`,
html`${item.name}`,
html`${item.createdBy?.username}`,
html`${item.expires?.toLocaleString() || t`-`}`,
html` <ak-forms-modal>

View File

@ -2,7 +2,6 @@ import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { InvitationStage, StagesApi } from "@goauthentik/api";
@ -49,7 +48,7 @@ export class InvitationStageForm extends ModelForm<InvitationStage, string> {
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input
type="text"
value="${ifDefined(this.instance?.name || "")}"
value="${this.instance?.name || ""}"
class="pf-c-form-control"
required
/>