diff --git a/authentik/admin/urls.py b/authentik/admin/urls.py index 6d4ad1b51..8df23a894 100644 --- a/authentik/admin/urls.py +++ b/authentik/admin/urls.py @@ -10,7 +10,6 @@ from authentik.admin.views import ( sources, stages, stages_bindings, - stages_invitations, stages_prompts, ) from authentik.providers.saml.views.metadata import MetadataImportView @@ -86,12 +85,6 @@ urlpatterns = [ stages_prompts.PromptUpdateView.as_view(), name="stage-prompt-update", ), - # Stage Invitations - path( - "stages/invitations/create/", - stages_invitations.InvitationCreateView.as_view(), - name="stage-invitation-create", - ), # Property Mappings path( "property-mappings/create/", diff --git a/authentik/admin/views/stages_invitations.py b/authentik/admin/views/stages_invitations.py deleted file mode 100644 index ca2552c58..000000000 --- a/authentik/admin/views/stages_invitations.py +++ /dev/null @@ -1,36 +0,0 @@ -"""authentik Invitation administration""" -from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.auth.mixins import ( - PermissionRequiredMixin as DjangoPermissionRequiredMixin, -) -from django.contrib.messages.views import SuccessMessageMixin -from django.http import HttpResponseRedirect -from django.urls import reverse_lazy -from django.utils.translation import gettext as _ - -from authentik.lib.views import CreateAssignPermView -from authentik.stages.invitation.forms import InvitationForm -from authentik.stages.invitation.models import Invitation - - -class InvitationCreateView( - SuccessMessageMixin, - LoginRequiredMixin, - DjangoPermissionRequiredMixin, - CreateAssignPermView, -): - """Create new Invitation""" - - model = Invitation - form_class = InvitationForm - permission_required = "authentik_stages_invitation.add_invitation" - - template_name = "generic/create.html" - success_url = reverse_lazy("authentik_core:if-admin") - success_message = _("Successfully created Invitation") - - def form_valid(self, form): - obj = form.save(commit=False) - obj.created_by = self.request.user - obj.save() - return HttpResponseRedirect(self.success_url) diff --git a/authentik/stages/invitation/api.py b/authentik/stages/invitation/api.py index 8bc103c56..1783bd76e 100644 --- a/authentik/stages/invitation/api.py +++ b/authentik/stages/invitation/api.py @@ -49,5 +49,4 @@ class InvitationViewSet(ModelViewSet): filterset_fields = ["created_by__username", "expires"] def perform_create(self, serializer: InvitationSerializer): - serializer.instance.created_by = self.request.user - return super().perform_create(serializer) + serializer.save(created_by=self.request.user) diff --git a/authentik/stages/invitation/forms.py b/authentik/stages/invitation/forms.py index 30fd2622f..a34bbe740 100644 --- a/authentik/stages/invitation/forms.py +++ b/authentik/stages/invitation/forms.py @@ -1,8 +1,7 @@ """authentik flows invitation forms""" from django import forms -from authentik.admin.fields import CodeMirrorWidget, YAMLField -from authentik.stages.invitation.models import Invitation, InvitationStage +from authentik.stages.invitation.models import InvitationStage class InvitationStageForm(forms.ModelForm): @@ -15,14 +14,3 @@ class InvitationStageForm(forms.ModelForm): widgets = { "name": forms.TextInput(), } - - -class InvitationForm(forms.ModelForm): - """InvitationForm""" - - class Meta: - - model = Invitation - fields = ["expires", "fixed_data"] - widgets = {"fixed_data": CodeMirrorWidget()} - field_classes = {"fixed_data": YAMLField} diff --git a/web/src/api/legacy.ts b/web/src/api/legacy.ts index 2c1855575..204c4d9d7 100644 --- a/web/src/api/legacy.ts +++ b/web/src/api/legacy.ts @@ -28,10 +28,6 @@ export class AdminURLManager { return `/administration/stages_prompts/${rest}`; } - static stageInvitations(rest: string): string { - return `/administration/stages/invitations/${rest}`; - } - static stageBindings(rest: string): string { return `/administration/stages/bindings/${rest}`; } @@ -52,10 +48,6 @@ export class UserURLManager { return `/-/user/tokens/${rest}`; } - static authenticatorWebauthn(rest: string): string { - return `/-/user/authenticator/webauthn/${rest}`; - } - } export class AppURLManager { diff --git a/web/src/elements/forms/Form.ts b/web/src/elements/forms/Form.ts index 9940b0e1f..4596053fd 100644 --- a/web/src/elements/forms/Form.ts +++ b/web/src/elements/forms/Form.ts @@ -84,6 +84,8 @@ export class Form extends LitElement { const values = form._serializeElementValues(element); if (element.tagName.toLowerCase() === "select" && "multiple" in element.attributes) { json[element.name] = values; + } else if (element.tagName.toLowerCase() === "input" && element.type === "date") { + json[element.name] = element.valueAsDate; } else { for (let v = 0; v < values.length; v++) { form._addSerializedElement(json, element.name, values[v]); diff --git a/web/src/pages/stages/InvitationForm.ts b/web/src/pages/stages/InvitationForm.ts new file mode 100644 index 000000000..5ed5bf81a --- /dev/null +++ b/web/src/pages/stages/InvitationForm.ts @@ -0,0 +1,57 @@ +import { Invitation, StagesApi } from "authentik-api"; +import { gettext } from "django"; +import { customElement, property } from "lit-element"; +import { html, TemplateResult } from "lit-html"; +import { DEFAULT_CONFIG } from "../../api/Config"; +import { Form } from "../../elements/forms/Form"; +import { ifDefined } from "lit-html/directives/if-defined"; +import "../../elements/forms/HorizontalFormElement"; +import "../../elements/CodeMirror"; +import YAML from "yaml"; + +@customElement("ak-stage-invitation-form") +export class InvitationForm extends Form { + + @property({attribute: false}) + invitation?: Invitation; + + getSuccessMessage(): string { + if (this.invitation) { + return gettext("Successfully updated invitation."); + } else { + return gettext("Successfully created invitation."); + } + } + + send = (data: Invitation): Promise => { + if (this.invitation) { + return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsUpdate({ + inviteUuid: this.invitation.pk || "", + data: data + }); + } else { + return new StagesApi(DEFAULT_CONFIG).stagesInvitationInvitationsCreate({ + data: data + }); + } + }; + + renderForm(): TemplateResult { + return html`
+ + + + + + +

