web: add UI for notification triggers

This commit is contained in:
Jens Langhammer 2021-01-12 22:26:57 +01:00
parent cb36a3c8c7
commit 47ddf0d7f2
11 changed files with 232 additions and 6 deletions

View File

@ -5,6 +5,7 @@ from authentik.admin.views import (
applications,
certificate_key_pair,
events_notifications_transports,
events_notifications_triggers,
flows,
groups,
outposts,
@ -369,4 +370,20 @@ urlpatterns = [
events_notifications_transports.NotificationTransportDeleteView.as_view(),
name="notification-transport-delete",
),
# Event Notification Triggers
path(
"events/triggers/create/",
events_notifications_triggers.NotificationTriggerCreateView.as_view(),
name="notification-trigger-create",
),
path(
"events/triggers/<uuid:pk>/update/",
events_notifications_triggers.NotificationTriggerUpdateView.as_view(),
name="notification-trigger-update",
),
path(
"events/triggers/<uuid:pk>/delete/",
events_notifications_triggers.NotificationTriggerDeleteView.as_view(),
name="notification-trigger-delete",
),
]

View File

@ -0,0 +1,64 @@
"""authentik NotificationTrigger 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 django.views.generic import UpdateView
from guardian.mixins import PermissionRequiredMixin
from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView
from authentik.events.forms import NotificationTriggerForm
from authentik.events.models import NotificationTrigger
from authentik.lib.views import CreateAssignPermView
class NotificationTriggerCreateView(
SuccessMessageMixin,
BackSuccessUrlMixin,
LoginRequiredMixin,
DjangoPermissionRequiredMixin,
CreateAssignPermView,
):
"""Create new NotificationTrigger"""
model = NotificationTrigger
form_class = NotificationTriggerForm
permission_required = "authentik_events.add_notificationtrigger"
template_name = "generic/create.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully created Notification Trigger")
class NotificationTriggerUpdateView(
SuccessMessageMixin,
BackSuccessUrlMixin,
LoginRequiredMixin,
PermissionRequiredMixin,
UpdateView,
):
"""Update application"""
model = NotificationTrigger
form_class = NotificationTriggerForm
permission_required = "authentik_events.change_notificationtrigger"
template_name = "generic/update.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully updated Notification Trigger")
class NotificationTriggerDeleteView(
LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView
):
"""Delete application"""
model = NotificationTrigger
permission_required = "authentik_events.delete_notificationtrigger"
template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully deleted Notification Trigger")

View File

@ -16,6 +16,7 @@ class NotificationTriggerSerializer(ModelSerializer):
"name",
"transports",
"severity",
"group",
]

View File

@ -2,7 +2,7 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from authentik.events.models import NotificationTransport
from authentik.events.models import NotificationTransport, NotificationTrigger
class NotificationTransportForm(forms.ModelForm):
@ -25,8 +25,23 @@ class NotificationTransportForm(forms.ModelForm):
}
help_texts = {
"webhook_url": _(
(
"Only required when the Generic or Slack Webhook is used."
)
("Only required when the Generic or Slack Webhook is used.")
),
}
class NotificationTriggerForm(forms.ModelForm):
"""NotificationTrigger Form"""
class Meta:
model = NotificationTrigger
fields = [
"name",
"transports",
"severity",
"group",
]
widgets = {
"name": forms.TextInput(),
}

View File

@ -7720,6 +7720,13 @@ definitions:
- notice
- warning
- alert
group:
title: Group
description: Define which group of users this notification should be sent
and shown to. If left empty, Notification won't ben sent.
type: string
format: uuid
x-nullable: true
Stage:
title: Stage obj
description: Stage Serializer

View File

@ -0,0 +1,25 @@
import { DefaultClient, QueryArguments, PBResponse } from "./Client";
export class Trigger {
pk: string;
name: string;
transports: string[];
severity: string;
group?: string;
constructor() {
throw Error();
}
static get(pk: string): Promise<Trigger> {
return DefaultClient.fetch<Trigger>(["events", "triggers", pk]);
}
static list(filter?: QueryArguments): Promise<PBResponse<Trigger>> {
return DefaultClient.fetch<PBResponse<Trigger>>(["events", "triggers"], filter);
}
static adminUrl(rest: string): string {
return `/administration/events/triggers/${rest}`;
}
}

View File

@ -44,7 +44,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
Edit
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
</ak-modal-button>&nbsp;
<ak-modal-button href="${PolicyBinding.adminUrl(`${item.pk}/delete/`)}">
<ak-spinner-button slot="trigger" class="pf-m-danger">
Delete

View File

@ -14,7 +14,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
}),
new SidebarItem("Events").children(
new SidebarItem("Log", "/events/log"),
new SidebarItem("Notification Triggers", "/administration/tasks/"),
new SidebarItem("Notification Triggers", "/events/triggers"),
new SidebarItem("Notification Transports", "/events/transports"),
).when((): Promise<boolean> => {
return User.me().then(u => u.is_superuser);

View File

@ -3,6 +3,7 @@ import { customElement, html, property, TemplateResult } from "lit-element";
import { DefaultClient, PBResponse } from "../../api/Client";
import { TablePage } from "../../elements/table/TablePage";
import "../../elements/buttons/ActionButton";
import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";

View File

@ -0,0 +1,94 @@
import { gettext } from "django";
import { customElement, html, property, TemplateResult } from "lit-element";
import { PBResponse } from "../../api/Client";
import { TablePage } from "../../elements/table/TablePage";
import "../../elements/policies/BoundPoliciesList";
import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { Trigger } from "../../api/EventTriggers";
@customElement("ak-event-trigger-list")
export class TriggerListPage extends TablePage<Trigger> {
expandable = true;
searchEnabled(): boolean {
return true;
}
pageTitle(): string {
return gettext("Notification Triggers");
}
pageDescription(): string {
return gettext("Send notifications on whenever a specific Event is created and matched by policies.");
}
pageIcon(): string {
return gettext("pf-icon pf-icon-attention-bell");
}
@property()
order = "name";
apiEndpoint(page: number): Promise<PBResponse<Trigger>> {
return Trigger.list({
ordering: this.order,
page: page,
search: this.search || "",
});
}
columns(): TableColumn[] {
return [
new TableColumn("Name", "name"),
new TableColumn("Severity", "severity"),
new TableColumn("Sent to group", "group"),
new TableColumn(""),
];
}
row(item: Trigger): TemplateResult[] {
return [
html`${item.name}`,
html`${item.severity}`,
html`${item.group || gettext("None (trigger disabled)")}`,
html`
<ak-modal-button href="${Trigger.adminUrl(`${item.pk}/update/`)}">
<ak-spinner-button slot="trigger" class="pf-m-secondary">
${gettext("Edit")}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>&nbsp;
<ak-modal-button href="${Trigger.adminUrl(`${item.pk}/delete/`)}">
<ak-spinner-button slot="trigger" class="pf-m-danger">
${gettext("Delete")}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
`,
];
}
renderToolbar(): TemplateResult {
return html`
<ak-modal-button href=${Trigger.adminUrl("create/")}>
<ak-spinner-button slot="trigger" class="pf-m-primary">
${gettext("Create")}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
${super.renderToolbar()}
`;
}
renderExpanded(item: Trigger): TemplateResult {
return html`
<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<ak-bound-policies-list .target=${item.pk}>
</ak-bound-policies-list>
</div>
</td>
<td></td>
<td></td>`;
}
}

View File

@ -9,6 +9,7 @@ import "./pages/sources/SourceViewPage";
import "./pages/flows/FlowViewPage";
import "./pages/events/EventListPage";
import "./pages/events/TransportListPage";
import "./pages/events/TriggerListPage";
export const ROUTES: Route[] = [
// Prevent infinite Shell loops
@ -28,4 +29,5 @@ export const ROUTES: Route[] = [
}),
new Route(new RegExp("^/events/log$"), html`<ak-event-list></ak-event-list>`),
new Route(new RegExp("^/events/transports$"), html`<ak-event-transport-list></ak-event-transport-list>`),
new Route(new RegExp("^/events/triggers$"), html`<ak-event-trigger-list></ak-event-trigger-list>`),
];