web/admin: migrate application form to web
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		@ -2,7 +2,6 @@
 | 
				
			|||||||
from django.urls import path
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.admin.views import (
 | 
					from authentik.admin.views import (
 | 
				
			||||||
    applications,
 | 
					 | 
				
			||||||
    flows,
 | 
					    flows,
 | 
				
			||||||
    outposts,
 | 
					    outposts,
 | 
				
			||||||
    outposts_service_connections,
 | 
					    outposts_service_connections,
 | 
				
			||||||
@ -19,17 +18,6 @@ from authentik.admin.views import (
 | 
				
			|||||||
from authentik.providers.saml.views.metadata import MetadataImportView
 | 
					from authentik.providers.saml.views.metadata import MetadataImportView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    # Applications
 | 
					 | 
				
			||||||
    path(
 | 
					 | 
				
			||||||
        "applications/create/",
 | 
					 | 
				
			||||||
        applications.ApplicationCreateView.as_view(),
 | 
					 | 
				
			||||||
        name="application-create",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    path(
 | 
					 | 
				
			||||||
        "applications/<uuid:pk>/update/",
 | 
					 | 
				
			||||||
        applications.ApplicationUpdateView.as_view(),
 | 
					 | 
				
			||||||
        name="application-update",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    # Sources
 | 
					    # Sources
 | 
				
			||||||
    path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
 | 
					    path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
 | 
				
			|||||||
@ -1,66 +0,0 @@
 | 
				
			|||||||
"""authentik Application administration"""
 | 
					 | 
				
			||||||
from typing import Any
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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.utils.translation import gettext as _
 | 
					 | 
				
			||||||
from django.views.generic import UpdateView
 | 
					 | 
				
			||||||
from guardian.mixins import PermissionRequiredMixin
 | 
					 | 
				
			||||||
from guardian.shortcuts import get_objects_for_user
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from authentik.core.forms.applications import ApplicationForm
 | 
					 | 
				
			||||||
from authentik.core.models import Application
 | 
					 | 
				
			||||||
from authentik.lib.views import CreateAssignPermView
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ApplicationCreateView(
 | 
					 | 
				
			||||||
    SuccessMessageMixin,
 | 
					 | 
				
			||||||
    LoginRequiredMixin,
 | 
					 | 
				
			||||||
    DjangoPermissionRequiredMixin,
 | 
					 | 
				
			||||||
    CreateAssignPermView,
 | 
					 | 
				
			||||||
):
 | 
					 | 
				
			||||||
    """Create new Application"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = Application
 | 
					 | 
				
			||||||
    form_class = ApplicationForm
 | 
					 | 
				
			||||||
    permission_required = "authentik_core.add_application"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    success_url = "/"
 | 
					 | 
				
			||||||
    template_name = "generic/create.html"
 | 
					 | 
				
			||||||
    success_message = _("Successfully created Application")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_initial(self) -> dict[str, Any]:
 | 
					 | 
				
			||||||
        if "provider" in self.request.GET:
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                initial_provider_pk = int(self.request.GET["provider"])
 | 
					 | 
				
			||||||
            except ValueError:
 | 
					 | 
				
			||||||
                return super().get_initial()
 | 
					 | 
				
			||||||
            providers = (
 | 
					 | 
				
			||||||
                get_objects_for_user(self.request.user, "authentik_core.view_provider")
 | 
					 | 
				
			||||||
                .filter(pk=initial_provider_pk)
 | 
					 | 
				
			||||||
                .select_subclasses()
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            if not providers.exists():
 | 
					 | 
				
			||||||
                return {}
 | 
					 | 
				
			||||||
            return {"provider": providers.first()}
 | 
					 | 
				
			||||||
        return super().get_initial()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ApplicationUpdateView(
 | 
					 | 
				
			||||||
    SuccessMessageMixin,
 | 
					 | 
				
			||||||
    LoginRequiredMixin,
 | 
					 | 
				
			||||||
    PermissionRequiredMixin,
 | 
					 | 
				
			||||||
    UpdateView,
 | 
					 | 
				
			||||||
):
 | 
					 | 
				
			||||||
    """Update application"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = Application
 | 
					 | 
				
			||||||
    form_class = ApplicationForm
 | 
					 | 
				
			||||||
    permission_required = "authentik_core.change_application"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    success_url = "/"
 | 
					 | 
				
			||||||
    template_name = "generic/update.html"
 | 
					 | 
				
			||||||
    success_message = _("Successfully updated Application")
 | 
					 | 
				
			||||||
@ -121,6 +121,7 @@ class ApplicationViewSet(ModelViewSet):
 | 
				
			|||||||
                required=True,
 | 
					                required=True,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					        responses={200: "Success"},
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    @action(detail=True, methods=["POST"], parser_classes=(MultiPartParser,))
 | 
					    @action(detail=True, methods=["POST"], parser_classes=(MultiPartParser,))
 | 
				
			||||||
    # pylint: disable=unused-argument
 | 
					    # pylint: disable=unused-argument
 | 
				
			||||||
@ -132,12 +133,7 @@ class ApplicationViewSet(ModelViewSet):
 | 
				
			|||||||
            return HttpResponseBadRequest()
 | 
					            return HttpResponseBadRequest()
 | 
				
			||||||
        app.meta_icon = icon
 | 
					        app.meta_icon = icon
 | 
				
			||||||
        app.save()
 | 
					        app.save()
 | 
				
			||||||
        return Response(
 | 
					        return Response({})
 | 
				
			||||||
            get_events_per_1h(
 | 
					 | 
				
			||||||
                action=EventAction.AUTHORIZE_APPLICATION,
 | 
					 | 
				
			||||||
                context__authorized_application__pk=app.pk.hex,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @permission_required(
 | 
					    @permission_required(
 | 
				
			||||||
        "authentik_core.view_application", ["authentik_events.view_event"]
 | 
					        "authentik_core.view_application", ["authentik_events.view_event"]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
"""authentik Core Application forms"""
 | 
					 | 
				
			||||||
from django import forms
 | 
					 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from authentik.core.models import Application, Provider
 | 
					 | 
				
			||||||
from authentik.lib.widgets import GroupedModelChoiceField
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ApplicationForm(forms.ModelForm):
 | 
					 | 
				
			||||||
    """Application Form"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, *args, **kwargs):  # pragma: no cover
 | 
					 | 
				
			||||||
        super().__init__(*args, **kwargs)
 | 
					 | 
				
			||||||
        self.fields["provider"].queryset = (
 | 
					 | 
				
			||||||
            Provider.objects.all().order_by("name").select_subclasses()
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Meta:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        model = Application
 | 
					 | 
				
			||||||
        fields = [
 | 
					 | 
				
			||||||
            "name",
 | 
					 | 
				
			||||||
            "slug",
 | 
					 | 
				
			||||||
            "provider",
 | 
					 | 
				
			||||||
            "meta_launch_url",
 | 
					 | 
				
			||||||
            "meta_icon",
 | 
					 | 
				
			||||||
            "meta_description",
 | 
					 | 
				
			||||||
            "meta_publisher",
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
        widgets = {
 | 
					 | 
				
			||||||
            "name": forms.TextInput(),
 | 
					 | 
				
			||||||
            "meta_launch_url": forms.TextInput(),
 | 
					 | 
				
			||||||
            "meta_publisher": forms.TextInput(),
 | 
					 | 
				
			||||||
            "meta_icon": forms.FileInput(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        help_texts = {
 | 
					 | 
				
			||||||
            "meta_launch_url": _(
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    "If left empty, authentik will try to extract the launch URL "
 | 
					 | 
				
			||||||
                    "based on the selected provider."
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        field_classes = {"provider": GroupedModelChoiceField}
 | 
					 | 
				
			||||||
        labels = {
 | 
					 | 
				
			||||||
            "meta_launch_url": _("Launch URL"),
 | 
					 | 
				
			||||||
            "meta_icon": _("Icon"),
 | 
					 | 
				
			||||||
            "meta_description": _("Description"),
 | 
					 | 
				
			||||||
            "meta_publisher": _("Publisher"),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
@ -1340,10 +1340,8 @@ paths:
 | 
				
			|||||||
          required: true
 | 
					          required: true
 | 
				
			||||||
          type: file
 | 
					          type: file
 | 
				
			||||||
      responses:
 | 
					      responses:
 | 
				
			||||||
        '201':
 | 
					        '200':
 | 
				
			||||||
          description: ''
 | 
					          description: Success
 | 
				
			||||||
          schema:
 | 
					 | 
				
			||||||
            $ref: '#/definitions/Application'
 | 
					 | 
				
			||||||
        '403':
 | 
					        '403':
 | 
				
			||||||
          description: Authentication credentials were invalid, absent or insufficient.
 | 
					          description: Authentication credentials were invalid, absent or insufficient.
 | 
				
			||||||
          schema:
 | 
					          schema:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,5 @@
 | 
				
			|||||||
export class AdminURLManager {
 | 
					export class AdminURLManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static applications(rest: string): string {
 | 
					 | 
				
			||||||
        return `/administration/applications/${rest}`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static policies(rest: string): string {
 | 
					    static policies(rest: string): string {
 | 
				
			||||||
        return `/administration/policies/${rest}`;
 | 
					        return `/administration/policies/${rest}`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -54,11 +54,18 @@ export class ModalButton extends LitElement {
 | 
				
			|||||||
        super();
 | 
					        super();
 | 
				
			||||||
        window.addEventListener("keyup", (e) => {
 | 
					        window.addEventListener("keyup", (e) => {
 | 
				
			||||||
            if (e.code === "Escape") {
 | 
					            if (e.code === "Escape") {
 | 
				
			||||||
 | 
					                this.resetForms();
 | 
				
			||||||
                this.open = false;
 | 
					                this.open = false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    resetForms(): void {
 | 
				
			||||||
 | 
					        this.querySelectorAll<HTMLFormElement>("[slot=form]").forEach(form => {
 | 
				
			||||||
 | 
					            form.reset();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updateHandlers(): void {
 | 
					    updateHandlers(): void {
 | 
				
			||||||
        // Ensure links close the modal
 | 
					        // Ensure links close the modal
 | 
				
			||||||
        this.shadowRoot?.querySelectorAll<HTMLAnchorElement>("a").forEach((a) => {
 | 
					        this.shadowRoot?.querySelectorAll<HTMLAnchorElement>("a").forEach((a) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -43,6 +43,40 @@ export class Form<T> extends LitElement {
 | 
				
			|||||||
        return this.successMessage;
 | 
					        return this.successMessage;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reset the inner iron-form
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    reset(): void {
 | 
				
			||||||
 | 
					        const ironForm = this.shadowRoot?.querySelector("iron-form");
 | 
				
			||||||
 | 
					        if (!ironForm) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ironForm.reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * If this form contains a file input, and the input as been filled, this function returns
 | 
				
			||||||
 | 
					     * said file.
 | 
				
			||||||
 | 
					     * @returns File object or undefined
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    getFormFile(): File | undefined {
 | 
				
			||||||
 | 
					        const ironForm = this.shadowRoot?.querySelector("iron-form");
 | 
				
			||||||
 | 
					        if (!ironForm) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const elements = ironForm._getSubmittableElements();
 | 
				
			||||||
 | 
					        for (let i = 0; i < elements.length; i++) {
 | 
				
			||||||
 | 
					            const element = elements[i] as HTMLInputElement;
 | 
				
			||||||
 | 
					            if (element.tagName.toLowerCase() === "input" && element.type === "file") {
 | 
				
			||||||
 | 
					                if ((element.files || []).length < 1) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // We already checked the length
 | 
				
			||||||
 | 
					                return (element.files || [])[0];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    serializeForm(form: IronFormElement): T {
 | 
					    serializeForm(form: IronFormElement): T {
 | 
				
			||||||
        const elements = form._getSubmittableElements();
 | 
					        const elements = form._getSubmittableElements();
 | 
				
			||||||
        const json: { [key: string]: unknown } = {};
 | 
					        const json: { [key: string]: unknown } = {};
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ export class ModalForm extends ModalButton {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            formPromise.then(() => {
 | 
					            formPromise.then(() => {
 | 
				
			||||||
                this.open = false;
 | 
					                this.open = false;
 | 
				
			||||||
 | 
					                form.reset();
 | 
				
			||||||
                this.dispatchEvent(
 | 
					                this.dispatchEvent(
 | 
				
			||||||
                    new CustomEvent(EVENT_REFRESH, {
 | 
					                    new CustomEvent(EVENT_REFRESH, {
 | 
				
			||||||
                        bubbles: true,
 | 
					                        bubbles: true,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										124
									
								
								web/src/pages/applications/ApplicationForm.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								web/src/pages/applications/ApplicationForm.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					import { CoreApi, Application, ProvidersApi, Provider } 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 { until } from "lit-html/directives/until";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit-html/directives/if-defined";
 | 
				
			||||||
 | 
					import "../../elements/forms/HorizontalFormElement";
 | 
				
			||||||
 | 
					import "../../elements/CodeMirror";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@customElement("ak-application-form")
 | 
				
			||||||
 | 
					export class ApplicationForm extends Form<Application> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ attribute: false })
 | 
				
			||||||
 | 
					    application?: Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ attribute: false })
 | 
				
			||||||
 | 
					    provider?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getSuccessMessage(): string {
 | 
				
			||||||
 | 
					        if (this.application) {
 | 
				
			||||||
 | 
					            return gettext("Successfully updated application.");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return gettext("Successfully created application.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    send = (data: Application): Promise<Application> => {
 | 
				
			||||||
 | 
					        let writeOp: Promise<Application>;
 | 
				
			||||||
 | 
					        if (this.application) {
 | 
				
			||||||
 | 
					            writeOp = new CoreApi(DEFAULT_CONFIG).coreApplicationsUpdate({
 | 
				
			||||||
 | 
					                slug: this.application.slug,
 | 
				
			||||||
 | 
					                data: data
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            writeOp = new CoreApi(DEFAULT_CONFIG).coreApplicationsCreate({
 | 
				
			||||||
 | 
					                data: data
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const icon = this.getFormFile();
 | 
				
			||||||
 | 
					        if (icon) {
 | 
				
			||||||
 | 
					            return writeOp.then(app => {
 | 
				
			||||||
 | 
					                return new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIcon({
 | 
				
			||||||
 | 
					                    slug: app.slug,
 | 
				
			||||||
 | 
					                    file: icon
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return writeOp;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    groupProviders(providers: Provider[]): TemplateResult {
 | 
				
			||||||
 | 
					        const m = new Map<string, Provider[]>();
 | 
				
			||||||
 | 
					        providers.forEach(p => {
 | 
				
			||||||
 | 
					            if (!m.has(p.verboseName || "")) {
 | 
				
			||||||
 | 
					                m.set(p.verboseName || "", []);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const tProviders = m.get(p.verboseName || "") || [];
 | 
				
			||||||
 | 
					            tProviders.push(p);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return html`
 | 
				
			||||||
 | 
					            ${Array.from(m).map(([group, providers]) => {
 | 
				
			||||||
 | 
					                return html`<optgroup label=${group}>
 | 
				
			||||||
 | 
					                    ${providers.map(p => {
 | 
				
			||||||
 | 
					                        const selected = (this.application?.provider?.pk === p.pk) || (this.provider === p.pk)
 | 
				
			||||||
 | 
					                        return html`<option ?selected=${selected} value=${ifDefined(p.pk)}>${p.name}</option>`;
 | 
				
			||||||
 | 
					                    })}
 | 
				
			||||||
 | 
					                </optgroup>`;
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					        `;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderForm(): TemplateResult {
 | 
				
			||||||
 | 
					        return html`<form class="pf-c-form pf-m-horizontal">
 | 
				
			||||||
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
 | 
					                label=${gettext("Name")}
 | 
				
			||||||
 | 
					                ?required=${true}
 | 
				
			||||||
 | 
					                name="name">
 | 
				
			||||||
 | 
					                <input type="text" value="${ifDefined(this.application?.name)}" class="pf-c-form-control" required>
 | 
				
			||||||
 | 
					                <p class="pf-c-form__helper-text">${gettext("Application's display Name.")}</p>
 | 
				
			||||||
 | 
					            </ak-form-element-horizontal>
 | 
				
			||||||
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
 | 
					                label=${gettext("Slug")}
 | 
				
			||||||
 | 
					                ?required=${true}
 | 
				
			||||||
 | 
					                name="slug">
 | 
				
			||||||
 | 
					                <input type="text" value="${ifDefined(this.application?.slug)}" class="pf-c-form-control" required>
 | 
				
			||||||
 | 
					                <p class="pf-c-form__helper-text">${gettext("Internal application name, used in URLs.")}</p>
 | 
				
			||||||
 | 
					            </ak-form-element-horizontal>
 | 
				
			||||||
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
 | 
					                label=${gettext("Provider")}
 | 
				
			||||||
 | 
					                name="parent">
 | 
				
			||||||
 | 
					                <select class="pf-c-form-control">
 | 
				
			||||||
 | 
					                    <option value="" ?selected=${this.application?.provider === undefined}>---------</option>
 | 
				
			||||||
 | 
					                    ${until(new ProvidersApi(DEFAULT_CONFIG).providersAllList({}).then(providers => {
 | 
				
			||||||
 | 
					                        return this.groupProviders(providers.results);
 | 
				
			||||||
 | 
					                    }), html``)}
 | 
				
			||||||
 | 
					                </select>
 | 
				
			||||||
 | 
					            </ak-form-element-horizontal>
 | 
				
			||||||
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
 | 
					                label=${gettext("Launch URL")}
 | 
				
			||||||
 | 
					                name="launchUrl">
 | 
				
			||||||
 | 
					                <input type="text" value="${ifDefined(this.application?.launchUrl)}" class="pf-c-form-control">
 | 
				
			||||||
 | 
					                <p class="pf-c-form__helper-text">${gettext("If left empty, authentik will try to extract the launch URL based on the selected provider.")}</p>
 | 
				
			||||||
 | 
					            </ak-form-element-horizontal>
 | 
				
			||||||
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
 | 
					                label=${gettext("Icon")}
 | 
				
			||||||
 | 
					                name="metaIcon">
 | 
				
			||||||
 | 
					                <input type="file" value="${ifDefined(this.application?.metaIcon)}" class="pf-c-form-control">
 | 
				
			||||||
 | 
					            </ak-form-element-horizontal>
 | 
				
			||||||
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
 | 
					                label=${gettext("Description")}
 | 
				
			||||||
 | 
					                name="metaDescription">
 | 
				
			||||||
 | 
					                <textarea class="pf-c-form-control">${ifDefined(this.application?.metaDescription)}</textarea>
 | 
				
			||||||
 | 
					            </ak-form-element-horizontal>
 | 
				
			||||||
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
 | 
					                label=${gettext("Publisher")}
 | 
				
			||||||
 | 
					                name="metaPublisher">
 | 
				
			||||||
 | 
					                <input type="text" value="${ifDefined(this.application?.metaPublisher)}" class="pf-c-form-control">
 | 
				
			||||||
 | 
					            </ak-form-element-horizontal>
 | 
				
			||||||
 | 
					        </form>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,17 +1,17 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { css, CSSResult, customElement, html, property, TemplateResult } from "lit-element";
 | 
					import { css, CSSResult, customElement, html, property, TemplateResult } from "lit-element";
 | 
				
			||||||
 | 
					import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
				
			||||||
import { AKResponse } from "../../api/Client";
 | 
					import { AKResponse } from "../../api/Client";
 | 
				
			||||||
import { TablePage } from "../../elements/table/TablePage";
 | 
					import { TablePage } from "../../elements/table/TablePage";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/buttons/ModalButton";
 | 
					import "../../elements/forms/ModalForm";
 | 
				
			||||||
import "../../elements/forms/DeleteForm";
 | 
					import "../../elements/forms/DeleteForm";
 | 
				
			||||||
import "../../elements/buttons/SpinnerButton";
 | 
					import "../../elements/buttons/SpinnerButton";
 | 
				
			||||||
import { TableColumn } from "../../elements/table/Table";
 | 
					import { TableColumn } from "../../elements/table/Table";
 | 
				
			||||||
import { PAGE_SIZE } from "../../constants";
 | 
					import { PAGE_SIZE } from "../../constants";
 | 
				
			||||||
import { Application, CoreApi } from "authentik-api";
 | 
					import { Application, CoreApi } from "authentik-api";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "../../api/Config";
 | 
					import { DEFAULT_CONFIG } from "../../api/Config";
 | 
				
			||||||
import { AdminURLManager } from "../../api/legacy";
 | 
					import "./ApplicationForm";
 | 
				
			||||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-application-list")
 | 
					@customElement("ak-application-list")
 | 
				
			||||||
export class ApplicationListPage extends TablePage<Application> {
 | 
					export class ApplicationListPage extends TablePage<Application> {
 | 
				
			||||||
@ -74,15 +74,22 @@ export class ApplicationListPage extends TablePage<Application> {
 | 
				
			|||||||
                ${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
 | 
					                ${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
 | 
				
			||||||
            </a>`,
 | 
					            </a>`,
 | 
				
			||||||
            html`<code>${item.slug}</code>`,
 | 
					            html`<code>${item.slug}</code>`,
 | 
				
			||||||
            html`${item.provider?.name}`,
 | 
					            html`${item.provider?.name || "-"}`,
 | 
				
			||||||
            html`${item.provider?.verboseName}`,
 | 
					            html`${item.provider?.verboseName || "-"}`,
 | 
				
			||||||
            html`
 | 
					            html`
 | 
				
			||||||
            <ak-modal-button href="${AdminURLManager.applications(`${item.pk}/update/`)}">
 | 
					            <ak-forms-modal>
 | 
				
			||||||
                <ak-spinner-button slot="trigger" class="pf-m-secondary">
 | 
					                <span slot="submit">
 | 
				
			||||||
 | 
					                    ${gettext("Update")}
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					                <span slot="header">
 | 
				
			||||||
 | 
					                    ${gettext("Update Application")}
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					                <ak-application-form slot="form" .application=${item}>
 | 
				
			||||||
 | 
					                </ak-application-form>
 | 
				
			||||||
 | 
					                <button slot="trigger" class="pf-c-button pf-m-secondary">
 | 
				
			||||||
                    ${gettext("Edit")}
 | 
					                    ${gettext("Edit")}
 | 
				
			||||||
                </ak-spinner-button>
 | 
					                </button>
 | 
				
			||||||
                <div slot="modal"></div>
 | 
					            </ak-forms-modal>
 | 
				
			||||||
            </ak-modal-button>
 | 
					 | 
				
			||||||
            <ak-forms-delete
 | 
					            <ak-forms-delete
 | 
				
			||||||
                .obj=${item}
 | 
					                .obj=${item}
 | 
				
			||||||
                objectLabel=${gettext("Application")}
 | 
					                objectLabel=${gettext("Application")}
 | 
				
			||||||
@ -100,12 +107,19 @@ export class ApplicationListPage extends TablePage<Application> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    renderToolbar(): TemplateResult {
 | 
					    renderToolbar(): TemplateResult {
 | 
				
			||||||
        return html`
 | 
					        return html`
 | 
				
			||||||
        <ak-modal-button href=${AdminURLManager.applications("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 Application")}
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					            <ak-application-form slot="form">
 | 
				
			||||||
 | 
					            </ak-application-form>
 | 
				
			||||||
 | 
					            <button slot="trigger" class="pf-c-button pf-m-primary">
 | 
				
			||||||
 | 
					                ${gettext("Create")}
 | 
				
			||||||
 | 
					            </button>
 | 
				
			||||||
 | 
					        </ak-forms-modal>
 | 
				
			||||||
        ${super.renderToolbar()}
 | 
					        ${super.renderToolbar()}
 | 
				
			||||||
        `;
 | 
					        `;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,6 @@ import { TableColumn } from "../../elements/table/Table";
 | 
				
			|||||||
import { PAGE_SIZE } from "../../constants";
 | 
					import { PAGE_SIZE } from "../../constants";
 | 
				
			||||||
import { EventsApi, NotificationRule } from "authentik-api";
 | 
					import { EventsApi, NotificationRule } from "authentik-api";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "../../api/Config";
 | 
					import { DEFAULT_CONFIG } from "../../api/Config";
 | 
				
			||||||
import { AdminURLManager } from "../../api/legacy";
 | 
					 | 
				
			||||||
import "../../elements/forms/DeleteForm";
 | 
					import "../../elements/forms/DeleteForm";
 | 
				
			||||||
import "./RuleForm";
 | 
					import "./RuleForm";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,21 @@
 | 
				
			|||||||
import { gettext } from "django";
 | 
					import { gettext } from "django";
 | 
				
			||||||
import { customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
					import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
 | 
				
			||||||
import { Provider } from "authentik-api";
 | 
					import { Provider } from "authentik-api";
 | 
				
			||||||
import { AdminURLManager } from "../../api/legacy";
 | 
					import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
				
			||||||
 | 
					import PFButton from "@patternfly/patternfly/components/Button/button.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "../../elements/buttons/ModalButton";
 | 
					import "../../elements/buttons/ModalButton";
 | 
				
			||||||
import "../../elements/Spinner";
 | 
					import "../../elements/Spinner";
 | 
				
			||||||
 | 
					import "../../elements/forms/ModalForm";
 | 
				
			||||||
 | 
					import "../../pages/applications/ApplicationForm";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-provider-related-application")
 | 
					@customElement("ak-provider-related-application")
 | 
				
			||||||
export class RelatedApplicationButton extends LitElement {
 | 
					export class RelatedApplicationButton extends LitElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static get styles(): CSSResult[] {
 | 
				
			||||||
 | 
					        return [PFBase, PFButton];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property({attribute: false})
 | 
					    @property({attribute: false})
 | 
				
			||||||
    provider?: Provider;
 | 
					    provider?: Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -18,12 +25,19 @@ export class RelatedApplicationButton extends LitElement {
 | 
				
			|||||||
                ${this.provider.assignedApplicationName}
 | 
					                ${this.provider.assignedApplicationName}
 | 
				
			||||||
            </a>`;
 | 
					            </a>`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return html`<ak-modal-button href=${AdminURLManager.applications(`create/?provider=${this.provider ? this.provider.pk : ""}`)}>
 | 
					        return html`<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 Application")}
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					            <ak-application-form slot="form" .provider=${this.provider?.pk}>
 | 
				
			||||||
 | 
					            </ak-application-form>
 | 
				
			||||||
 | 
					            <button slot="trigger" class="pf-c-button pf-m-primary">
 | 
				
			||||||
 | 
					                ${gettext("Create")}
 | 
				
			||||||
 | 
					            </button>
 | 
				
			||||||
 | 
					        </ak-forms-modal>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user