Make throttling more strict

This commit is contained in:
Spitap
2023-01-27 22:03:09 +01:00
parent bde07163b0
commit cbfac68b97
13 changed files with 49 additions and 42 deletions
+7
View File
@@ -11,11 +11,13 @@ from rest_framework.decorators import action
from rest_framework.exceptions import ParseError
from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from modoboa.core import models as core_models
from modoboa.core import sms_backends
from modoboa.lib import renderers as lib_renderers
from modoboa.lib import viewsets as lib_viewsets
from modoboa.lib.throttle import UserDdosPerView
from ... import lib, models
from . import serializers
@@ -40,6 +42,7 @@ class DomainViewSet(lib_viewsets.RevisionModelMixin, viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
serializer_class = serializers.DomainSerializer
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
@@ -69,6 +72,7 @@ class DomainAliasViewSet(lib_viewsets.RevisionModelMixin,
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
renderer_classes = (renderers.JSONRenderer, lib_renderers.CSVRenderer)
serializer_class = serializers.DomainAliasSerializer
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
@@ -86,6 +90,7 @@ class AccountViewSet(lib_viewsets.RevisionModelMixin, viewsets.ModelViewSet):
filter_backends = (filters.SearchFilter, )
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
search_fields = ("^first_name", "^last_name", "^email")
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_serializer_class(self):
"""Return a serializer."""
@@ -183,6 +188,7 @@ class AliasViewSet(lib_viewsets.RevisionModelMixin, viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
serializer_class = serializers.AliasSerializer
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
@@ -215,6 +221,7 @@ class SenderAddressViewSet(lib_viewsets.RevisionModelMixin,
filterset_class = SenderAddressFilterSet
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
serializer_class = serializers.SenderAddressSerializer
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
+7 -7
View File
@@ -11,12 +11,13 @@ from rest_framework import (
)
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.throttling import UserRateThrottle
from modoboa.admin.api.v1 import viewsets as v1_viewsets
from modoboa.core import models as core_models
from modoboa.lib import renderers as lib_renderers
from modoboa.lib import viewsets as lib_viewsets
from modoboa.lib.throttle import UserDosThrottleViewset
from modoboa.lib.throttle import UserDdosPerView
from ... import lib
from ... import models
@@ -52,7 +53,7 @@ class DomainViewSet(lib_viewsets.RevisionModelMixin,
permission_classes = (
permissions.IsAuthenticated, permissions.DjangoModelPermissions,
)
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
@@ -161,7 +162,6 @@ class AccountViewSet(v1_viewsets.AccountViewSet):
filter_backends = (filters.SearchFilter, dj_filters.DjangoFilterBackend)
filterset_class = AccountFilterSet
throttle_classes = [UserDosThrottleViewset]
def get_serializer_class(self):
if self.action in ["create", "validate", "update", "partial_update"]:
@@ -216,7 +216,7 @@ class IdentityViewSet(viewsets.ViewSet):
permission_classes = (permissions.IsAuthenticated, )
serializer_class = None
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
def list(self, request, **kwargs):
"""Return all identities."""
@@ -256,7 +256,7 @@ class AliasViewSet(v1_viewsets.AliasViewSet):
"""Viewset for Alias."""
serializer_class = serializers.AliasSerializer
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
@action(methods=["post"], detail=False)
def validate(self, request, **kwargs):
@@ -278,7 +278,7 @@ class AliasViewSet(v1_viewsets.AliasViewSet):
class UserAccountViewSet(viewsets.ViewSet):
"""Viewset for current user operations."""
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
@action(methods=["get", "post"], detail=False)
def forward(self, request, **kwargs):
@@ -341,7 +341,7 @@ class AlarmViewSet(viewsets.ReadOnlyModelViewSet):
)
search_fields = ["domain__name", "title"]
serializer_class = serializers.AlarmSerializer
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
return models.Alarm.objects.select_related("domain").filter(
+6
View File
@@ -5,10 +5,15 @@ from django.utils.translation import ugettext as _
import django_otp
from django_otp.plugins.otp_static.models import StaticDevice, StaticToken
from django_otp.plugins.otp_totp.models import TOTPDevice
from rest_framework import permissions, response, viewsets
from rest_framework.decorators import action
from rest_framework.throttling import UserRateThrottle
from drf_spectacular.utils import extend_schema
from modoboa.lib.throttle import UserDdosPerView
from . import serializers
@@ -20,6 +25,7 @@ class AccountViewSet(viewsets.ViewSet):
permission_classes = (permissions.IsAuthenticated, )
serializer_class = None
throttle_classes = [UserDdosPerView, UserRateThrottle]
@action(methods=["post"], detail=False, url_path="tfa/setup")
def tfa_setup(self, request):
+4 -5
View File
@@ -12,10 +12,9 @@ from drf_spectacular.utils import extend_schema
from rest_framework import response, status
from rest_framework_simplejwt import views as jwt_views
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
from rest_framework.throttling import ScopedRateThrottle
from rest_framework.throttling import ScopedRateThrottle, UserRateThrottle
from rest_framework.views import APIView
from modoboa.lib.throttle import UserDosThrottleView
from modoboa.core.password_hashers import get_password_hasher
from modoboa.core.utils import check_for_updates
from modoboa.parameters import tools as param_tools
@@ -29,7 +28,7 @@ logger = logging.getLogger("modoboa.auth")
class TokenObtainPairView(jwt_views.TokenObtainPairView):
"""We overwrite this view to deal with password scheme update."""
throttle_classes = [ScopedRateThrottle]
throttle_scope = "login"
@@ -89,7 +88,7 @@ class EmailPasswordResetView(APIView):
"""
An Api View which provides a method to request a password reset token based on an e-mail address.
"""
throttle_classes = [ScopedRateThrottle]
throttle_scope = 'password_recovery_request'
@@ -179,7 +178,7 @@ class PasswordResetConfirmView(APIView):
class ComponentsInformationAPIView(APIView):
"""Retrieve information about installed components."""
throttle_classes = [UserDosThrottleView]
throttle_classes = [UserRateThrottle]
@extend_schema(responses=serializers.ModoboaComponentSerializer(many=True))
def get(self, request, *args, **kwargs):
+2
View File
@@ -11,6 +11,7 @@ from rest_framework import filters, permissions, response, viewsets
from rest_framework.authtoken.models import Token
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.throttling import UserRateThrottle
from rest_framework_simplejwt.tokens import RefreshToken
from modoboa.admin.api.v1 import serializers as admin_v1_serializers
@@ -162,6 +163,7 @@ class LogViewSet(viewsets.ReadOnlyModelViewSet):
queryset = models.Log.objects.all()
search_fields = ["logger", "level", "message"]
serializer_class = serializers.LogSerializer
throttle_classes = [UserRateThrottle]
class LanguageViewSet(viewsets.ViewSet):
@@ -194,9 +194,8 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'anon': '100/hour',
'login': '3/minute',
'ddos': '100/minute',
'user': '100/minute',
'ddos': '1/second',
'password_recovery_request': '6/hour',
'password_recovery_totp_check': '20/hour',
'password_recovery_apply': '20/hour'
+3 -2
View File
@@ -2,9 +2,10 @@
from rest_framework import permissions, response, viewsets
from rest_framework.decorators import action
from rest_framework.throttling import UserRateThrottle
from modoboa.admin import models as admin_models
from modoboa.lib.throttle import UserDosThrottleViewset
from modoboa.lib.throttle import UserDdosPerView
from . import serializers
@@ -13,7 +14,7 @@ class DNSViewSet(viewsets.GenericViewSet):
"""A viewset to provide extra routes related to DNS information."""
permission_classes = (permissions.IsAuthenticated, )
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
+2 -18
View File
@@ -1,9 +1,9 @@
from rest_framework.throttling import SimpleRateThrottle
class UserDosThrottleViewset(SimpleRateThrottle):
class UserDdosPerView(SimpleRateThrottle):
"""Custom Throttle class for rest_framework. The throttling is applied on a per view basis for authentificated users."""
scope = "ddos"
scope = 'ddos'
def get_cache_key(self, request, view):
if request.user and request.user.is_authenticated:
@@ -15,19 +15,3 @@ class UserDosThrottleViewset(SimpleRateThrottle):
'scope': hash(f"{view.basename}_{view.name}"),
'ident': ident
}
class UserDosThrottleView(SimpleRateThrottle):
"""Custom Throttle class for rest_framework. The throttling is applied on a per view basis for authentificated users."""
scope = "ddos"
def get_cache_key(self, request, view):
if request.user and request.user.is_authenticated:
ident = request.user.pk
else:
ident = self.get_ident(request)
return self.cache_format % {
'scope': id(view),
'ident': ident
}
+3
View File
@@ -4,8 +4,10 @@ from django.contrib.contenttypes.models import ContentType
from rest_framework import mixins, viewsets
from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated
from rest_framework.throttling import UserRateThrottle
from modoboa.core import models as core_models
from modoboa.lib.throttle import UserDdosPerView
from . import serializers
@@ -17,6 +19,7 @@ class ResourcesViewSet(
permission_classes = [IsAuthenticated, DjangoModelPermissions]
serializer_class = serializers.ResourcesSerializer
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
+4 -3
View File
@@ -6,10 +6,11 @@ from django.db.models import Q
from drf_spectacular.utils import extend_schema
from rest_framework import filters, permissions, response, viewsets
from rest_framework.throttling import UserRateThrottle
from modoboa.admin import models as admin_models
from modoboa.lib import pagination
from modoboa.lib.throttle import UserDosThrottleViewset
from modoboa.lib.throttle import UserDdosPerView
from ... import models
from ... import signals
@@ -20,7 +21,7 @@ class StatisticsViewSet(viewsets.ViewSet):
"""A viewset to provide extra route related to mail statistics."""
permission_classes = (permissions.IsAuthenticated, )
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
@extend_schema(
parameters=[serializers.StatisticsInputSerializer],
@@ -63,7 +64,7 @@ class MaillogViewSet(viewsets.ReadOnlyModelViewSet):
permissions = (permissions.IsAuthenticated, )
search_fields = ["queue_id", "sender", "rcpt", "original_rcpt", "status"]
serializer_class = serializers.MaillogSerializer
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
+3 -2
View File
@@ -3,8 +3,9 @@
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework import response, viewsets
from rest_framework.decorators import action
from rest_framework.throttling import UserRateThrottle
from modoboa.lib.throttle import UserDosThrottleViewset
from modoboa.lib.throttle import UserDdosPerView
from . import serializers
from ... import tools
@@ -15,7 +16,7 @@ class ParametersViewSet(viewsets.ViewSet):
lookup_value_regex = r"\w+"
serializer_class = None
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserRateThrottle]
@extend_schema(responses=serializers.ApplicationSerializer(many=True))
@action(methods=["get"], detail=False)
+3
View File
@@ -2,8 +2,10 @@
from rest_framework import viewsets
from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated
from rest_framework.throttling import UserRateThrottle
from modoboa.admin import models as admin_models
from modoboa.lib.throttle import UserDdosPerView
from modoboa.lib.viewsets import RevisionModelMixin
from . import serializers
@@ -13,6 +15,7 @@ class RelayDomainViewSet(RevisionModelMixin, viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
serializer_class = serializers.RelayDomainSerializer
throttle_classes = [UserDdosPerView, UserRateThrottle]
def get_queryset(self):
"""Filter queryset based on current user."""
+3 -2
View File
@@ -2,8 +2,9 @@
from drf_spectacular.utils import extend_schema
from rest_framework import permissions, response, viewsets
from rest_framework.throttling import UserRateThrottle
from modoboa.lib.throttle import UserDosThrottleViewset
from modoboa.lib.throttle import UserDdosPerView
from . import serializers
from ... import backends
@@ -13,7 +14,7 @@ class TransportViewSet(viewsets.ViewSet):
"""Viewset for Transport."""
permissions = (permissions.IsAuthenticated, )
throttle_classes = [UserDosThrottleViewset]
throttle_classes = [UserDdosPerView, UserDdosPerView]
@extend_schema(
responses={200: serializers.TransportBackendSerializer}