mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-20 06:08:35 +02:00
Merge pull request #2884 from modoboa/better-dkim
Added an alarm on dkim write error, reworked alarms
This commit is contained in:
@@ -226,6 +226,10 @@ class AdminGlobalParametersSerializer(serializers.Serializer):
|
||||
raise serializers.ValidationError(
|
||||
_("Directory not found.")
|
||||
)
|
||||
if not os.access(value, os.W_OK):
|
||||
raise serializers.ValidationError(
|
||||
_("Directory non-writable")
|
||||
)
|
||||
code, output = exec_cmd("which openssl")
|
||||
if code:
|
||||
raise serializers.ValidationError(
|
||||
@@ -621,3 +625,14 @@ class AlarmSerializer(serializers.ModelSerializer):
|
||||
depth = 1
|
||||
fields = "__all__"
|
||||
model = models.Alarm
|
||||
|
||||
|
||||
class AlarmSwitchStatusSerializer(serializers.Serializer):
|
||||
"""Serializer to switch the status of an Alarm."""
|
||||
|
||||
status = serializers.IntegerField()
|
||||
|
||||
def validate(self, data):
|
||||
if data["status"] not in [constants.ALARM_OPENED, constants.ALARM_CLOSED]:
|
||||
raise ValidationError(_("Unknown status"))
|
||||
return data
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.utils.encoding import force_text
|
||||
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from modoboa.admin import factories, models
|
||||
from modoboa.admin import factories, models, constants
|
||||
from modoboa.core import models as core_models
|
||||
from modoboa.lib.tests import ModoAPITestCase
|
||||
|
||||
@@ -479,9 +479,84 @@ class AlarmViewSetTestCase(ModoAPITestCase):
|
||||
factories.populate_database()
|
||||
factories.AlarmFactory(
|
||||
domain__name="test.com", mailbox=None, title="Test alarm")
|
||||
cls.da_token = Token.objects.create(
|
||||
user=core_models.User.objects.get(username="admin@test.com"))
|
||||
|
||||
def test_list(self):
|
||||
url = reverse("v2:alarm-list")
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(len(resp.json()), 1)
|
||||
|
||||
def test_update_alarm(self):
|
||||
"""Try updating alarm status and delete it afterward."""
|
||||
|
||||
domain = models.Domain.objects.get(name="test.com")
|
||||
|
||||
# Try performing action on restricted domains
|
||||
self.client.credentials(
|
||||
HTTP_AUTHORIZATION="Token " + self.da_token.key)
|
||||
domain = models.Domain.objects.get(name="test2.com")
|
||||
alarm_restricted = models.Alarm.objects.create(
|
||||
domain=domain, mailbox=None, title="Test alarm 2")
|
||||
alarm_restricted.save()
|
||||
url = reverse("v2:alarm-switch", args=[alarm_restricted.pk])
|
||||
resp = self.client.post(url)
|
||||
self.assertEqual(resp.status_code, 405)
|
||||
url = reverse("v2:alarm-detail", args=[alarm_restricted.pk])
|
||||
resp = self.client.delete(url)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
# Perform actions as SuperAdmin
|
||||
self.client.credentials(HTTP_AUTHORIZATION="Token " + self.token.key)
|
||||
|
||||
alarm = models.Alarm.objects.create(
|
||||
domain=domain, mailbox=None, title="Test alarm 3")
|
||||
alarm.save()
|
||||
|
||||
# Switch status of the alarm to close
|
||||
url = reverse("v2:alarm-switch", args=[alarm.pk])
|
||||
resp = self.client.patch(url, {"status": constants.ALARM_CLOSED})
|
||||
self.assertEqual(resp.status_code, 204)
|
||||
|
||||
# Check actual status
|
||||
url = reverse("v2:alarm-detail", args=[alarm.pk])
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.json()["status"], constants.ALARM_CLOSED)
|
||||
|
||||
# Switch status back to open
|
||||
url = reverse("v2:alarm-switch", args=[alarm.pk])
|
||||
resp = self.client.patch(url, {"status": constants.ALARM_OPENED})
|
||||
self.assertEqual(resp.status_code, 204)
|
||||
|
||||
# Check actual status
|
||||
url = reverse("v2:alarm-detail", args=[alarm.pk])
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.json()["status"], constants.ALARM_OPENED)
|
||||
|
||||
# Try to set an non-existant status
|
||||
url = reverse("v2:alarm-switch", args=[alarm.pk])
|
||||
resp = self.client.patch(url, {"status": 10})
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
# Delete the alarm
|
||||
url = reverse("v2:alarm-detail", args=[alarm.pk])
|
||||
resp = self.client.delete(url)
|
||||
self.assertEqual(resp.status_code, 204)
|
||||
|
||||
def test_bulk_delete(self):
|
||||
url = reverse("v2:alarm-bulk-delete")
|
||||
resp = self.client.delete(url)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
resp = self.client.delete(f"{url}?ids[]=toto")
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
alarm1 = factories.AlarmFactory(
|
||||
domain__name="test.com", mailbox=None, title="Test alarm")
|
||||
alarm2 = factories.AlarmFactory(
|
||||
domain__name="test.com", mailbox=None, title="Test alarm")
|
||||
resp = self.client.delete(f"{url}?ids[]={alarm1.pk}&ids[]={alarm2.pk}")
|
||||
self.assertEqual(resp.status_code, 204)
|
||||
with self.assertRaises(models.Alarm.DoesNotExist):
|
||||
alarm1.refresh_from_db()
|
||||
with self.assertRaises(models.Alarm.DoesNotExist):
|
||||
alarm2.refresh_from_db()
|
||||
|
||||
@@ -20,6 +20,7 @@ from modoboa.lib.throttle import GetThrottleViewsetMixin
|
||||
|
||||
from ... import lib
|
||||
from ... import models
|
||||
from ... import constants
|
||||
from . import serializers
|
||||
|
||||
|
||||
@@ -325,10 +326,30 @@ class UserAccountViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
|
||||
return response.Response(serializer.validated_data)
|
||||
|
||||
|
||||
class AlarmViewSet(GetThrottleViewsetMixin, viewsets.ReadOnlyModelViewSet):
|
||||
class AlarmFilterSet(dj_filters.FilterSet):
|
||||
"""Custom FilterSet for Alarms."""
|
||||
|
||||
min_date = dj_filters.DateTimeFilter(method="filter_date")
|
||||
|
||||
class Meta:
|
||||
model = models.Alarm
|
||||
fields = ["created"]
|
||||
|
||||
def filter_date(self, queryset, name, value):
|
||||
return queryset.filter(created__gt=value)
|
||||
|
||||
|
||||
class AlarmViewSet(GetThrottleViewsetMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
"""Viewset for Alarm."""
|
||||
|
||||
filter_backends = (filters.OrderingFilter, filters.SearchFilter, )
|
||||
filter_backends = (filters.OrderingFilter,
|
||||
filters.SearchFilter,
|
||||
dj_filters.DjangoFilterBackend)
|
||||
filterset_class = AlarmFilterSet
|
||||
ordering_fields = ["created", "status", "title"]
|
||||
pagination_class = pagination.PageNumberPagination
|
||||
permission_classes = (
|
||||
@@ -342,3 +363,29 @@ class AlarmViewSet(GetThrottleViewsetMixin, viewsets.ReadOnlyModelViewSet):
|
||||
domain__in=models.Domain.objects.get_for_admin(
|
||||
self.request.user)
|
||||
).order_by("-created")
|
||||
|
||||
@action(methods=['patch'], detail=True)
|
||||
def switch(self, request, **kwargs):
|
||||
"""Custom update method that switch status of an alarm."""
|
||||
alarm = self.get_object()
|
||||
serializer = serializers.AlarmSwitchStatusSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
if serializer.data["status"] == constants.ALARM_CLOSED:
|
||||
alarm.close()
|
||||
else:
|
||||
alarm.reopen()
|
||||
return response.Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@action(methods=['delete'], detail=False)
|
||||
def bulk_delete(self, request, **kwargs):
|
||||
"""Delete multiple alarms at the same time."""
|
||||
ids = request.query_params.getlist("ids[]")
|
||||
if not ids:
|
||||
return response.Response(_("No alarm ID provided"), status=400)
|
||||
try:
|
||||
ids = [int(alarm_id) for alarm_id in ids]
|
||||
except ValueError:
|
||||
return response.Response(
|
||||
_("Received invalid alarm id(s)"), status=400)
|
||||
models.Alarm.objects.filter(pk__in=ids).delete()
|
||||
return response.Response(status=204)
|
||||
|
||||
Reference in New Issue
Block a user