admin: finalise migration
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
d7698343ae
commit
aaebd01058
|
@ -7,5 +7,4 @@ class AuthentikAdminConfig(AppConfig):
|
|||
|
||||
name = "authentik.admin"
|
||||
label = "authentik_admin"
|
||||
mountpoint = "administration/"
|
||||
verbose_name = "authentik Admin"
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
{% extends base_template|default:"generic/form.html" %}
|
||||
|
||||
{% load authentik_utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block above_form %}
|
||||
<h1>
|
||||
{% blocktrans with type=form|form_verbose_name %}
|
||||
Create {{ type }}
|
||||
{% endblocktrans %}
|
||||
</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block action %}
|
||||
{% blocktrans with type=form|form_verbose_name %}
|
||||
Create {{ type }}
|
||||
{% endblocktrans %}
|
||||
{% endblock %}
|
|
@ -1,38 +0,0 @@
|
|||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
{% block above_form %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section">
|
||||
<div class="pf-l-stack">
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<form id="main-form" action="" method="post" class="pf-c-form pf-m-horizontal" enctype="multipart/form-data">
|
||||
{% include 'partials/form_horizontal.html' with form=form %}
|
||||
{% block beneath_form %}
|
||||
{% endblock %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="pf-c-modal-box__footer">
|
||||
<ak-spinner-button form="main-form">
|
||||
{% block action %}{% endblock %}
|
||||
</ak-spinner-button>
|
||||
<a class="pf-c-button pf-m-secondary" href="#/">{% trans "Cancel" %}</a>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{{ block.super }}
|
||||
{{ form.media.js }}
|
||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||
{% extends base_template|default:"generic/form.html" %}
|
||||
|
||||
{% load authentik_utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block above_form %}
|
||||
<h1>
|
||||
{% blocktrans with type=form|form_verbose_name|title inst=form.instance %}
|
||||
Update {{ inst }}
|
||||
{% endblocktrans %}
|
||||
</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block action %}
|
||||
{% blocktrans with type=form|form_verbose_name %}
|
||||
Update {{ type }}
|
||||
{% endblocktrans %}
|
||||
{% endblock %}
|
|
@ -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/<uuid:pk>/update/",
|
||||
stages.StageUpdateView.as_view(),
|
||||
name="stage-update",
|
||||
),
|
||||
]
|
|
@ -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")
|
|
@ -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()
|
||||
)
|
|
@ -1,115 +0,0 @@
|
|||
{% load authentik_utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
{% if field.field.widget|fieldtype == 'HiddenInput' %}
|
||||
{{ field }}
|
||||
{% else %}
|
||||
<div class="pf-c-form__group {% if field.errors %} has-error {% endif %}">
|
||||
{% if field.field.widget|fieldtype == 'RadioSelect' %}
|
||||
<div class="pf-c-form__group-label">
|
||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
|
||||
<span class="pf-c-form__label-text">{{ field.label }}</span>
|
||||
{% if field.field.required %}
|
||||
<span class="pf-c-form__label-required" aria-hidden="true">*</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
<div class="pf-c-form__group-control">
|
||||
{% for c in field %}
|
||||
<div class="pf-c-radio">
|
||||
<input class="pf-c-radio__input"
|
||||
type="radio" id="{{ field.name }}-{{ forloop.counter0 }}"
|
||||
name="{% if wizard %}{{ wizard.steps.current }}-{% endif %}{{ field.name }}"
|
||||
value="{{ c.data.value }}"
|
||||
{% if c.data.selected %} checked {% endif %}/>
|
||||
<label class="pf-c-radio__label" for="{{ field.name }}-{{ forloop.counter0 }}">{{ c.choice_label }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if field.help_text %}
|
||||
<p class="pf-c-form__helper-text">{{ field.help_text }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elif field.field.widget|fieldtype == 'Select' or field.field.widget|fieldtype == "SelectMultiple" %}
|
||||
<div class="pf-c-form__group-label">
|
||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
|
||||
<span class="pf-c-form__label-text">{{ field.label }}</span>
|
||||
{% if field.field.required %}
|
||||
<span class="pf-c-form__label-required" aria-hidden="true">*</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="pf-c-form__horizontal-group">
|
||||
{{ field|css_class:"pf-c-form-control" }}
|
||||
{% if field.help_text %}
|
||||
<p class="pf-c-form__helper-text">{{ field.help_text|safe }}</p>
|
||||
{% endif %}
|
||||
{% if field.field.widget|fieldtype == 'SelectMultiple' %}
|
||||
<p class="pf-c-form__helper-text">{% trans 'Hold control/command to select multiple items.' %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% elif field.field.widget|fieldtype == 'CheckboxInput' %}
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="pf-c-form__horizontal-group">
|
||||
<div class="pf-c-check">
|
||||
{{ field|css_class:"pf-c-check__input" }}
|
||||
<label class="pf-c-check__label" for="{{ field.name }}-{{ forloop.counter0 }}">{{ field.label }}</label>
|
||||
</div>
|
||||
{% if field.help_text %}
|
||||
<p class="pf-c-form__helper-text">{{ field.help_text|safe }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% elif field.field.widget|fieldtype == "FileInput" %}
|
||||
<div class="pf-c-form__group-label">
|
||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
|
||||
<span class="pf-c-form__label-text">{{ field.label }}</span>
|
||||
{% if field.field.required %}
|
||||
<span class="pf-c-form__label-required" aria-hidden="true">*</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="c-form__horizontal-group">
|
||||
{{ field|css_class:"pf-c-form-control" }}
|
||||
{% if field.help_text %}
|
||||
<p class="pf-c-form__helper-text">{{ field.help_text|safe }}</p>
|
||||
{% endif %}
|
||||
{% if field.value %}
|
||||
<a target="_blank" href="{{ field.value.url }}" class="pf-c-form__helper-text">
|
||||
{% blocktrans with current=field.value %}
|
||||
Currently set to {{current}}.
|
||||
{% endblocktrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-form__group-label">
|
||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
|
||||
<span class="pf-c-form__label-text">{{ field.label }}</span>
|
||||
{% if field.field.required %}
|
||||
<span class="pf-c-form__label-required" aria-hidden="true">*</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="c-form__horizontal-group">
|
||||
{{ field|css_class:'pf-c-form-control' }}
|
||||
{% if field.help_text %}
|
||||
<p class="pf-c-form__helper-text">{{ field.help_text|safe }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for error in field.errors %}
|
||||
<p class="pf-c-form__helper-text pf-m-error">
|
||||
{{ error }}
|
||||
</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
|
@ -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"])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
)
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<Stage> {
|
||||
|
@ -61,12 +80,33 @@ export class StageListPage extends TablePage<Stage> {
|
|||
</a>`;
|
||||
})}`,
|
||||
html`
|
||||
<ak-modal-button href="${AdminURLManager.stages(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${gettext("Update")}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${gettext(`Update ${item.verboseName}`)}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
.args=${{
|
||||
"stageUUID": item.pk
|
||||
}}
|
||||
type=${ifDefined(item.objectType)}
|
||||
.typeMap=${{
|
||||
"dummy": "ak-policy-dummy-form",
|
||||
"eventmatcher": "ak-policy-event-matcher-form",
|
||||
"expression": "ak-policy-expression-form",
|
||||
"passwordexpiry": "ak-policy-password-expiry-form",
|
||||
"haveibeenpwend": "ak-policy-hibp-form",
|
||||
"password": "ak-policy-password-form",
|
||||
"reputation": "ak-policy-reputation-form",
|
||||
}}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-forms-delete
|
||||
.obj=${item}
|
||||
objectLabel=${gettext("Group")}
|
||||
|
@ -93,12 +133,21 @@ export class StageListPage extends TablePage<Stage> {
|
|||
${until(new StagesApi(DEFAULT_CONFIG).stagesAllTypes().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-modal-button href="${type.component}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${type.name}<br>
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
${gettext("Create")}
|
||||
</span>
|
||||
<span slot="header">
|
||||
${gettext(`Create ${type.name}`)}
|
||||
</span>
|
||||
<ak-proxy-form
|
||||
slot="form"
|
||||
type=${type.component}>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
|
|
Reference in New Issue