diff --git a/apps/api/serializers.py b/apps/api/serializers.py new file mode 100644 index 00000000..1f658217 --- /dev/null +++ b/apps/api/serializers.py @@ -0,0 +1,32 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + + +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth.models import User +from rest_framework.serializers import ModelSerializer + +class UserSerializer(ModelSerializer): + """ + REST API Serializer for Users. + The djangorestframework plugin will analyse the model `User` and parse all fields in the API. + """ + + class Meta: + model = User + exclude = ( + 'password', + 'groups', + 'user_permissions', + ) + + +class ContentTypeSerializer(ModelSerializer): + """ + REST API Serializer for Users. + The djangorestframework plugin will analyse the model `User` and parse all fields in the API. + """ + + class Meta: + model = ContentType + fields = '__all__' diff --git a/apps/api/urls.py b/apps/api/urls.py index 40d21874..4addbdf1 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -3,109 +3,8 @@ from django.conf import settings from django.conf.urls import url, include -from django.contrib.auth.models import User -from django.contrib.contenttypes.models import ContentType -from django.db.models import Q -from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import routers, serializers -from rest_framework.viewsets import ReadOnlyModelViewSet -from api.viewsets import ReadProtectedModelViewSet -from note.models import Alias - - -class UserSerializer(serializers.ModelSerializer): - """ - REST API Serializer for Users. - The djangorestframework plugin will analyse the model `User` and parse all fields in the API. - """ - - class Meta: - model = User - exclude = ( - 'password', - 'groups', - 'user_permissions', - ) - - -class ContentTypeSerializer(serializers.ModelSerializer): - """ - REST API Serializer for Users. - The djangorestframework plugin will analyse the model `User` and parse all fields in the API. - """ - - class Meta: - model = ContentType - fields = '__all__' - - -class UserViewSet(ReadProtectedModelViewSet): - """ - REST API View set. - The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, - then render it on /api/users/ - """ - queryset = User.objects.all() - serializer_class = UserSerializer - filter_backends = [DjangoFilterBackend] - filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ] - - def get_queryset(self): - queryset = super().get_queryset() - # Sqlite doesn't support ORDER BY in subqueries - queryset = queryset.order_by("username") \ - if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset - - if "search" in self.request.GET: - pattern = self.request.GET["search"] - - # We match first a user by its username, then if an alias is matched without normalization - # And finally if the normalized pattern matches a normalized alias. - queryset = queryset.filter( - username__iregex="^" + pattern - ).union( - queryset.filter( - Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), all=True).union( - queryset.filter( - Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), - all=True).union( - queryset.filter( - Q(note__alias__normalized_name__iregex="^" + pattern.lower()) - & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), - all=True).union( - queryset.filter( - (Q(last_name__iregex="^" + pattern) | Q(first_name__iregex="^" + pattern)) - & ~Q(note__alias__normalized_name__iregex="^" + pattern.lower()) - & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), - all=True) - - queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ - else queryset.order_by("username") - - return queryset - - -# This ViewSet is the only one that is accessible from all authenticated users! -class ContentTypeViewSet(ReadOnlyModelViewSet): - """ - REST API View set. - The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, - then render it on /api/users/ - """ - queryset = ContentType.objects.all() - serializer_class = ContentTypeSerializer - +from rest_framework import routers +from .viewsets import ContentTypeViewSet, UserViewSet # Routers provide an easy way of automatically determining the URL conf. # Register each app API router and user viewset diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py index 01fc7998..825082c0 100644 --- a/apps/api/viewsets.py +++ b/apps/api/viewsets.py @@ -2,12 +2,22 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.contenttypes.models import ContentType +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.viewsets import ReadOnlyModelViewSet, ModelViewSet + from permission.backends import PermissionBackend -from rest_framework import viewsets + from note_kfet.middlewares import get_current_session +from note.models import Alias + +from .serializers import UserSerializer, ContentTypeSerializer -class ReadProtectedModelViewSet(viewsets.ModelViewSet): +class ReadProtectedModelViewSet(ModelViewSet): """ Protect a ModelViewSet by filtering the objects that the user cannot see. """ @@ -22,7 +32,7 @@ class ReadProtectedModelViewSet(viewsets.ModelViewSet): return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() -class ReadOnlyProtectedModelViewSet(viewsets.ReadOnlyModelViewSet): +class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet): """ Protect a ReadOnlyModelViewSet by filtering the objects that the user cannot see. """ @@ -35,3 +45,64 @@ class ReadOnlyProtectedModelViewSet(viewsets.ReadOnlyModelViewSet): user = self.request.user get_current_session().setdefault("permission_mask", 42) return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() + + + +class UserViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, + then render it on /api/users/ + """ + queryset = User.objects.all() + serializer_class = UserSerializer + filter_backends = [DjangoFilterBackend] + filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ] + + def get_queryset(self): + queryset = super().get_queryset() + # Sqlite doesn't support ORDER BY in subqueries + queryset = queryset.order_by("username") \ + if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset + + if "search" in self.request.GET: + pattern = self.request.GET["search"] + + # We match first a user by its username, then if an alias is matched without normalization + # And finally if the normalized pattern matches a normalized alias. + queryset = queryset.filter( + username__iregex="^" + pattern).union( + queryset.filter( + Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True).union( + queryset.filter( + Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True).union( + queryset.filter( + Q(note__alias__normalized_name__iregex="^" + pattern.lower()) + & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True).union( + queryset.filter( + (Q(last_name__iregex="^" + pattern) | Q(first_name__iregex="^" + pattern)) + & ~Q(note__alias__normalized_name__iregex="^" + pattern.lower()) + & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True) + + queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ + else queryset.order_by("username") + + return queryset + + +# This ViewSet is the only one that is accessible from all authenticated users! +class ContentTypeViewSet(ReadOnlyModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, + then render it on /api/users/ + """ + queryset = ContentType.objects.all() + serializer_class = ContentTypeSerializer