diff --git a/authentik/admin/apps.py b/authentik/admin/apps.py index db05f4daa..0c1da4d62 100644 --- a/authentik/admin/apps.py +++ b/authentik/admin/apps.py @@ -7,5 +7,4 @@ class AuthentikAdminConfig(AppConfig): name = "authentik.admin" label = "authentik_admin" - mountpoint = "administration/" verbose_name = "authentik Admin" diff --git a/authentik/admin/templates/generic/create.html b/authentik/admin/templates/generic/create.html deleted file mode 100644 index c1cbe91f1..000000000 --- a/authentik/admin/templates/generic/create.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends base_template|default:"generic/form.html" %} - -{% load authentik_utils %} -{% load i18n %} - -{% block above_form %} -

- {% blocktrans with type=form|form_verbose_name %} - Create {{ type }} - {% endblocktrans %} -

-{% endblock %} - -{% block action %} -{% blocktrans with type=form|form_verbose_name %} -Create {{ type }} -{% endblocktrans %} -{% endblock %} diff --git a/authentik/admin/templates/generic/form.html b/authentik/admin/templates/generic/form.html deleted file mode 100644 index 0dd1c2ad1..000000000 --- a/authentik/admin/templates/generic/form.html +++ /dev/null @@ -1,38 +0,0 @@ -{% load i18n %} -{% load authentik_utils %} -{% load static %} - -{% block content %} -
-
- {% block above_form %} - {% endblock %} -
-
-
-
-
-
-
-
- {% include 'partials/form_horizontal.html' with form=form %} - {% block beneath_form %} - {% endblock %} -
-
-
-
-
-
- -{% endblock %} - -{% block scripts %} -{{ block.super }} -{{ form.media.js }} -{% endblock %} diff --git a/authentik/admin/templates/generic/update.html b/authentik/admin/templates/generic/update.html deleted file mode 100644 index 7b46d40f9..000000000 --- a/authentik/admin/templates/generic/update.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends base_template|default:"generic/form.html" %} - -{% load authentik_utils %} -{% load i18n %} - -{% block above_form %} -

- {% blocktrans with type=form|form_verbose_name|title inst=form.instance %} - Update {{ inst }} - {% endblocktrans %} -

