From 09d2452080b5b004e89bbcff0aa70ed26c55e960 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 7 Aug 2021 17:26:38 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Abstract=20permissions,=20add=20?= =?UTF-8?q?`ResearchTag`=20and=20`ResearchGroup`=20viewsets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/sophon/core/models.py | 48 +++++++++------- backend/sophon/core/serializers.py | 47 ++++++++++++++++ backend/sophon/core/urls.py | 2 + backend/sophon/core/views.py | 71 ++++++++++++++++++++++-- backend/sophon/{core => }/permissions.py | 6 +- 5 files changed, 145 insertions(+), 29 deletions(-) rename backend/sophon/{core => }/permissions.py (72%) diff --git a/backend/sophon/core/models.py b/backend/sophon/core/models.py index b74e2f5..3955c9c 100644 --- a/backend/sophon/core/models.py +++ b/backend/sophon/core/models.py @@ -381,6 +381,24 @@ class ResearchGroup(models.Model): max_length=16, ) + def can_be_edited_by(self, user) -> bool: + if user.is_superuser: + return True + + elif user in self.members: + return True + + return False + + def can_be_administrated_by(self, user) -> bool: + if user.is_superuser: + return True + + elif user in self.owner: + return True + + return False + def __str__(self): return f"{self.slug}" @@ -420,6 +438,15 @@ class ResearchTag(models.Model): on_delete=models.CASCADE, ) + def can_be_administrated_by(self, user) -> bool: + if user.is_superuser: + return True + + elif user == self.owner: + return True + + return False + def __str__(self): return f"[{self.name}]" @@ -482,13 +509,6 @@ class ResearchProject(models.Model): ) def can_be_viewed_by(self, user) -> bool: - """ - Check whether an user should be allowed to **view** the project contents. - - :param user: The user to check permissions for. - :return: :data:`True` if the user can view the details, or :data:`False` if they cannot. - """ - if user.is_superuser: return True @@ -503,13 +523,6 @@ class ResearchProject(models.Model): raise ValueError(f"Unknown visibility value: {self.visibility}") def can_be_edited_by(self, user) -> bool: - """ - Check whether an user should be allowed to **edit** the project details. - - :param user: The user to check permissions for. - :return: :data:`True` if the user can edit the details, or :data:`False` if they cannot. - """ - if user.is_superuser: return True @@ -519,13 +532,6 @@ class ResearchProject(models.Model): return False def can_be_administrated_by(self, user) -> bool: - """ - Check whether an user should be allowed to **administrate** the project. - - :param user: The user to check permissions for. - :return: :data:`True` if the user can administrate the project, or :data:`False` if they cannot. - """ - if user.is_superuser: return True diff --git a/backend/sophon/core/serializers.py b/backend/sophon/core/serializers.py index 468156d..a6034d8 100644 --- a/backend/sophon/core/serializers.py +++ b/backend/sophon/core/serializers.py @@ -120,6 +120,53 @@ class ResearchProjectPublicSerializer(serializers.ModelSerializer): ) +class ResearchTagPublicSerializer(serializers.ModelSerializer): + """ + Serializer for users who are not owners of a :class:`.models.ResearchTag`. + """ + + # TODO: Add a list of projects with the tag + + class Meta: + model = models.ResearchTag + fields = ( + "slug", + "name", + "description", + "color", + "owner", + ) + read_only_fields = ( + "slug", + "name", + "description", + "color", + "owner", + ) + + +class ResearchTagAdminSerializer(serializers.ModelSerializer): + """ + Serializer for users who are owners of a :class:`.models.ResearchTag`. + """ + + # TODO: Add a list of projects with the tag + + class Meta: + model = models.ResearchTag + fields = ( + "slug", + "name", + "description", + "color", + "owner", + ) + read_only_fields = ( + "slug", + "owner", + ) + + class ResearchProjectViewerSerializer(serializers.ModelSerializer): """ Serializer for users who are not collaborators of a :class:`~.models.ResearchProject`, but have permissions to view it. diff --git a/backend/sophon/core/urls.py b/backend/sophon/core/urls.py index c9a3918..72e8283 100644 --- a/backend/sophon/core/urls.py +++ b/backend/sophon/core/urls.py @@ -7,6 +7,8 @@ router = DefaultRouter() router.register("datasources", views.DataSourceViewSet) router.register("dataflows", views.DataFlowViewSet) router.register("projects", views.ResearchProjectViewSet) +router.register("tags", views.ResearchTagViewSet) +router.register("groups", views.ResearchGroupViewSet) urlpatterns = [ path("", include(router.urls)), diff --git a/backend/sophon/core/views.py b/backend/sophon/core/views.py index 7fae7da..1fcaee0 100644 --- a/backend/sophon/core/views.py +++ b/backend/sophon/core/views.py @@ -2,7 +2,8 @@ from logging import getLogger from rest_framework import viewsets, decorators, response, permissions, request as r -from . import models, serializers, permissions as custom_permissions +from . import models, serializers +from .. import permissions as custom_permissions log = getLogger(__name__) @@ -15,10 +16,10 @@ class ResearchProjectViewSet(viewsets.ModelViewSet): return { "list": [], "create": [permissions.IsAuthenticated], - "retrieve": [custom_permissions.CanViewProject], - "update": [custom_permissions.CanEditProject], - "partial_update": [custom_permissions.CanEditProject], - "destroy": [custom_permissions.CanAdministrateProject], + "retrieve": [custom_permissions.CanView], + "update": [custom_permissions.CanEdit], + "partial_update": [custom_permissions.CanEdit], + "destroy": [custom_permissions.CanAdministrate], "metadata": [], None: [], }[self.action] @@ -41,6 +42,66 @@ class ResearchProjectViewSet(viewsets.ModelViewSet): return serializers.ResearchProjectPublicSerializer +class ResearchGroupViewSet(viewsets.ModelViewSet): + queryset = models.ResearchGroup.objects.all() + + @property + def permission_classes(self): + return { + "list": [], + "create": [permissions.IsAuthenticated], + "retrieve": [permissions.IsAuthenticated], + "update": [custom_permissions.CanAdministrate], + "partial_update": [custom_permissions.CanAdministrate], + "destroy": [custom_permissions.CanAdministrate], + "metadata": [], + None: [], + }[self.action] + + def get_serializer_class(self): + if self.action == "list": + return serializers.ResearchGroupPublicSerializer + elif self.action == "create": + return serializers.ResearchGroupAdminSerializer + else: + group = self.get_object() + user = self.request.user + if group.can_be_administrated_by(user): + return serializers.ResearchGroupAdminSerializer + else: + return serializers.ResearchGroupPublicSerializer + + +class ResearchTagViewSet(viewsets.ModelViewSet): + queryset = models.ResearchTag.objects.all() + + @property + def permission_classes(self): + return { + "list": [], + "create": [permissions.IsAuthenticated], + "retrieve": [permissions.IsAuthenticated], + "update": [custom_permissions.CanAdministrate], + "partial_update": [custom_permissions.CanAdministrate], + "destroy": [custom_permissions.CanAdministrate], + "metadata": [], + None: [], + }[self.action] + + def get_serializer_class(self): + if self.action == "list": + return serializers.ResearchTagPublicSerializer + elif self.action == "create": + return serializers.ResearchTagAdminSerializer + else: + group = self.get_object() + user = self.request.user + if group.can_be_administrated_by(user): + return serializers.ResearchTagAdminSerializer + else: + return serializers.ResearchTagPublicSerializer + + class DataFlowViewSet(viewsets.ModelViewSet): """ Viewset for :class:`.models.DataFlow` instances. diff --git a/backend/sophon/core/permissions.py b/backend/sophon/permissions.py similarity index 72% rename from backend/sophon/core/permissions.py rename to backend/sophon/permissions.py index 63ae0b1..c2bce86 100644 --- a/backend/sophon/core/permissions.py +++ b/backend/sophon/permissions.py @@ -5,16 +5,16 @@ from rest_framework import permissions log = logging.getLogger(__name__) -class CanAdministrateProject(permissions.BasePermission): +class CanAdministrate(permissions.BasePermission): def has_object_permission(self, request, view, obj): return obj.can_be_administrated_by(request.user) -class CanEditProject(permissions.BasePermission): +class CanEdit(permissions.BasePermission): def has_object_permission(self, request, view, obj): return obj.can_be_edited_by(request.user) -class CanViewProject(permissions.BasePermission): +class CanView(permissions.BasePermission): def has_object_permission(self, request, view, obj): return obj.can_be_viewed_by(request.user)