events: triggers -> rules

This commit is contained in:
Jens Langhammer 2021-01-15 16:23:27 +01:00
parent 0b41cb84f0
commit 192dbe05c4
19 changed files with 298 additions and 298 deletions

View File

@ -4,8 +4,8 @@ from django.urls import path
from authentik.admin.views import ( from authentik.admin.views import (
applications, applications,
certificate_key_pair, certificate_key_pair,
events_notifications_rules,
events_notifications_transports, events_notifications_transports,
events_notifications_triggers,
flows, flows,
groups, groups,
outposts, outposts,
@ -370,20 +370,20 @@ urlpatterns = [
events_notifications_transports.NotificationTransportDeleteView.as_view(), events_notifications_transports.NotificationTransportDeleteView.as_view(),
name="notification-transport-delete", name="notification-transport-delete",
), ),
# Event Notification Triggers # Event Notification Rules
path( path(
"events/triggers/create/", "events/rules/create/",
events_notifications_triggers.NotificationTriggerCreateView.as_view(), events_notifications_rules.NotificationRuleCreateView.as_view(),
name="notification-trigger-create", name="notification-rule-create",
), ),
path( path(
"events/triggers/<uuid:pk>/update/", "events/rules/<uuid:pk>/update/",
events_notifications_triggers.NotificationTriggerUpdateView.as_view(), events_notifications_rules.NotificationRuleUpdateView.as_view(),
name="notification-trigger-update", name="notification-rule-update",
), ),
path( path(
"events/triggers/<uuid:pk>/delete/", "events/rules/<uuid:pk>/delete/",
events_notifications_triggers.NotificationTriggerDeleteView.as_view(), events_notifications_rules.NotificationRuleDeleteView.as_view(),
name="notification-trigger-delete", name="notification-rule-delete",
), ),
] ]

View File

