web/admin: migrate invitations to web

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-03-30 22:11:30 +02:00
parent 717282b4b7
commit 8a3b1ae29d
8 changed files with 75 additions and 72 deletions

View File

@ -10,7 +10,6 @@ from authentik.admin.views import (
sources, sources,
stages, stages,
stages_bindings, stages_bindings,
stages_invitations,
stages_prompts, stages_prompts,
) )
from authentik.providers.saml.views.metadata import MetadataImportView from authentik.providers.saml.views.metadata import MetadataImportView
@ -86,12 +85,6 @@ urlpatterns = [
stages_prompts.PromptUpdateView.as_view(), stages_prompts.PromptUpdateView.as_view(),
name="stage-prompt-update", name="stage-prompt-update",
), ),
# Stage Invitations
path(
"stages/invitations/create/",
stages_invitations.InvitationCreateView.as_view(),
name="stage-invitation-create",
),
# Property Mappings # Property Mappings
path( path(
"property-mappings/create/", "property-mappings/create/",

View File

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

View File

@ -49,5 +49,4 @@ class InvitationViewSet(ModelViewSet):
filterset_fields = ["created_by__username", "expires"] filterset_fields = ["created_by__username", "expires"]
def perform_create(self, serializer: InvitationSerializer): def perform_create(self, serializer: InvitationSerializer):
serializer.instance.created_by = self.request.user serializer.save(created_by=self.request.user)
return super().perform_create(serializer)

View File

@ -1,8 +1,7 @@
"""authentik flows invitation forms""" """authentik flows invitation forms"""
from django import forms from django import forms
from authentik.admin.fields import CodeMirrorWidget, YAMLField from authentik.stages.invitation.models import InvitationStage
from authentik.stages.invitation.models import Invitation, InvitationStage
class InvitationStageForm(forms.ModelForm): class InvitationStageForm(forms.ModelForm):
@ -15,14 +14,3 @@ class InvitationStageForm(forms.ModelForm):
widgets = { widgets = {
"name": forms.TextInput(), "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}

View File

@ -28,10 +28,6 @@ export class AdminURLManager {
return `/administration/stages_prompts/${rest}`; return `/administration/stages_prompts/${rest}`;
} }
static stageInvitations(rest: string): string {
return `/administration/stages/invitations/${rest}`;
}
static stageBindings(rest: string): string { static stageBindings(rest: string): string {
return `/administration/stages/bindings/${rest}`; return `/administration/stages/bindings/${rest}`;
} }
@ -52,10 +48,6 @@ export class UserURLManager {
return `/-/user/tokens/${rest}`; return `/-/user/tokens/${rest}`;
} }
static authenticatorWebauthn(rest: string): string {
return `/-/user/authenticator/webauthn/${rest}`;
}
} }
export class AppURLManager { export class AppURLManager {

View File

@ -84,6 +84,8 @@ export class Form<T> extends LitElement {
const values = form._serializeElementValues(element); const values = form._serializeElementValues(element);
if (element.tagName.toLowerCase() === "select" && "multiple" in element.attributes) { if (element.tagName.toLowerCase() === "select" && "multiple" in element.attributes) {
json[element.name] = values; json[element.name] = values;
} else if (element.tagName.toLowerCase() === "input" && element.type === "date") {
json[element.name] = element.valueAsDate;
} else { } else {
for (let v = 0; v < values.length; v++) { for (let v = 0; v < values.length; v++) {
form._addSerializedElement(json, element.name, values[v]); form._addSerializedElement(json, element.name, values[v]);

View File

@ -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<Invitation> {
@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<Invitation> => {
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`<form class="pf-c-form pf-m-horizontal">
<ak-form-element-horizontal
label=${gettext("Expires")}
?required=${true}
name="expires">
<input type="date" value="${ifDefined(this.invitation?.expires)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${gettext("Attributes")}
name="fixedData">
<ak-codemirror mode="yaml" value="${YAML.stringify(this.invitation?.fixedData)}">
</ak-codemirror>
<p class="pf-c-form__helper-text">${gettext("Optional data which is loaded into the flow's 'prompt_data' context variable.")}</p>
</ak-form-element-horizontal>
</form>`;
}
}

View File

@ -6,11 +6,12 @@ import { TablePage } from "../../elements/table/TablePage";
import "../../elements/buttons/ModalButton"; import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton"; import "../../elements/buttons/SpinnerButton";
import "../../elements/forms/DeleteForm"; import "../../elements/forms/DeleteForm";
import "../../elements/forms/ModalForm";
import "./InvitationForm";
import { TableColumn } from "../../elements/table/Table"; import { TableColumn } from "../../elements/table/Table";
import { PAGE_SIZE } from "../../constants"; import { PAGE_SIZE } from "../../constants";
import { Invitation, StagesApi } from "authentik-api"; import { Invitation, StagesApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { AdminURLManager } from "../../api/legacy";
@customElement("ak-stage-invitation-list") @customElement("ak-stage-invitation-list")
export class InvitationListPage extends TablePage<Invitation> { export class InvitationListPage extends TablePage<Invitation> {
@ -71,12 +72,19 @@ export class InvitationListPage extends TablePage<Invitation> {
renderToolbar(): TemplateResult { renderToolbar(): TemplateResult {
return html` return html`
<ak-modal-button href=${AdminURLManager.stageInvitations("create/")}> <ak-forms-modal>
<ak-spinner-button slot="trigger" class="pf-m-primary"> <span slot="submit">
${gettext("Create")} ${gettext("Create")}
</ak-spinner-button> </span>
<div slot="modal"></div> <span slot="header">
</ak-modal-button> ${gettext("Create Invitation")}
</span>
<ak-stage-invitation-form slot="form">
</ak-stage-invitation-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${gettext("Create")}
</button>
</ak-forms-modal>
${super.renderToolbar()} ${super.renderToolbar()}
`; `;
} }