diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 3d2d143f2..48898237d 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -59,19 +59,19 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV def update(self, *args, **kwargs): """ - Customized view endpoint to update all children if, the item type has changed. + Customized view endpoint to update all children if the item type has changed. """ old_type = self.get_object().type - result = super().update(*args, **kwargs) + response = super().update(*args, **kwargs) - # update all children, if the item type has changed + # Update all children if the item type has changed. item = self.get_object() if old_type != item.type: items_to_update = [] - # rekursively add children to items_to_update + # Recursively add children to items_to_update. def add_item(item): items_to_update.append(item) for child in item.children.all(): @@ -80,7 +80,7 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV add_item(item) inform_changed_data(items_to_update) - return result + return response @detail_route(methods=['POST', 'PATCH', 'DELETE']) def manage_speaker(self, request, pk=None): diff --git a/openslides/motions/views.py b/openslides/motions/views.py index af0132f03..e9699d709 100644 --- a/openslides/motions/views.py +++ b/openslides/motions/views.py @@ -21,6 +21,7 @@ from ..utils.rest_api import ( GenericViewSet, ModelViewSet, Response, + ReturnDict, UpdateModelMixin, ValidationError, detail_route, @@ -195,7 +196,12 @@ class MotionViewSet(ModelViewSet): inform_changed_data(new_users) headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + # Strip out response data so nobody gets unrestricted data. + data = ReturnDict( + id=serializer.data.get('id'), + serializer=serializer + ) + return Response(data, status=status.HTTP_201_CREATED, headers=headers) def update(self, request, *args, **kwargs): """ @@ -256,7 +262,8 @@ class MotionViewSet(ModelViewSet): new_users = list(updated_motion.supporters.all()) inform_changed_data(new_users) - return Response(serializer.data) + # We do not add serializer.data to response so nobody gets unrestricted data here. + return Response() @list_route(methods=['post']) def sort(self, request): @@ -636,10 +643,10 @@ class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet): """ Customized view endpoint to update a motion poll. """ - result = super().update(*args, **kwargs) + response = super().update(*args, **kwargs) poll = self.get_object() poll.motion.write_log([ugettext_noop('Vote updated')], self.request.user) - return result + return response def destroy(self, *args, **kwargs): """ diff --git a/openslides/utils/rest_api.py b/openslides/utils/rest_api.py index f1886bfdd..36a9f3b72 100644 --- a/openslides/utils/rest_api.py +++ b/openslides/utils/rest_api.py @@ -6,13 +6,14 @@ from rest_framework import status from rest_framework.decorators import detail_route, list_route from rest_framework.metadata import SimpleMetadata from rest_framework.mixins import ( - CreateModelMixin, + CreateModelMixin as _CreateModelMixin, DestroyModelMixin, ListModelMixin as _ListModelMixin, RetrieveModelMixin as _RetrieveModelMixin, - UpdateModelMixin, + UpdateModelMixin as _UpdateModelMixin, ) from rest_framework.relations import MANY_RELATION_KWARGS +from rest_framework.request import Request from rest_framework.response import Response from rest_framework.routers import DefaultRouter from rest_framework.serializers import ( @@ -33,10 +34,10 @@ from rest_framework.serializers import ( SerializerMethodField, ValidationError, ) +from rest_framework.utils.serializer_helpers import ReturnDict from rest_framework.viewsets import ( GenericViewSet as _GenericViewSet, ModelViewSet as _ModelViewSet, - ViewSet as _ViewSet, ) from .access_permissions import BaseAccessPermissions @@ -44,8 +45,8 @@ from .auth import user_to_collection_user from .collection import Collection, CollectionElement -__all__ = ['detail_route', 'DecimalField', 'list_route', 'SimpleMetadata', 'CreateModelMixin', - 'DestroyModelMixin', 'UpdateModelMixin', 'CharField', 'DictField', 'FileField', +__all__ = ['detail_route', 'DecimalField', 'list_route', 'SimpleMetadata', + 'DestroyModelMixin', 'CharField', 'DictField', 'FileField', 'IntegerField', 'JSONField', 'ListField', 'ListSerializer', 'status', 'RelatedField', 'SerializerMethodField', 'ValidationError'] @@ -237,13 +238,44 @@ class RetrieveModelMixin(_RetrieveModelMixin): return response +class CreateModelMixin(_CreateModelMixin): + """ + Mixin to override create requests. + """ + def create(self, request: Request, *args: Any, **kwargs: Any) -> Response: + """ + Just remove all response data (except 'id') so nobody may get + unrestricted data. + + Special viewsets may override this. + """ + response = super().create(request, *args, **kwargs) + response.data = ReturnDict( + id=response.data.get('id'), + serializer=response.data.serializer # This kwarg is not send to the client. + ) + return response + + +class UpdateModelMixin(_UpdateModelMixin): + """ + Mixin to override update requests. + """ + def update(self, request: Request, *args: Any, **kwargs: Any) -> Response: + """ + Just remove all response data so nobody may get unrestricted data. + + Special viewsets may override this. + """ + response = super().update(request, *args, **kwargs) + response.data = None + return response + + class GenericViewSet(PermissionMixin, _GenericViewSet): pass -class ModelViewSet(PermissionMixin, ListModelMixin, RetrieveModelMixin, _ModelViewSet): - pass - - -class ViewSet(PermissionMixin, _ViewSet): +class ModelViewSet(PermissionMixin, ListModelMixin, RetrieveModelMixin, + CreateModelMixin, UpdateModelMixin, _ModelViewSet): pass