1
Fork 0
mirror of https://github.com/Steffo99/sophon.git synced 2024-12-22 23:04:21 +00:00

Properly check permissions for Projects

This commit is contained in:
Steffo 2021-04-15 17:14:28 +02:00 committed by Stefano Pigozzi
parent a7ae9f28f5
commit 95f4981dec
3 changed files with 45 additions and 23 deletions

View file

@ -9,19 +9,6 @@ import json
import abc import abc
class RelatedToProject(metaclass=abc.ABCMeta):
"""
A model which is related to :class:`.Project` in some way.
"""
@abc.abstractmethod
def get_project(self) -> "Project":
"""
:return: The project this object is related to.
"""
raise NotImplementedError()
class DataSource(models.Model): class DataSource(models.Model):
""" """
A :class:`.DataSource` is a web service which provides access to statistical information sourced by multiple data A :class:`.DataSource` is a web service which provides access to statistical information sourced by multiple data
@ -312,15 +299,12 @@ class DataFlow(models.Model):
return f"[{self.datasource}] {self.id}" return f"[{self.datasource}] {self.id}"
class Project(models.Model, RelatedToProject): class Project(models.Model):
""" """
A research :class:`.Project` is a work which may use zero or more :class:`.DataSource`\\ s to prove or disprove an A research :class:`.Project` is a work which may use zero or more :class:`.DataSource`\\ s to prove or disprove an
hypothesis. hypothesis.
""" """
def get_project(self):
return self
slug = models.SlugField( slug = models.SlugField(
"Slug", "Slug",
help_text="Unique alphanumeric string which identifies the project.", help_text="Unique alphanumeric string which identifies the project.",
@ -349,6 +333,7 @@ class Project(models.Model, RelatedToProject):
("PRIVATE", "🔒 Private"), ("PRIVATE", "🔒 Private"),
], ],
default="INTERNAL", default="INTERNAL",
max_length=16,
) )
owner = models.ForeignKey( owner = models.ForeignKey(
@ -371,5 +356,24 @@ class Project(models.Model, RelatedToProject):
blank=True, blank=True,
) )
def get_contributors(self):
return {self.owner, *self.collaborators.values()}
def can_be_viewed_by(self, user):
if self.visibility == "PUBLIC":
return True
elif self.visibility == "INTERNAL":
return not user.is_anonymous()
elif self.visibility == "PRIVATE":
return user in self.get_contributors()
else:
raise ValueError(f"Unknown visibility value: {self.visibility}")
def can_be_edited_by(self, user):
return user in self.get_contributors()
def can_be_administrated_by(self, user):
return user == self.owner
def __str__(self): def __str__(self):
return self.slug return self.slug

View file

@ -0,0 +1,18 @@
import logging
from rest_framework import permissions
log = logging.getLogger(__name__)
class ProjectPermissions(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

View file

@ -1,5 +1,5 @@
from rest_framework import viewsets, decorators, response from rest_framework import viewsets, decorators, response, permissions
from . import models, serializers from . import models, serializers, permissions as custom_permissions
from datetime import datetime from datetime import datetime
from logging import getLogger from logging import getLogger
@ -12,7 +12,7 @@ class ProjectViewSet(viewsets.ModelViewSet):
""" """
queryset = models.Project.objects.all() queryset = models.Project.objects.all()
serializer_class = serializers.ProjectSerializer serializer_class = serializers.ProjectSerializer
permission_classes = [] permission_classes = [custom_permissions.ProjectPermissions]
class DataFlowViewSet(viewsets.ModelViewSet): class DataFlowViewSet(viewsets.ModelViewSet):
@ -21,7 +21,7 @@ class DataFlowViewSet(viewsets.ModelViewSet):
""" """
queryset = models.DataFlow.objects.all() queryset = models.DataFlow.objects.all()
serializer_class = serializers.DataFlowSerializer serializer_class = serializers.DataFlowSerializer
permission_classes = [] permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
class DataSourceViewSet(viewsets.ModelViewSet): class DataSourceViewSet(viewsets.ModelViewSet):
@ -30,10 +30,10 @@ class DataSourceViewSet(viewsets.ModelViewSet):
""" """
queryset = models.DataSource.objects.all() queryset = models.DataSource.objects.all()
serializer_class = serializers.DataSourceSerializer serializer_class = serializers.DataSourceSerializer
permission_classes = [] permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
@decorators.action(methods=["post"], detail=True) @decorators.action(methods=["post"], detail=True)
def sync(self, request, pk): def sync(self, request, *args, **kwargs):
""" """
Syncronize the :class:`.models.DataFlow`\\ s with the ones stored in the server of the Syncronize the :class:`.models.DataFlow`\\ s with the ones stored in the server of the
:class:`.models.DataSource`\\ . :class:`.models.DataSource`\\ .