diff --git a/authentik/events/api/notification_transport.py b/authentik/events/api/notification_transport.py index 25d48f3dc..26d56a12a 100644 --- a/authentik/events/api/notification_transport.py +++ b/authentik/events/api/notification_transport.py @@ -1,7 +1,10 @@ """NotificationTransport API Views""" +from typing import Any + from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiResponse, extend_schema from rest_framework.decorators import action +from rest_framework.exceptions import ValidationError from rest_framework.fields import CharField, ListField, SerializerMethodField from rest_framework.request import Request from rest_framework.response import Response @@ -29,6 +32,14 @@ class NotificationTransportSerializer(ModelSerializer): """Return selected mode with a UI Label""" return TransportMode(instance.mode).label + def validate(self, attrs: dict[Any, str]) -> dict[Any, str]: + """Ensure the required fields are set.""" + mode = attrs.get("mode") + if mode in [TransportMode.WEBHOOK, TransportMode.WEBHOOK_SLACK]: + if "webhook_url" not in attrs or attrs.get("webhook_url", "") == "": + raise ValidationError("Webhook URL may not be empty.") + return attrs + class Meta: model = NotificationTransport diff --git a/authentik/events/models.py b/authentik/events/models.py index ec2d4d63a..af5022b99 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Optional, Type, Union from uuid import uuid4 from django.conf import settings +from django.core.validators import URLValidator from django.db import models from django.http import HttpRequest from django.http.request import QueryDict @@ -223,7 +224,7 @@ class NotificationTransport(models.Model): name = models.TextField(unique=True) mode = models.TextField(choices=TransportMode.choices) - webhook_url = models.TextField(blank=True) + webhook_url = models.TextField(blank=True, validators=[URLValidator()]) webhook_mapping = models.ForeignKey( "NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None ) diff --git a/authentik/events/tests/test_api.py b/authentik/events/tests/test_api.py index be81c2462..5d9074bbd 100644 --- a/authentik/events/tests/test_api.py +++ b/authentik/events/tests/test_api.py @@ -4,7 +4,13 @@ from django.urls import reverse from rest_framework.test import APITestCase from authentik.core.models import User -from authentik.events.models import Event, EventAction, Notification, NotificationSeverity +from authentik.events.models import ( + Event, + EventAction, + Notification, + NotificationSeverity, + TransportMode, +) class TestEventsAPI(APITestCase): @@ -41,3 +47,23 @@ class TestEventsAPI(APITestCase): ) notification.refresh_from_db() self.assertTrue(notification.seen) + + def test_transport(self): + """Test transport API""" + response = self.client.post( + reverse("authentik_api:notificationtransport-list"), + data={ + "name": "foo-with", + "mode": TransportMode.WEBHOOK, + "webhook_url": "http://foo.com", + }, + ) + self.assertEqual(response.status_code, 201) + response = self.client.post( + reverse("authentik_api:notificationtransport-list"), + data={ + "name": "foo-without", + "mode": TransportMode.WEBHOOK, + }, + ) + self.assertEqual(response.status_code, 400)