diff --git a/sophon/core/permissions.py b/sophon/core/permissions.py index 035146d..65aa273 100644 --- a/sophon/core/permissions.py +++ b/sophon/core/permissions.py @@ -4,15 +4,16 @@ from rest_framework import permissions log = logging.getLogger(__name__) -class ProjectPermissions(permissions.BasePermission): +class CanViewObject(permissions.BasePermission): def has_object_permission(self, request, view, obj): - if view.action == "retrieve": - return obj.can_be_viewed_by(request.user) - elif view.action == "update": - return obj.can_be_edited_by(request.user) - elif view.action == "partial_update": - return obj.can_be_edited_by(request.user) - elif view.action == "destroy": - return obj.can_be_administrated_by(request.user) - log.warning(f"Rejecting permissions for unknown action: {view.action}") - return False + return obj.can_be_viewed_by(request.user) + + +class CanEditObject(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + return obj.can_be_edited_by(request.user) + + +class CanAdministrateObject(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + return obj.can_be_administrated_by(request.user) diff --git a/sophon/core/serializers.py b/sophon/core/serializers.py index 5d2d374..7b6e6ff 100644 --- a/sophon/core/serializers.py +++ b/sophon/core/serializers.py @@ -30,6 +30,10 @@ class DataSourceSerializer(serializers.ModelSerializer): "builtin", "last_sync", ] + read_only_fields = [ + "builtin", + "last_sync", + ] class DataFlowSerializer(serializers.ModelSerializer): @@ -45,11 +49,35 @@ class DataFlowSerializer(serializers.ModelSerializer): "id", "description", ] + read_only_fields = [ + "surrogate_id", + ] -class ProjectSerializer(serializers.ModelSerializer): +class ProjectExternalSerializer(serializers.ModelSerializer): """ - Serializer for :class:`.models.Project` . + Serializer for :class:`.models.Project` when accessed from outside. + """ + + class Meta: + model = models.Project + fields = [ + "slug", + "name", + "visibility", + "owner", + ] + read_only_fields = [ + "slug", + "name", + "visibility", + "owner", + ] + + +class ProjectCollaboratorSerializer(serializers.ModelSerializer): + """ + Serializer for :class:`.models.Project` when accessed as a collaborator. """ class Meta: @@ -63,3 +91,30 @@ class ProjectSerializer(serializers.ModelSerializer): "collaborators", "flows", ] + read_only_fields = [ + "slug", + "visibility", + "owner", + "collaborators", + ] + + +class ProjectOwnerSerializer(serializers.ModelSerializer): + """ + Serializer for :class:`.models.Project` when accessed as the project owner. + """ + + class Meta: + model = models.Project + fields = [ + "slug", + "name", + "description", + "visibility", + "owner", + "collaborators", + "flows", + ] + read_only_fields = [ + "slug", + ] diff --git a/sophon/core/urls.py b/sophon/core/urls.py index f265484..b054216 100644 --- a/sophon/core/urls.py +++ b/sophon/core/urls.py @@ -4,7 +4,9 @@ from . import views router = DefaultRouter() -router.register("projects", views.ProjectViewSet) +router.register("projects_external", views.ProjectExternalViewSet) +router.register("projects_contributor", views.ProjectContributorViewSet) +router.register("projects_owner", views.ProjectOwnerViewSet) router.register("datasources", views.DataSourceViewSet) router.register("dataflows", views.DataFlowViewSet) diff --git a/sophon/core/views.py b/sophon/core/views.py index 23b3cfe..3f30f86 100644 --- a/sophon/core/views.py +++ b/sophon/core/views.py @@ -1,4 +1,4 @@ -from rest_framework import viewsets, decorators, response, permissions +from rest_framework import viewsets, decorators, response, permissions, mixins from . import models, serializers, permissions as custom_permissions from datetime import datetime from logging import getLogger @@ -6,19 +6,46 @@ from logging import getLogger log = getLogger(__name__) -class ProjectViewSet(viewsets.ModelViewSet): +class ProjectExternalViewSet(viewsets.ReadOnlyModelViewSet): """ - Viewset for :class:`.models.Project` instances. + Viewset for :class:`.models.Project` instances when viewed by the outside. """ + queryset = models.Project.objects.all() - serializer_class = serializers.ProjectSerializer - permission_classes = [custom_permissions.ProjectPermissions] + serializer_class = serializers.ProjectExternalSerializer + permission_classes = [] + + +class ProjectContributorViewSet(viewsets.GenericViewSet, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin): + """ + Viewset for :class:`.models.Project` instances when viewed by a project contributor. + """ + + queryset = models.Project.objects.all() + serializer_class = serializers.ProjectCollaboratorSerializer + permission_classes = [custom_permissions.CanEditObject] + + +class ProjectOwnerViewSet(viewsets.GenericViewSet, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin): + """ + Viewset for :class:`.models.Project` instances when viewed by a project owner. + """ + + queryset = models.Project.objects.all() + serializer_class = serializers.ProjectOwnerSerializer + permission_classes = [custom_permissions.CanAdministrateObject] class DataFlowViewSet(viewsets.ModelViewSet): """ Viewset for :class:`.models.DataFlow` instances. """ + queryset = models.DataFlow.objects.all() serializer_class = serializers.DataFlowSerializer permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly] @@ -28,6 +55,7 @@ class DataSourceViewSet(viewsets.ModelViewSet): """ Viewset for :class:`.models.DataSource` instances. """ + queryset = models.DataSource.objects.all() serializer_class = serializers.DataSourceSerializer permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]