events: catch errors during send and re-raise as custom type

This commit is contained in:
Jens Langhammer 2021-01-12 21:48:16 +01:00
parent c727c845df
commit f30bdbecd6
2 changed files with 36 additions and 14 deletions

View File

@ -1,6 +1,6 @@
"""authentik events models""" """authentik events models"""
from inspect import getmodule, stack from inspect import getmodule, stack
from smtplib import SMTPException
from typing import Optional, Union from typing import Optional, Union
from uuid import uuid4 from uuid import uuid4
@ -9,7 +9,7 @@ from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from requests import post from requests import RequestException, post
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik import __version__ from authentik import __version__
@ -19,6 +19,7 @@ from authentik.core.middleware import (
) )
from authentik.core.models import Group, User from authentik.core.models import Group, User
from authentik.events.utils import cleanse_dict, get_user, sanitize_dict from authentik.events.utils import cleanse_dict, get_user, sanitize_dict
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import get_client_ip from authentik.lib.utils.http import get_client_ip
from authentik.policies.models import PolicyBindingModel from authentik.policies.models import PolicyBindingModel
from authentik.stages.email.tasks import send_mail from authentik.stages.email.tasks import send_mail
@ -27,6 +28,10 @@ from authentik.stages.email.utils import TemplateEmailMessage
LOGGER = get_logger("authentik.events") LOGGER = get_logger("authentik.events")
class NotificationTransportError(SentryIgnoredException):
"""Error raised when a notification fails to be delivered"""
class EventAction(models.TextChoices): class EventAction(models.TextChoices):
"""All possible actions to save into the events log""" """All possible actions to save into the events log"""
@ -192,13 +197,17 @@ class NotificationTransport(models.Model):
def send_webhook(self, notification: "Notification") -> list[str]: def send_webhook(self, notification: "Notification") -> list[str]:
"""Send notification to generic webhook""" """Send notification to generic webhook"""
response = post( try:
self.webhook_url, response = post(
json={ self.webhook_url,
"body": notification.body, json={
"severity": notification.severity, "body": notification.body,
}, "severity": notification.severity,
) },
)
response.raise_for_status()
except RequestException as exc:
raise NotificationTransportError from exc
return [ return [
response.status_code, response.status_code,
response.text, response.text,
@ -235,7 +244,11 @@ class NotificationTransport(models.Model):
if notification.event: if notification.event:
body["attachments"][0]["title"] = notification.event.action body["attachments"][0]["title"] = notification.event.action
body["attachments"][0]["text"] = notification.event.action body["attachments"][0]["text"] = notification.event.action
response = post(self.webhook_url, json=body) try:
response = post(self.webhook_url, json=body)
response.raise_for_status()
except RequestException as exc:
raise NotificationTransportError from exc
return [ return [
response.status_code, response.status_code,
response.text, response.text,
@ -257,8 +270,11 @@ class NotificationTransport(models.Model):
}, },
) )
# Email is sent directly here, as the call to send() should have been from a task. # Email is sent directly here, as the call to send() should have been from a task.
# pyright: reportGeneralTypeIssues=false try:
return send_mail(mail.__dict__) # pylint: disable=no-value-for-parameter # pyright: reportGeneralTypeIssues=false
return send_mail(mail.__dict__) # pylint: disable=no-value-for-parameter
except (SMTPException, ConnectionError) as exc:
raise NotificationTransportError from exc
class Meta: class Meta:

View File

@ -6,6 +6,7 @@ from authentik.events.models import (
Event, Event,
Notification, Notification,
NotificationTransport, NotificationTransport,
NotificationTransportError,
NotificationTrigger, NotificationTrigger,
) )
from authentik.lib.tasks import MonitoredTask, TaskResult, TaskResultStatus from authentik.lib.tasks import MonitoredTask, TaskResult, TaskResultStatus
@ -64,7 +65,12 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
) )
@CELERY_APP.task(bind=True, base=MonitoredTask) @CELERY_APP.task(
bind=True,
autoretry_for=(NotificationTransportError),
retry_backoff=True,
base=MonitoredTask,
)
def notification_transport( def notification_transport(
self: MonitoredTask, notification_pk: int, transport_pk: int self: MonitoredTask, notification_pk: int, transport_pk: int
): ):
@ -77,6 +83,6 @@ def notification_transport(
) )
transport.send(notification) transport.send(notification)
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL)) self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))
except Exception as exc: except NotificationTransportError as exc:
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc)) self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
raise exc raise exc