-{% endblock %} - -{% block action %} -{% blocktrans with type=form|form_verbose_name %} -Update {{ type }} -{% endblocktrans %} -{% endblock %} diff --git a/authentik/admin/urls.py b/authentik/admin/urls.py deleted file mode 100644 index 1871d2b21..000000000 --- a/authentik/admin/urls.py +++ /dev/null @@ -1,14 +0,0 @@ -"""authentik URL Configuration""" -from django.urls import path - -from authentik.admin.views import stages - -urlpatterns = [ - # Stages - path("stages/create/", stages.StageCreateView.as_view(), name="stage-create"), - path( - "stages//update/", - stages.StageUpdateView.as_view(), - name="stage-update", - ), -] diff --git a/authentik/admin/views/__init__.py b/authentik/admin/views/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/authentik/admin/views/stages.py b/authentik/admin/views/stages.py deleted file mode 100644 index 39ff3ffbb..000000000 --- a/authentik/admin/views/stages.py +++ /dev/null @@ -1,43 +0,0 @@ -"""authentik Stage 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.urls import reverse_lazy -from django.utils.translation import gettext as _ -from guardian.mixins import PermissionRequiredMixin - -from authentik.admin.views.utils import InheritanceCreateView, InheritanceUpdateView -from authentik.flows.models import Stage - - -class StageCreateView( - SuccessMessageMixin, - LoginRequiredMixin, - DjangoPermissionRequiredMixin, - InheritanceCreateView, -): - """Create new Stage""" - - model = Stage - template_name = "generic/create.html" - permission_required = "authentik_flows.add_stage" - - success_url = reverse_lazy("authentik_core:if-admin") - success_message = _("Successfully created Stage") - - -class StageUpdateView( - SuccessMessageMixin, - LoginRequiredMixin, - PermissionRequiredMixin, - InheritanceUpdateView, -): - """Update stage""" - - model = Stage - permission_required = "authentik_flows.update_application" - template_name = "generic/update.html" - success_url = reverse_lazy("authentik_core:if-admin") - success_message = _("Successfully updated Stage") diff --git a/authentik/admin/views/utils.py b/authentik/admin/views/utils.py deleted file mode 100644 index 4c2fe7a38..000000000 --- a/authentik/admin/views/utils.py +++ /dev/null @@ -1,50 +0,0 @@ -"""authentik admin util views""" -from typing import Any - -from django.http import Http404 -from django.views.generic import UpdateView - -from authentik.lib.utils.reflection import all_subclasses -from authentik.lib.views import CreateAssignPermView - - -class InheritanceCreateView(CreateAssignPermView): - """CreateView for objects using InheritanceManager""" - - def get_form_class(self): - provider_type = self.request.GET.get("type") - try: - model = next( - x for x in all_subclasses(self.model) if x.__name__ == provider_type - ) - except StopIteration as exc: - raise Http404 from exc - return model().form - - def get_context_data(self, **kwargs: Any) -> dict[str, Any]: - kwargs = super().get_context_data(**kwargs) - form_cls = self.get_form_class() - if hasattr(form_cls, "template_name"): - kwargs["base_template"] = form_cls.template_name - return kwargs - - -class InheritanceUpdateView(UpdateView): - """UpdateView for objects using InheritanceManager""" - - def get_context_data(self, **kwargs: Any) -> dict[str, Any]: - kwargs = super().get_context_data(**kwargs) - form_cls = self.get_form_class() - if hasattr(form_cls, "template_name"): - kwargs["base_template"] = form_cls.template_name - return kwargs - - def get_form_class(self): - return self.get_object().form - - def get_object(self, queryset=None): - return ( - self.model.objects.filter(pk=self.kwargs.get("pk")) - .select_subclasses() - .first() - ) diff --git a/authentik/core/templates/partials/form_horizontal.html b/authentik/core/templates/partials/form_horizontal.html deleted file mode 100644 index 14baa5286..000000000 --- a/authentik/core/templates/partials/form_horizontal.html +++ /dev/null @@ -1,115 +0,0 @@ -{% load authentik_utils %} -{% load i18n %} - -{% csrf_token %} -{% for field in form %} -{% if field.field.widget|fieldtype == 'HiddenInput' %} -{{ field }} -{% else %} -
- {% if field.field.widget|fieldtype == 'RadioSelect' %} -
- -
-
- {% for c in field %} -
- - -
- {% endfor %} - {% if field.help_text %} -

{{ field.help_text }}

- {% endif %} -
- {% elif field.field.widget|fieldtype == 'Select' or field.field.widget|fieldtype == "SelectMultiple" %} -
- -
-
-
- {{ field|css_class:"pf-c-form-control" }} - {% if field.help_text %} -

{{ field.help_text|safe }}

- {% endif %} - {% if field.field.widget|fieldtype == 'SelectMultiple' %} -

{% trans 'Hold control/command to select multiple items.' %}

- {% endif %} -
-
- {% elif field.field.widget|fieldtype == 'CheckboxInput' %} -
-
-
- {{ field|css_class:"pf-c-check__input" }} - -
- {% if field.help_text %} -

{{ field.help_text|safe }}

- {% endif %} -
-
- {% elif field.field.widget|fieldtype == "FileInput" %} -
- -
-
-
- {{ field|css_class:"pf-c-form-control" }} - {% if field.help_text %} -

{{ field.help_text|safe }}

- {% endif %} - {% if field.value %} - - {% blocktrans with current=field.value %} - Currently set to {{current}}. - {% endblocktrans %} - - {% endif %} -
-
- {% else %} -
- -
-
-
- {{ field|css_class:'pf-c-form-control' }} - {% if field.help_text %} -

{{ field.help_text|safe }}

- {% endif %} -
-
- {% endif %} - {% for error in field.errors %} -

- {{ error }} -

- {% endfor %} -
-{% endif %} -{% endfor %} diff --git a/authentik/flows/api/stages.py b/authentik/flows/api/stages.py index cbfc0c65f..d639a7f6b 100644 --- a/authentik/flows/api/stages.py +++ b/authentik/flows/api/stages.py @@ -1,7 +1,6 @@ """Flow Stage API Views""" from typing import Iterable -from django.urls import reverse from drf_yasg.utils import swagger_auto_schema from rest_framework import mixins from rest_framework.decorators import action @@ -70,8 +69,7 @@ class StageViewSet( { "name": verbose_name(subclass), "description": subclass.__doc__, - "component": reverse("authentik_admin:stage-create") - + f"?type={subclass.__name__}", + "component": subclass().component, } ) data = sorted(data, key=lambda x: x["name"]) diff --git a/authentik/flows/models.py b/authentik/flows/models.py index 3db3db439..75c0bc930 100644 --- a/authentik/flows/models.py +++ b/authentik/flows/models.py @@ -3,7 +3,6 @@ from typing import TYPE_CHECKING, Optional, Type from uuid import uuid4 from django.db import models -from django.forms import ModelForm from django.http import HttpRequest from django.utils.translation import gettext_lazy as _ from model_utils.managers import InheritanceManager @@ -60,8 +59,8 @@ class Stage(SerializerModel): raise NotImplementedError @property - def form(self) -> Type[ModelForm]: - """Return Form class used to edit this object""" + def component(self) -> str: + """Return component used to edit this object""" raise NotImplementedError @property diff --git a/authentik/flows/tests/test_models.py b/authentik/flows/tests/test_models.py deleted file mode 100644 index 864518c39..000000000 --- a/authentik/flows/tests/test_models.py +++ /dev/null @@ -1,31 +0,0 @@ -"""flow model tests""" -from typing import Callable, Type - -from django.forms import ModelForm -from django.test import TestCase - -from authentik.flows.models import Stage -from authentik.flows.stage import StageView - - -class TestStageProperties(TestCase): - """Generic model properties tests""" - - -def stage_tester_factory(model: Type[Stage]) -> Callable: - """Test a form""" - - def tester(self: TestStageProperties): - model_inst = model() - self.assertTrue(issubclass(model_inst.form, ModelForm)) - self.assertTrue(issubclass(model_inst.type, StageView)) - - return tester - - -for stage_type in Stage.__subclasses__(): - setattr( - TestStageProperties, - f"test_stage_{stage_type.__name__}", - stage_tester_factory(stage_type), - ) diff --git a/authentik/stages/deny/tests.py b/authentik/stages/deny/tests.py index 22b4babc9..f87d4601a 100644 --- a/authentik/stages/deny/tests.py +++ b/authentik/stages/deny/tests.py @@ -9,7 +9,6 @@ from authentik.flows.markers import StageMarker from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding from authentik.flows.planner import FlowPlan from authentik.flows.views import SESSION_KEY_PLAN -from authentik.stages.deny.forms import DenyStageForm from authentik.stages.deny.models import DenyStage @@ -52,8 +51,3 @@ class TestUserDenyStage(TestCase): "type": ChallengeTypes.NATIVE.value, }, ) - - def test_form(self): - """Test Form""" - data = {"name": "test"} - self.assertEqual(DenyStageForm(data).is_valid(), True) diff --git a/authentik/stages/dummy/tests.py b/authentik/stages/dummy/tests.py index 02dd11876..b943d391e 100644 --- a/authentik/stages/dummy/tests.py +++ b/authentik/stages/dummy/tests.py @@ -5,7 +5,6 @@ from django.utils.encoding import force_str from authentik.core.models import User from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding -from authentik.stages.dummy.forms import DummyStageForm from authentik.stages.dummy.models import DummyStage @@ -49,8 +48,3 @@ class TestDummyStage(TestCase): force_str(response.content), {"to": reverse("authentik_core:root-redirect"), "type": "redirect"}, ) - - def test_form(self): - """Test Form""" - data = {"name": "test"} - self.assertEqual(DummyStageForm(data).is_valid(), True) diff --git a/web/src/api/legacy.ts b/web/src/api/legacy.ts index 47d430d8d..871491be5 100644 --- a/web/src/api/legacy.ts +++ b/web/src/api/legacy.ts @@ -1,15 +1,3 @@ -export class AdminURLManager { - - static policies(rest: string): string { - return `/administration/policies/${rest}`; - } - - static stages(rest: string): string { - return `/administration/stages/${rest}`; - } - -} - export class AppURLManager { static sourceSAML(slug: string, rest: string): string { diff --git a/web/src/pages/stages/StageListPage.ts b/web/src/pages/stages/StageListPage.ts index 2e760fabc..a9dedb57f 100644 --- a/web/src/pages/stages/StageListPage.ts +++ b/web/src/pages/stages/StageListPage.ts @@ -4,15 +4,34 @@ import { AKResponse } from "../../api/Client"; import { TableColumn } from "../../elements/table/Table"; import { TablePage } from "../../elements/table/TablePage"; -import "../../elements/buttons/ModalButton"; import "../../elements/buttons/SpinnerButton"; import "../../elements/buttons/Dropdown"; import "../../elements/forms/DeleteForm"; +import "../../elements/forms/ProxyForm"; +import "../../elements/forms/ModalForm"; import { until } from "lit-html/directives/until"; import { PAGE_SIZE } from "../../constants"; import { Stage, StagesApi } from "authentik-api"; import { DEFAULT_CONFIG } from "../../api/Config"; -import { AdminURLManager } from "../../api/legacy"; +import { ifDefined } from "lit-html/directives/if-defined"; + +import "./pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts"; +import "./pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts"; +import "./pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts"; +import "./pages/stages/authenticator_webauthn/AuthenticateWebAuthnStageForm.ts"; +import "./pages/stages/captcha/CaptchaStageForm.ts"; +import "./pages/stages/consent/ConsentStageForm.ts"; +import "./pages/stages/deny/DenyStageForm.ts"; +import "./pages/stages/dummy/DummyStageForm.ts"; +import "./pages/stages/email/EmailStageForm.ts"; +import "./pages/stages/identification/IdentificationStageForm.ts"; +import "./pages/stages/invitation/InvitationStageForm.ts"; +import "./pages/stages/password/PasswordStageForm.ts"; +import "./pages/stages/prompt/PromptStageForm.ts"; +import "./pages/stages/user_delete/UserDeleteStageForm.ts"; +import "./pages/stages/user_login/UserLoginStageForm.ts"; +import "./pages/stages/user_logout/UserLogoutStageForm.ts"; +import "./pages/stages/user_write/UserWriteStageForm.ts"; @customElement("ak-stage-list") export class StageListPage extends TablePage { @@ -61,12 +80,33 @@ export class StageListPage extends TablePage { `; })}`, html` - - + + + ${gettext("Update")} + + + ${gettext(`Update ${item.verboseName}`)} + + + + + { ${until(new StagesApi(DEFAULT_CONFIG).stagesAllTypes().then((types) => { return types.map((type) => { return html`
  • - - -
    -
  • `; }); }), html``)}