${gettext("Optional data which is loaded into the flow's 'prompt_data' context variable.")}

+
+
`; + } + +} diff --git a/web/src/pages/stages/InvitationListPage.ts b/web/src/pages/stages/InvitationListPage.ts index f762c2ae5..98bba779b 100644 --- a/web/src/pages/stages/InvitationListPage.ts +++ b/web/src/pages/stages/InvitationListPage.ts @@ -6,11 +6,12 @@ import { TablePage } from "../../elements/table/TablePage"; import "../../elements/buttons/ModalButton"; import "../../elements/buttons/SpinnerButton"; import "../../elements/forms/DeleteForm"; +import "../../elements/forms/ModalForm"; +import "./InvitationForm"; import { TableColumn } from "../../elements/table/Table"; import { PAGE_SIZE } from "../../constants"; import { Invitation, StagesApi } from "authentik-api"; import { DEFAULT_CONFIG } from "../../api/Config"; -import { AdminURLManager } from "../../api/legacy"; @customElement("ak-stage-invitation-list") export class InvitationListPage extends TablePage { @@ -71,12 +72,19 @@ export class InvitationListPage extends TablePage { renderToolbar(): TemplateResult { return html` - - + + ${gettext("Create")} - -
-
+ + + ${gettext("Create Invitation")} + + + + + ${super.renderToolbar()} `; }