@ -1,4 +1,4 @@
"""authentik NotificationTrigger administration""" """authentik NotificationRule administration"""
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import ( from django.contrib.auth.mixins import (
PermissionRequiredMixin as DjangoPermissionRequiredMixin, PermissionRequiredMixin as DjangoPermissionRequiredMixin,
@ -10,30 +10,30 @@ from django.views.generic import UpdateView
from guardian.mixins import PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView
from authentik.events.forms import NotificationTriggerForm from authentik.events.forms import NotificationRuleForm
from authentik.events.models import NotificationTrigger from authentik.events.models import NotificationRule
from authentik.lib.views import CreateAssignPermView from authentik.lib.views import CreateAssignPermView
class NotificationTriggerCreateView( class NotificationRuleCreateView(
SuccessMessageMixin, SuccessMessageMixin,
BackSuccessUrlMixin, BackSuccessUrlMixin,
LoginRequiredMixin, LoginRequiredMixin,
DjangoPermissionRequiredMixin, DjangoPermissionRequiredMixin,
CreateAssignPermView, CreateAssignPermView,
): ):
"""Create new NotificationTrigger""" """Create new NotificationRule"""
model = NotificationTrigger model = NotificationRule
form_class = NotificationTriggerForm form_class = NotificationRuleForm
permission_required = "authentik_events.add_notificationtrigger" permission_required = "authentik_events.add_NotificationRule"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("authentik_core:shell") success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully created Notification Trigger") success_message = _("Successfully created Notification Rule")
class NotificationTriggerUpdateView( class NotificationRuleUpdateView(
SuccessMessageMixin, SuccessMessageMixin,
BackSuccessUrlMixin, BackSuccessUrlMixin,
LoginRequiredMixin, LoginRequiredMixin,
@ -42,23 +42,23 @@ class NotificationTriggerUpdateView(
): ):
"""Update application""" """Update application"""
model = NotificationTrigger model = NotificationRule
form_class = NotificationTriggerForm form_class = NotificationRuleForm
permission_required = "authentik_events.change_notificationtrigger" permission_required = "authentik_events.change_NotificationRule"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("authentik_core:shell") success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully updated Notification Trigger") success_message = _("Successfully updated Notification Rule")
class NotificationTriggerDeleteView( class NotificationRuleDeleteView(
LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView
): ):
"""Delete application""" """Delete application"""
model = NotificationTrigger model = NotificationRule
permission_required = "authentik_events.delete_notificationtrigger" permission_required = "authentik_events.delete_NotificationRule"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_core:shell") success_url = reverse_lazy("authentik_core:shell")
success_message = _("Successfully deleted Notification Trigger") success_message = _("Successfully deleted Notification Rule")

View File

@ -21,8 +21,8 @@ from authentik.core.api.users import UserViewSet
from authentik.crypto.api import CertificateKeyPairViewSet from authentik.crypto.api import CertificateKeyPairViewSet
from authentik.events.api.event import EventViewSet from authentik.events.api.event import EventViewSet
from authentik.events.api.notification import NotificationViewSet from authentik.events.api.notification import NotificationViewSet
from authentik.events.api.notification_rule import NotificationRuleViewSet
from authentik.events.api.notification_transport import NotificationTransportViewSet from authentik.events.api.notification_transport import NotificationTransportViewSet
from authentik.events.api.notification_trigger import NotificationTriggerViewSet
from authentik.flows.api import ( from authentik.flows.api import (
FlowCacheViewSet, FlowCacheViewSet,
FlowStageBindingViewSet, FlowStageBindingViewSet,
@ -103,7 +103,7 @@ router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet)
router.register("events/events", EventViewSet) router.register("events/events", EventViewSet)
router.register("events/notifications", NotificationViewSet) router.register("events/notifications", NotificationViewSet)
router.register("events/transports", NotificationTransportViewSet) router.register("events/transports", NotificationTransportViewSet)
router.register("events/triggers", NotificationTriggerViewSet) router.register("events/rules", NotificationRuleViewSet)
router.register("sources/all", SourceViewSet) router.register("sources/all", SourceViewSet)
router.register("sources/ldap", LDAPSourceViewSet) router.register("sources/ldap", LDAPSourceViewSet)

View File

@ -0,0 +1,28 @@
"""NotificationRule API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.events.models import NotificationRule
class NotificationRuleSerializer(ModelSerializer):
"""NotificationRule Serializer"""
class Meta:
model = NotificationRule
depth = 2
fields = [
"pk",
"name",
"transports",
"severity",
"group",
]
class NotificationRuleViewSet(ModelViewSet):
"""NotificationRule Viewset"""
queryset = NotificationRule.objects.all()
serializer_class = NotificationRuleSerializer

View File

@ -1,28 +0,0 @@
"""NotificationTrigger API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.events.models import NotificationTrigger
class NotificationTriggerSerializer(ModelSerializer):
"""NotificationTrigger Serializer"""
class Meta:
model = NotificationTrigger
depth = 2
fields = [
"pk",
"name",
"transports",
"severity",
"group",
]
class NotificationTriggerViewSet(ModelViewSet):
"""NotificationTrigger Viewset"""
queryset = NotificationTrigger.objects.all()
serializer_class = NotificationTriggerSerializer

View File

@ -2,7 +2,7 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from authentik.events.models import NotificationTransport, NotificationTrigger from authentik.events.models import NotificationRule, NotificationTransport
class NotificationTransportForm(forms.ModelForm): class NotificationTransportForm(forms.ModelForm):
@ -30,12 +30,12 @@ class NotificationTransportForm(forms.ModelForm):
} }
class NotificationTriggerForm(forms.ModelForm): class NotificationRuleForm(forms.ModelForm):
"""NotificationTrigger Form""" """NotificationRule Form"""
class Meta: class Meta:
model = NotificationTrigger model = NotificationRule
fields = [ fields = [
"name", "name",
"group", "group",

View File

@ -48,7 +48,7 @@ class Migration(migrations.Migration):
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name="NotificationTrigger", name="NotificationRule",
fields=[ fields=[
( (
"policybindingmodel_ptr", "policybindingmodel_ptr",
@ -93,8 +93,8 @@ class Migration(migrations.Migration):
), ),
], ],
options={ options={
"verbose_name": "Notification Trigger", "verbose_name": "Notification Rule",
"verbose_name_plural": "Notification Triggers", "verbose_name_plural": "Notification Rules",
}, },
bases=("authentik_policies.policybindingmodel",), bases=("authentik_policies.policybindingmodel",),
), ),

View File

@ -14,7 +14,7 @@ def notify_configuration_error(apps: Apps, schema_editor: BaseDatabaseSchemaEdit
EventMatcherPolicy = apps.get_model( EventMatcherPolicy = apps.get_model(
"authentik_policies_event_matcher", "EventMatcherPolicy" "authentik_policies_event_matcher", "EventMatcherPolicy"
) )
NotificationTrigger = apps.get_model("authentik_events", "NotificationTrigger") NotificationRule = apps.get_model("authentik_events", "NotificationRule")
NotificationTransport = apps.get_model("authentik_events", "NotificationTransport") NotificationTransport = apps.get_model("authentik_events", "NotificationTransport")
admin_group = ( admin_group = (
@ -27,7 +27,7 @@ def notify_configuration_error(apps: Apps, schema_editor: BaseDatabaseSchemaEdit
name="default-match-configuration-error", name="default-match-configuration-error",
defaults={"action": EventAction.CONFIGURATION_ERROR}, defaults={"action": EventAction.CONFIGURATION_ERROR},
) )
trigger, _ = NotificationTrigger.objects.using(db_alias).update_or_create( trigger, _ = NotificationRule.objects.using(db_alias).update_or_create(
name="default-notify-configuration-error", name="default-notify-configuration-error",
defaults={"group": admin_group, "severity": NotificationSeverity.ALERT}, defaults={"group": admin_group, "severity": NotificationSeverity.ALERT},
) )
@ -53,7 +53,7 @@ def notify_update(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
EventMatcherPolicy = apps.get_model( EventMatcherPolicy = apps.get_model(
"authentik_policies_event_matcher", "EventMatcherPolicy" "authentik_policies_event_matcher", "EventMatcherPolicy"
) )
NotificationTrigger = apps.get_model("authentik_events", "NotificationTrigger") NotificationRule = apps.get_model("authentik_events", "NotificationRule")
NotificationTransport = apps.get_model("authentik_events", "NotificationTransport") NotificationTransport = apps.get_model("authentik_events", "NotificationTransport")
admin_group = ( admin_group = (
@ -66,7 +66,7 @@ def notify_update(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
name="default-match-update", name="default-match-update",
defaults={"action": EventAction.UPDATE_AVAILABLE}, defaults={"action": EventAction.UPDATE_AVAILABLE},
) )
trigger, _ = NotificationTrigger.objects.using(db_alias).update_or_create( trigger, _ = NotificationRule.objects.using(db_alias).update_or_create(
name="default-notify-update", name="default-notify-update",
defaults={"group": admin_group, "severity": NotificationSeverity.ALERT}, defaults={"group": admin_group, "severity": NotificationSeverity.ALERT},
) )
@ -92,7 +92,7 @@ def notify_exception(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
EventMatcherPolicy = apps.get_model( EventMatcherPolicy = apps.get_model(
"authentik_policies_event_matcher", "EventMatcherPolicy" "authentik_policies_event_matcher", "EventMatcherPolicy"
) )
NotificationTrigger = apps.get_model("authentik_events", "NotificationTrigger") NotificationRule = apps.get_model("authentik_events", "NotificationRule")
NotificationTransport = apps.get_model("authentik_events", "NotificationTransport") NotificationTransport = apps.get_model("authentik_events", "NotificationTransport")
admin_group = ( admin_group = (
@ -109,7 +109,7 @@ def notify_exception(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
name="default-match-property-mapping-exception", name="default-match-property-mapping-exception",
defaults={"action": EventAction.PROPERTY_MAPPING_EXCEPTION}, defaults={"action": EventAction.PROPERTY_MAPPING_EXCEPTION},
) )
trigger, _ = NotificationTrigger.objects.using(db_alias).update_or_create( trigger, _ = NotificationRule.objects.using(db_alias).update_or_create(
name="default-notify-exception", name="default-notify-exception",
defaults={"group": admin_group, "severity": NotificationSeverity.ALERT}, defaults={"group": admin_group, "severity": NotificationSeverity.ALERT},
) )
@ -150,7 +150,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
( (
"authentik_events", "authentik_events",
"0010_notification_notificationtransport_notificationtrigger", "0010_notification_notificationtransport_notificationrule",
), ),
("authentik_core", "0016_auto_20201202_2234"), ("authentik_core", "0016_auto_20201202_2234"),
("authentik_policies_event_matcher", "0003_auto_20210110_1907"), ("authentik_policies_event_matcher", "0003_auto_20210110_1907"),

View File

@ -174,7 +174,7 @@ class TransportMode(models.TextChoices):
class NotificationTransport(models.Model): class NotificationTransport(models.Model):
"""Action which is executed when a Trigger matches""" """Action which is executed when a Rule matches"""
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -323,7 +323,7 @@ class Notification(models.Model):
verbose_name_plural = _("Notifications") verbose_name_plural = _("Notifications")
class NotificationTrigger(PolicyBindingModel): class NotificationRule(PolicyBindingModel):
"""Decide when to create a Notification based on policies attached to this object.""" """Decide when to create a Notification based on policies attached to this object."""
name = models.TextField(unique=True) name = models.TextField(unique=True)
@ -357,9 +357,9 @@ class NotificationTrigger(PolicyBindingModel):
) )
def __str__(self) -> str: def __str__(self) -> str:
return f"Notification Trigger {self.name}" return f"Notification Rule {self.name}"
class Meta: class Meta:
verbose_name = _("Notification Trigger") verbose_name = _("Notification Rule")
verbose_name_plural = _("Notification Triggers") verbose_name_plural = _("Notification Rules")

View File

@ -5,9 +5,9 @@ from structlog import get_logger
from authentik.events.models import ( from authentik.events.models import (
Event, Event,
Notification, Notification,
NotificationRule,
NotificationTransport, NotificationTransport,
NotificationTransportError, NotificationTransportError,
NotificationTrigger,
) )
from authentik.lib.tasks import MonitoredTask, TaskResult, TaskResultStatus from authentik.lib.tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.policies.engine import PolicyEngine, PolicyEngineMode from authentik.policies.engine import PolicyEngine, PolicyEngineMode
@ -20,7 +20,7 @@ LOGGER = get_logger()
@CELERY_APP.task() @CELERY_APP.task()
def event_notification_handler(event_uuid: str): def event_notification_handler(event_uuid: str):
"""Start task for each trigger definition""" """Start task for each trigger definition"""
for trigger in NotificationTrigger.objects.all(): for trigger in NotificationRule.objects.all():
event_trigger_handler.apply_async( event_trigger_handler.apply_async(
args=[event_uuid, trigger.name], queue="authentik_events" args=[event_uuid, trigger.name], queue="authentik_events"
) )
@ -28,20 +28,20 @@ def event_notification_handler(event_uuid: str):
@CELERY_APP.task() @CELERY_APP.task()
def event_trigger_handler(event_uuid: str, trigger_name: str): def event_trigger_handler(event_uuid: str, trigger_name: str):
"""Check if policies attached to NotificationTrigger match event""" """Check if policies attached to NotificationRule match event"""
event: Event = Event.objects.get(event_uuid=event_uuid) event: Event = Event.objects.get(event_uuid=event_uuid)
trigger: NotificationTrigger = NotificationTrigger.objects.get(name=trigger_name) trigger: NotificationRule = NotificationRule.objects.get(name=trigger_name)
if "policy_uuid" in event.context: if "policy_uuid" in event.context:
policy_uuid = event.context["policy_uuid"] policy_uuid = event.context["policy_uuid"]
if PolicyBinding.objects.filter( if PolicyBinding.objects.filter(
target__in=NotificationTrigger.objects.all().values_list( target__in=NotificationRule.objects.all().values_list(
"pbm_uuid", flat=True "pbm_uuid", flat=True
), ),
policy=policy_uuid, policy=policy_uuid,
).exists(): ).exists():
# If policy that caused this event to be created is attached # If policy that caused this event to be created is attached
# to *any* NotificationTrigger, we return early. # to *any* NotificationRule, we return early.
# This is the most effective way to prevent infinite loops. # This is the most effective way to prevent infinite loops.
LOGGER.debug( LOGGER.debug(
"e(trigger): attempting to prevent infinite loop", trigger=trigger "e(trigger): attempting to prevent infinite loop", trigger=trigger

View File

@ -8,8 +8,8 @@ from authentik.core.models import Group, User
from authentik.events.models import ( from authentik.events.models import (
Event, Event,
EventAction, EventAction,
NotificationRule,
NotificationTransport, NotificationTransport,
NotificationTrigger,
) )
from authentik.policies.event_matcher.models import EventMatcherPolicy from authentik.policies.event_matcher.models import EventMatcherPolicy
from authentik.policies.exceptions import PolicyException from authentik.policies.exceptions import PolicyException
@ -28,7 +28,7 @@ class TestEventsNotifications(TestCase):
def test_trigger_empty(self): def test_trigger_empty(self):
"""Test trigger without any policies attached""" """Test trigger without any policies attached"""
transport = NotificationTransport.objects.create(name="transport") transport = NotificationTransport.objects.create(name="transport")
trigger = NotificationTrigger.objects.create(name="trigger", group=self.group) trigger = NotificationRule.objects.create(name="trigger", group=self.group)
trigger.transports.add(transport) trigger.transports.add(transport)
trigger.save() trigger.save()
@ -40,7 +40,7 @@ class TestEventsNotifications(TestCase):
def test_trigger_single(self): def test_trigger_single(self):
"""Test simple transport triggering""" """Test simple transport triggering"""
transport = NotificationTransport.objects.create(name="transport") transport = NotificationTransport.objects.create(name="transport")
trigger = NotificationTrigger.objects.create(name="trigger", group=self.group) trigger = NotificationRule.objects.create(name="trigger", group=self.group)
trigger.transports.add(transport) trigger.transports.add(transport)
trigger.save() trigger.save()
matcher = EventMatcherPolicy.objects.create( matcher = EventMatcherPolicy.objects.create(
@ -55,7 +55,7 @@ class TestEventsNotifications(TestCase):
def test_trigger_no_group(self): def test_trigger_no_group(self):
"""Test trigger without group""" """Test trigger without group"""
trigger = NotificationTrigger.objects.create(name="trigger") trigger = NotificationRule.objects.create(name="trigger")
matcher = EventMatcherPolicy.objects.create( matcher = EventMatcherPolicy.objects.create(
name="matcher", action=EventAction.CUSTOM_PREFIX name="matcher", action=EventAction.CUSTOM_PREFIX
) )
@ -69,8 +69,8 @@ class TestEventsNotifications(TestCase):
def test_policy_error_recursive(self): def test_policy_error_recursive(self):
"""Test Policy error which would cause recursion""" """Test Policy error which would cause recursion"""
transport = NotificationTransport.objects.create(name="transport") transport = NotificationTransport.objects.create(name="transport")
NotificationTrigger.objects.filter(name__startswith="default").delete() NotificationRule.objects.filter(name__startswith="default").delete()
trigger = NotificationTrigger.objects.create(name="trigger", group=self.group) trigger = NotificationRule.objects.create(name="trigger", group=self.group)
trigger.transports.add(transport) trigger.transports.add(transport)
trigger.save() trigger.save()
matcher = EventMatcherPolicy.objects.create( matcher = EventMatcherPolicy.objects.create(

View File

@ -1090,6 +1090,133 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/events/rules/:
get:
operationId: events_rules_list
description: NotificationRule Viewset
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
$ref: '#/definitions/NotificationRule'
tags:
- events
post:
operationId: events_rules_create
description: NotificationRule Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/NotificationRule'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/NotificationRule'
tags:
- events
parameters: []
/events/rules/{pbm_uuid}/:
get:
operationId: events_rules_read
description: NotificationRule Viewset
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/NotificationRule'
tags:
- events
put:
operationId: events_rules_update
description: NotificationRule Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/NotificationRule'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/NotificationRule'
tags:
- events
patch:
operationId: events_rules_partial_update
description: NotificationRule Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/NotificationRule'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/NotificationRule'
tags:
- events
delete:
operationId: events_rules_delete
description: NotificationRule Viewset
parameters: []
responses:
'204':
description: ''
tags:
- events
parameters:
- name: pbm_uuid
in: path
description: A UUID string identifying this Notification Rule.
required: true
type: string
format: uuid
/events/transports/: /events/transports/:
get: get:
operationId: events_transports_list operationId: events_transports_list
@ -1243,133 +1370,6 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/events/triggers/:
get:
operationId: events_triggers_list
description: NotificationTrigger Viewset
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
- name: page_size
in: query
description: Number of results to return per page.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
$ref: '#/definitions/NotificationTrigger'
tags:
- events
post:
operationId: events_triggers_create
description: NotificationTrigger Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/NotificationTrigger'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/NotificationTrigger'
tags:
- events
parameters: []
/events/triggers/{pbm_uuid}/:
get:
operationId: events_triggers_read
description: NotificationTrigger Viewset
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/NotificationTrigger'
tags:
- events
put:
operationId: events_triggers_update
description: NotificationTrigger Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/NotificationTrigger'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/NotificationTrigger'
tags:
- events
patch:
operationId: events_triggers_partial_update
description: NotificationTrigger Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/NotificationTrigger'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/NotificationTrigger'
tags:
- events
delete:
operationId: events_triggers_delete
description: NotificationTrigger Viewset
parameters: []
responses:
'204':
description: ''
tags:
- events
parameters:
- name: pbm_uuid
in: path
description: A UUID string identifying this Notification Trigger.
required: true
type: string
format: uuid
/flows/bindings/: /flows/bindings/:
get: get:
operationId: flows_bindings_list operationId: flows_bindings_list
@ -7630,38 +7630,8 @@ definitions:
seen: seen:
title: Seen title: Seen
type: boolean type: boolean
NotificationTransport: NotificationRule:
description: NotificationTransport Serializer description: NotificationRule Serializer
required:
- name
- mode
type: object
properties:
pk:
title: Uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
minLength: 1
mode:
title: Mode
type: string
enum:
- webhook
- webhook_slack
- email
mode_verbose:
title: Mode verbose
type: string
readOnly: true
webhook_url:
title: Webhook url
type: string
NotificationTrigger:
description: NotificationTrigger Serializer
required: required:
- name - name
type: object type: object
@ -7679,7 +7649,7 @@ definitions:
description: '' description: ''
type: array type: array
items: items:
description: Action which is executed when a Trigger matches description: Action which is executed when a Rule matches
required: required:
- name - name
- mode - mode
@ -7767,6 +7737,36 @@ definitions:
format: uuid format: uuid
readOnly: true readOnly: true
readOnly: true readOnly: true
NotificationTransport:
description: NotificationTransport Serializer
required:
- name
- mode
type: object
properties:
pk:
title: Uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
minLength: 1
mode:
title: Mode
type: string
enum:
- webhook
- webhook_slack
- email
mode_verbose:
title: Mode verbose
type: string
readOnly: true
webhook_url:
title: Webhook url
type: string
Stage: Stage:
title: Stage obj title: Stage obj
description: Stage Serializer description: Stage Serializer

26
web/src/api/EventRules.ts Normal file
View File

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

View File

@ -1,26 +0,0 @@
import { DefaultClient, QueryArguments, PBResponse } from "./Client";
import { Group } from "./Groups";
export class Trigger {
pk: string;
name: string;
transports: string[];
severity: string;
group?: Group;
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

@ -1,7 +1,7 @@
import { customElement, html, LitElement, TemplateResult } from "lit-element"; import { customElement, html, LitElement, TemplateResult } from "lit-element";
@customElement("ak-notification-trigger") @customElement("ak-notification-trigger")
export class NotificationTrigger extends LitElement { export class NotificationRule extends LitElement {
constructor() { constructor() {
super(); super();

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import "./pages/sources/SourceViewPage";
import "./pages/flows/FlowViewPage"; import "./pages/flows/FlowViewPage";
import "./pages/events/EventListPage"; import "./pages/events/EventListPage";
import "./pages/events/TransportListPage"; import "./pages/events/TransportListPage";
import "./pages/events/TriggerListPage"; import "./pages/events/RuleListPage";
export const ROUTES: Route[] = [ export const ROUTES: Route[] = [
// Prevent infinite Shell loops // Prevent infinite Shell loops
@ -29,5 +29,5 @@ export const ROUTES: Route[] = [
}), }),
new Route(new RegExp("^/events/log$"), html`<ak-event-list></ak-event-list>`), 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/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>`), new Route(new RegExp("^/events/rules$"), html`<ak-event-rule-list></ak-event-rule-list>`),
]; ];

View File

@ -3,18 +3,18 @@ title: Notifications
--- ---
:::note :::note
To prevent infinite loops (events created by policies which are attached to a Notification rule), **any events created by a policy which is attached to any Notification Trigger do not trigger notifications.** To prevent infinite loops (events created by policies which are attached to a Notification rule), **any events created by a policy which is attached to any Notification Rules do not trigger notifications.**
::: :::
## Filtering Events ## Filtering Events
Starting with authentik 0.15, you can create notification triggers, which can alert you based on the creation of certain events. Starting with authentik 0.15, you can create notification rules, which can alert you based on the creation of certain events.
Filtering is done by using the Policy Engine. You can do simple filtering using the "Event Matcher Policy" type. Filtering is done by using the Policy Engine. You can do simple filtering using the "Event Matcher Policy" type.
![](./event_matcher.png) ![](./event_matcher.png)
An event has to match all configured fields, otherwise the trigger will not activate. An event has to match all configured fields, otherwise the rule will not trigger.
To match events with an "Expression Policy", you can write code like so: To match events with an "Expression Policy", you can write code like so:
@ -27,9 +27,9 @@ return ip_address(request.context["evnet"].client_ip) in ip_network('192.0.2.0/2
## Selecting who gets notified ## Selecting who gets notified
After you've created the policies to match the events you want, create a "Notification Trigger". After you've created the policies to match the events you want, create a "Notification Rule".
You have to select which group the generated notification should be sent to. If left empty, the trigger will be disabled. You have to select which group the generated notification should be sent to. If left empty, the rule will be disabled.
You also have to select which transports should be used to send the notification. You also have to select which transports should be used to send the notification.
A transport with the name "default-email-transport" is created by default. This transport will use the [global email configuration](../installation/docker-compose#email-configuration-optional-but-recommended). A transport with the name "default-email-transport" is created by default. This transport will use the [global email configuration](../installation/docker-compose#email-configuration-optional-but-recommended).