mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-03 08:58:47 +01:00 
			
		
		
		
	Parse input of search filters to prevent errors based on invalid regex, fixes #113
Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
		@@ -1,9 +1,10 @@
 | 
			
		||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from api.viewsets import ReadProtectedModelViewSet
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import SearchFilter
 | 
			
		||||
 | 
			
		||||
from api.filters import RegexSafeSearchFilter
 | 
			
		||||
from api.viewsets import ReadProtectedModelViewSet
 | 
			
		||||
 | 
			
		||||
from .serializers import ActivitySerializer, ActivityTypeSerializer, EntrySerializer, GuestSerializer
 | 
			
		||||
from ..models import Activity, ActivityType, Entry, Guest
 | 
			
		||||
@@ -29,7 +30,7 @@ class ActivityViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Activity.objects.order_by('id')
 | 
			
		||||
    serializer_class = ActivitySerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'description', 'activity_type', 'location', 'creater', 'organizer', 'attendees_club',
 | 
			
		||||
                        'date_start', 'date_end', 'valid', 'open', ]
 | 
			
		||||
    search_fields = ['$name', '$description', '$location', '$creater__last_name', '$creater__first_name',
 | 
			
		||||
@@ -47,7 +48,7 @@ class GuestViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Guest.objects.order_by('id')
 | 
			
		||||
    serializer_class = GuestSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['activity', 'activity__name', 'last_name', 'first_name', 'inviter', 'inviter__alias__name',
 | 
			
		||||
                        'inviter__alias__normalized_name', ]
 | 
			
		||||
    search_fields = ['$activity__name', '$last_name', '$first_name', '$inviter__user__email', '$inviter__alias__name',
 | 
			
		||||
@@ -62,7 +63,7 @@ class EntryViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Entry.objects.order_by('id')
 | 
			
		||||
    serializer_class = EntrySerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['activity', 'time', 'note', 'guest', ]
 | 
			
		||||
    search_fields = ['$activity__name', '$note__user__email', '$note__alias__name', '$note__alias__normalized_name',
 | 
			
		||||
                     '$guest__last_name', '$guest__first_name', ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								apps/api/filters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								apps/api/filters.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
import re
 | 
			
		||||
from functools import lru_cache
 | 
			
		||||
 | 
			
		||||
from rest_framework.filters import SearchFilter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RegexSafeSearchFilter(SearchFilter):
 | 
			
		||||
    @lru_cache
 | 
			
		||||
    def validate_regex(self, search_term) -> bool:
 | 
			
		||||
        try:
 | 
			
		||||
            re.compile(search_term)
 | 
			
		||||
            return True
 | 
			
		||||
        except re.error:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def get_search_fields(self, view, request):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure that given regex are valid.
 | 
			
		||||
        If not, we consider that the user is trying to search by substring.
 | 
			
		||||
        """
 | 
			
		||||
        search_fields = super().get_search_fields(view, request)
 | 
			
		||||
        search_terms = self.get_search_terms(request)
 | 
			
		||||
 | 
			
		||||
        for search_term in search_terms:
 | 
			
		||||
            if not self.validate_regex(search_term):
 | 
			
		||||
                # Invalid regex. We assume we don't query by regex but by substring.
 | 
			
		||||
                search_fields = [f.replace('$', '') for f in search_fields]
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
        return search_fields
 | 
			
		||||
 | 
			
		||||
    def get_search_terms(self, request):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure that search field is a valid regex query. If not, we remove extra characters.
 | 
			
		||||
        """
 | 
			
		||||
        terms = super().get_search_terms(request)
 | 
			
		||||
        if not all(self.validate_regex(term) for term in terms):
 | 
			
		||||
            # Invalid regex. If a ^ is prefixed to the search term, we remove it.
 | 
			
		||||
            terms = [term[1:] if term[0] == '^' else term for term in terms]
 | 
			
		||||
            # Same for dollars.
 | 
			
		||||
            terms = [term[:-1] if term[-1] == '$' else term for term in terms]
 | 
			
		||||
        return terms
 | 
			
		||||
@@ -12,11 +12,13 @@ from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
from django.db.models.fields.files import ImageFieldFile
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from phonenumbers import PhoneNumber
 | 
			
		||||
from rest_framework.filters import OrderingFilter
 | 
			
		||||
 | 
			
		||||
from api.filters import RegexSafeSearchFilter
 | 
			
		||||
from member.models import Membership, Club
 | 
			
		||||
from note.models import NoteClub, NoteUser, Alias, Note
 | 
			
		||||
from permission.models import PermissionMask, Permission, Role
 | 
			
		||||
from phonenumbers import PhoneNumber
 | 
			
		||||
from rest_framework.filters import SearchFilter, OrderingFilter
 | 
			
		||||
 | 
			
		||||
from .viewsets import ContentTypeViewSet, UserViewSet
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +89,7 @@ class TestAPI(TestCase):
 | 
			
		||||
                    resp = self.client.get(url + f"?ordering=-{field}")
 | 
			
		||||
                    self.assertEqual(resp.status_code, 200)
 | 
			
		||||
 | 
			
		||||
            if SearchFilter in backends:
 | 
			
		||||
            if RegexSafeSearchFilter in backends:
 | 
			
		||||
                # Basic search
 | 
			
		||||
                for field in viewset.search_fields:
 | 
			
		||||
                    obj = self.fix_note_object(obj, field)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,11 @@ from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from rest_framework.filters import SearchFilter
 | 
			
		||||
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
 | 
			
		||||
from permission.backends import PermissionBackend
 | 
			
		||||
from note.models import Alias
 | 
			
		||||
 | 
			
		||||
from .filters import RegexSafeSearchFilter
 | 
			
		||||
from .serializers import UserSerializer, ContentTypeSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -107,6 +107,6 @@ class ContentTypeViewSet(ReadOnlyModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = ContentType.objects.order_by('id')
 | 
			
		||||
    serializer_class = ContentTypeSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['id', 'app_label', 'model', ]
 | 
			
		||||
    search_fields = ['$app_label', '$model', ]
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import OrderingFilter
 | 
			
		||||
 | 
			
		||||
from api.viewsets import ReadOnlyProtectedModelViewSet
 | 
			
		||||
 | 
			
		||||
from .serializers import ChangelogSerializer
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,9 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import OrderingFilter, SearchFilter
 | 
			
		||||
from rest_framework.filters import OrderingFilter
 | 
			
		||||
 | 
			
		||||
from api.filters import RegexSafeSearchFilter
 | 
			
		||||
from api.viewsets import ReadProtectedModelViewSet
 | 
			
		||||
 | 
			
		||||
from .serializers import ProfileSerializer, ClubSerializer, MembershipSerializer
 | 
			
		||||
@@ -17,7 +19,7 @@ class ProfileViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Profile.objects.order_by('id')
 | 
			
		||||
    serializer_class = ProfileSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['user', 'user__first_name', 'user__last_name', 'user__username', 'user__email',
 | 
			
		||||
                        'user__note__alias__name', 'user__note__alias__normalized_name', 'phone_number', "section",
 | 
			
		||||
                        'department', 'promotion', 'address', 'paid', 'ml_events_registration', 'ml_sport_registration',
 | 
			
		||||
@@ -34,7 +36,7 @@ class ClubViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Club.objects.order_by('id')
 | 
			
		||||
    serializer_class = ClubSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'email', 'note__alias__name', 'note__alias__normalized_name', 'parent_club',
 | 
			
		||||
                        'parent_club__name', 'require_memberships', 'membership_fee_paid', 'membership_fee_unpaid',
 | 
			
		||||
                        'membership_duration', 'membership_start', 'membership_end', ]
 | 
			
		||||
@@ -49,7 +51,7 @@ class MembershipViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Membership.objects.order_by('id')
 | 
			
		||||
    serializer_class = MembershipSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, OrderingFilter, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['club__name', 'club__email', 'club__note__alias__name', 'club__note__alias__normalized_name',
 | 
			
		||||
                        'user__username', 'user__last_name', 'user__first_name', 'user__email',
 | 
			
		||||
                        'user__note__alias__name', 'user__note__alias__normalized_name',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,18 @@
 | 
			
		||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import OrderingFilter, SearchFilter
 | 
			
		||||
from rest_framework.filters import OrderingFilter
 | 
			
		||||
from rest_framework import viewsets
 | 
			
		||||
from rest_framework.response import Response
 | 
			
		||||
from rest_framework import status
 | 
			
		||||
 | 
			
		||||
from api.filters import RegexSafeSearchFilter
 | 
			
		||||
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
 | 
			
		||||
from permission.backends import PermissionBackend
 | 
			
		||||
 | 
			
		||||
@@ -29,7 +32,7 @@ class NotePolymorphicViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Note.objects.order_by('id')
 | 
			
		||||
    serializer_class = NotePolymorphicSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter, OrderingFilter]
 | 
			
		||||
    filterset_fields = ['alias__name', 'polymorphic_ctype', 'is_active', 'balance', 'last_negative', 'created_at', ]
 | 
			
		||||
    search_fields = ['$alias__normalized_name', '$alias__name', '$polymorphic_ctype__model',
 | 
			
		||||
                     '$noteuser__user__last_name', '$noteuser__user__first_name', '$noteuser__user__email',
 | 
			
		||||
@@ -95,7 +98,7 @@ class AliasViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Alias.objects
 | 
			
		||||
    serializer_class = AliasSerializer
 | 
			
		||||
    filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
 | 
			
		||||
    filter_backends = [RegexSafeSearchFilter, DjangoFilterBackend, OrderingFilter]
 | 
			
		||||
    search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
 | 
			
		||||
    filterset_fields = ['name', 'normalized_name', 'note', 'note__noteuser__user',
 | 
			
		||||
                        'note__noteclub__club', 'note__polymorphic_ctype__model', ]
 | 
			
		||||
@@ -147,7 +150,7 @@ class AliasViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
class ConsumerViewSet(ReadOnlyProtectedModelViewSet):
 | 
			
		||||
    queryset = Alias.objects
 | 
			
		||||
    serializer_class = ConsumerSerializer
 | 
			
		||||
    filter_backends = [SearchFilter, OrderingFilter, DjangoFilterBackend]
 | 
			
		||||
    filter_backends = [RegexSafeSearchFilter, OrderingFilter, DjangoFilterBackend]
 | 
			
		||||
    search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
 | 
			
		||||
    filterset_fields = ['name', 'normalized_name', 'note', 'note__noteuser__user',
 | 
			
		||||
                        'note__noteclub__club', 'note__polymorphic_ctype__model', ]
 | 
			
		||||
@@ -207,7 +210,7 @@ class TemplateCategoryViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = TemplateCategory.objects.order_by('name')
 | 
			
		||||
    serializer_class = TemplateCategorySerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'templates', 'templates__name']
 | 
			
		||||
    search_fields = ['$name', '$templates__name', ]
 | 
			
		||||
 | 
			
		||||
@@ -220,7 +223,7 @@ class TransactionTemplateViewSet(viewsets.ModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = TransactionTemplate.objects.order_by('name')
 | 
			
		||||
    serializer_class = TransactionTemplateSerializer
 | 
			
		||||
    filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
 | 
			
		||||
    filter_backends = [RegexSafeSearchFilter, DjangoFilterBackend, OrderingFilter]
 | 
			
		||||
    filterset_fields = ['name', 'amount', 'display', 'category', 'category__name', ]
 | 
			
		||||
    search_fields = ['$name', '$category__name', ]
 | 
			
		||||
    ordering_fields = ['amount', ]
 | 
			
		||||
@@ -234,7 +237,7 @@ class TransactionViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Transaction.objects.order_by('-created_at')
 | 
			
		||||
    serializer_class = TransactionPolymorphicSerializer
 | 
			
		||||
    filter_backends = [SearchFilter, DjangoFilterBackend, OrderingFilter]
 | 
			
		||||
    filter_backends = [RegexSafeSearchFilter, DjangoFilterBackend, OrderingFilter]
 | 
			
		||||
    filterset_fields = ['source', 'source_alias', 'source__alias__name', 'source__alias__normalized_name',
 | 
			
		||||
                        'destination', 'destination_alias', 'destination__alias__name',
 | 
			
		||||
                        'destination__alias__normalized_name', 'quantity', 'polymorphic_ctype', 'amount',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from api.viewsets import ReadOnlyProtectedModelViewSet
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import SearchFilter
 | 
			
		||||
 | 
			
		||||
from api.filters import RegexSafeSearchFilter
 | 
			
		||||
from api.viewsets import ReadOnlyProtectedModelViewSet
 | 
			
		||||
 | 
			
		||||
from .serializers import PermissionSerializer, RoleSerializer
 | 
			
		||||
from ..models import Permission, Role
 | 
			
		||||
@@ -17,7 +18,7 @@ class PermissionViewSet(ReadOnlyProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Permission.objects.order_by('id')
 | 
			
		||||
    serializer_class = PermissionSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['model', 'type', 'query', 'mask', 'field', 'permanent', ]
 | 
			
		||||
    search_fields = ['$model__name', '$query', '$description', ]
 | 
			
		||||
 | 
			
		||||
@@ -30,6 +31,6 @@ class RoleViewSet(ReadOnlyProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Role.objects.order_by('id')
 | 
			
		||||
    serializer_class = RoleSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'permissions', 'for_club', 'memberships__user', ]
 | 
			
		||||
    search_fields = ['$name', '$for_club__name', ]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,8 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import SearchFilter
 | 
			
		||||
 | 
			
		||||
from api.filters import RegexSafeSearchFilter
 | 
			
		||||
from api.viewsets import ReadProtectedModelViewSet
 | 
			
		||||
 | 
			
		||||
from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer, \
 | 
			
		||||
@@ -18,7 +19,7 @@ class InvoiceViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Invoice.objects.order_by('id')
 | 
			
		||||
    serializer_class = InvoiceSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['bde', 'object', 'description', 'name', 'address', 'date', 'acquitted', 'locked', ]
 | 
			
		||||
    search_fields = ['$object', '$description', '$name', '$address', ]
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +32,7 @@ class ProductViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Product.objects.order_by('invoice_id', 'id')
 | 
			
		||||
    serializer_class = ProductSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['invoice', 'designation', 'quantity', 'amount', ]
 | 
			
		||||
    search_fields = ['$designation', '$invoice__object', ]
 | 
			
		||||
 | 
			
		||||
@@ -44,7 +45,7 @@ class RemittanceTypeViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = RemittanceType.objects.order_by('id')
 | 
			
		||||
    serializer_class = RemittanceTypeSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['note', ]
 | 
			
		||||
    search_fields = ['$note__special_type', ]
 | 
			
		||||
 | 
			
		||||
@@ -57,7 +58,7 @@ class RemittanceViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Remittance.objects.order_by('id')
 | 
			
		||||
    serializer_class = RemittanceSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['date', 'remittance_type', 'comment', 'closed', 'transaction_proxies__transaction', ]
 | 
			
		||||
    search_fields = ['$remittance_type__note__special_type', '$comment', ]
 | 
			
		||||
 | 
			
		||||
@@ -70,7 +71,7 @@ class SogeCreditViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = SogeCredit.objects.order_by('id')
 | 
			
		||||
    serializer_class = SogeCreditSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['user', 'user__last_name', 'user__first_name', 'user__email', 'user__note__alias__name',
 | 
			
		||||
                        'user__note__alias__normalized_name', 'transactions', 'credit_transaction', ]
 | 
			
		||||
    search_fields = ['$user__last_name', '$user__first_name', '$user__email', '$user__note__alias__name',
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,9 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import OrderingFilter, SearchFilter
 | 
			
		||||
from rest_framework.filters import OrderingFilter
 | 
			
		||||
 | 
			
		||||
from api.filters import RegexSafeSearchFilter
 | 
			
		||||
from api.viewsets import ReadProtectedModelViewSet
 | 
			
		||||
 | 
			
		||||
from .serializers import WEIClubSerializer, BusSerializer, BusTeamSerializer, WEIRoleSerializer, \
 | 
			
		||||
@@ -18,7 +20,7 @@ class WEIClubViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = WEIClub.objects.order_by('id')
 | 
			
		||||
    serializer_class = WEIClubSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'year', 'date_start', 'date_end', 'email', 'note__alias__name',
 | 
			
		||||
                        'note__alias__normalized_name', 'parent_club', 'parent_club__name', 'require_memberships',
 | 
			
		||||
                        'membership_fee_paid', 'membership_fee_unpaid', 'membership_duration', 'membership_start',
 | 
			
		||||
@@ -34,7 +36,7 @@ class BusViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = Bus.objects.order_by('id')
 | 
			
		||||
    serializer_class = BusSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'wei', 'description', ]
 | 
			
		||||
    search_fields = ['$name', '$wei__name', '$description', ]
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +49,7 @@ class BusTeamViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = BusTeam.objects.order_by('id')
 | 
			
		||||
    serializer_class = BusTeamSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'bus', 'color', 'description', 'bus__wei', ]
 | 
			
		||||
    search_fields = ['$name', '$bus__name', '$bus__wei__name', '$description', ]
 | 
			
		||||
 | 
			
		||||
@@ -60,7 +62,7 @@ class WEIRoleViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = WEIRole.objects.order_by('id')
 | 
			
		||||
    serializer_class = WEIRoleSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['name', 'permissions', 'memberships', ]
 | 
			
		||||
    search_fields = ['$name', ]
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +75,7 @@ class WEIRegistrationViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = WEIRegistration.objects.order_by('id')
 | 
			
		||||
    serializer_class = WEIRegistrationSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email',
 | 
			
		||||
                        'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name',
 | 
			
		||||
                        'wei__email', 'wei__year', 'soge_credit', 'caution_check', 'birth_date', 'gender',
 | 
			
		||||
@@ -92,7 +94,7 @@ class WEIMembershipViewSet(ReadProtectedModelViewSet):
 | 
			
		||||
    """
 | 
			
		||||
    queryset = WEIMembership.objects.order_by('id')
 | 
			
		||||
    serializer_class = WEIMembershipSerializer
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
 | 
			
		||||
    filter_backends = [DjangoFilterBackend, OrderingFilter, RegexSafeSearchFilter]
 | 
			
		||||
    filterset_fields = ['club__name', 'club__email', 'club__note__alias__name',
 | 
			
		||||
                        'club__note__alias__normalized_name', 'user__username', 'user__last_name',
 | 
			
		||||
                        'user__first_name', 'user__email', 'user__note__alias__name',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user