refactor permissions
This commit is contained in:
parent
b94c058598
commit
c059b88101
@ -15,6 +15,13 @@ class UserSerializer(ModelSerializer):
|
|||||||
'can_data_be_shared']
|
'can_data_be_shared']
|
||||||
|
|
||||||
|
|
||||||
|
class UserListSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['id', 'username']
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateSerializer(ModelSerializer):
|
class UserUpdateSerializer(ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -7,7 +7,7 @@ class AdminProject(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.display(description='contributors')
|
@admin.display(description='contributors')
|
||||||
def contributors(self, obj):
|
def contributors(self, obj):
|
||||||
return obj.contributor
|
return obj.contributors
|
||||||
|
|
||||||
class AdminIssue(admin.ModelAdmin):
|
class AdminIssue(admin.ModelAdmin):
|
||||||
list_display = ('id', 'title', 'author', 'project')
|
list_display = ('id', 'title', 'author', 'project')
|
||||||
|
@ -39,9 +39,13 @@ class ProjectContributor(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('contributor', 'project')
|
unique_together = ('contributor', 'project')
|
||||||
|
|
||||||
|
def get_user(self):
|
||||||
|
return self.contributor
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.contributor.username
|
return self.contributor.username
|
||||||
|
|
||||||
|
|
||||||
class Issue(models.Model):
|
class Issue(models.Model):
|
||||||
|
|
||||||
class Priority(models.TextChoices):
|
class Priority(models.TextChoices):
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from rest_framework.permissions import BasePermission
|
from rest_framework.permissions import BasePermission
|
||||||
from support.models import Project
|
from support.models import Project, Issue, Comment
|
||||||
|
|
||||||
|
|
||||||
class IsAuthor(BasePermission):
|
class IsAuthor(BasePermission):
|
||||||
|
|
||||||
@ -13,7 +14,8 @@ class IsAuthor(BasePermission):
|
|||||||
class IsContributor(BasePermission):
|
class IsContributor(BasePermission):
|
||||||
|
|
||||||
def has_object_permission(self, request, view, object):
|
def has_object_permission(self, request, view, object):
|
||||||
return bool(request.user
|
print(object.contributors.all())
|
||||||
and request.user.is_authenticated
|
return bool(request.user.is_authenticated
|
||||||
and request.user in object.contributors.all()
|
and request.user in object.contributors.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,12 +15,15 @@ class ContributorSerializer(ModelSerializer):
|
|||||||
|
|
||||||
class ContributorListSerializer(ModelSerializer):
|
class ContributorListSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
contributor = StringRelatedField(many=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProjectContributor
|
model = ProjectContributor
|
||||||
fields = ['contributor']
|
fields = ['contributor']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectSerializer(ModelSerializer):
|
class ProjectSerializer(ModelSerializer):
|
||||||
|
|
||||||
author = StringRelatedField(many=False)
|
author = StringRelatedField(many=False)
|
||||||
@ -65,13 +68,9 @@ class IssueSerializer(ModelSerializer):
|
|||||||
model = Issue
|
model = Issue
|
||||||
fields = ['id', 'title', 'project', 'date_created', 'priority',
|
fields = ['id', 'title', 'project', 'date_created', 'priority',
|
||||||
'tag', 'status', 'author']
|
'tag', 'status', 'author']
|
||||||
read_only_field = ['author']
|
|
||||||
|
|
||||||
|
|
||||||
def validate_author(self, data):
|
|
||||||
if Project.objects.filter(contributors=data.author).exists():
|
|
||||||
raise ValidationError("Requestor isn't contributor")
|
|
||||||
return data
|
|
||||||
|
|
||||||
def validate_project(self, data):
|
def validate_project(self, data):
|
||||||
# if data['user'] not in data['project'].contributors:
|
# if data['user'] not in data['project'].contributors:
|
||||||
@ -84,15 +83,34 @@ class IssueSerializer(ModelSerializer):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class IssueDetailSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
comments = SerializerMethodField()
|
||||||
|
author = StringRelatedField(many=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Issue
|
||||||
|
fields = ['title', 'project', 'date_created', 'priority',
|
||||||
|
'tag', 'status', 'author', 'comments']
|
||||||
|
|
||||||
|
def get_comments(self, instance):
|
||||||
|
queryset = Comment.objects.filter(issue=instance.id)
|
||||||
|
serializer = CommentListSerializer(queryset, many=True)
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
|
||||||
class CommentListSerializer(ModelSerializer):
|
class CommentListSerializer(ModelSerializer):
|
||||||
|
|
||||||
issue = IssueSerializer(many=False)
|
issue = IssueSerializer(many=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Comment
|
model = Comment
|
||||||
fields = ['title', 'date_created', 'author', 'issue']
|
fields = ['title', 'date_created', 'author', 'issue']
|
||||||
|
|
||||||
|
|
||||||
class CommentDetailSerializer(ModelSerializer):
|
class CommentDetailSerializer(ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Comment
|
model = Comment
|
||||||
fields = ['title', 'date_created', 'author', 'description']
|
fields = ['title', 'date_created', 'description', 'issue', 'author']
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
|
from django.contrib.auth.checks import check_models_permissions
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from rest_framework.serializers import raise_errors_on_nested_writes
|
from rest_framework.serializers import raise_errors_on_nested_writes
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
from rest_framework.views import APIView
|
||||||
from support.models import Project, ProjectContributor, Issue, Comment
|
from support.models import Project, ProjectContributor, Issue, Comment
|
||||||
from authentication.models import User
|
from authentication.models import User
|
||||||
from support.serializers import (ProjectSerializer,
|
from support.serializers import (ProjectSerializer,
|
||||||
ProjectDetailSerializer,
|
ProjectDetailSerializer,
|
||||||
ContributorSerializer,
|
ContributorSerializer,
|
||||||
|
ContributorListSerializer,
|
||||||
IssueSerializer,
|
IssueSerializer,
|
||||||
|
IssueDetailSerializer,
|
||||||
CommentListSerializer,
|
CommentListSerializer,
|
||||||
CommentDetailSerializer)
|
CommentDetailSerializer)
|
||||||
|
from authentication.serializers import UserListSerializer
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.permissions import (IsAuthenticated,
|
from rest_framework.permissions import (IsAuthenticated,
|
||||||
@ -16,6 +21,7 @@ from rest_framework.permissions import (IsAuthenticated,
|
|||||||
from support.permissions import IsAuthor, IsContributor
|
from support.permissions import IsAuthor, IsContributor
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
class ProjectViewSet(ModelViewSet):
|
class ProjectViewSet(ModelViewSet):
|
||||||
@ -26,10 +32,30 @@ class ProjectViewSet(ModelViewSet):
|
|||||||
|
|
||||||
queryset = Project.objects.filter(active=True)
|
queryset = Project.objects.filter(active=True)
|
||||||
|
|
||||||
def get_serializer_class(self):
|
|
||||||
if self.action == 'retrieve':
|
def retrieve(self, request, pk, *args, **kwargs):
|
||||||
return self.detail_serializer_class
|
"""
|
||||||
return super().get_serializer_class()
|
check if requestor is in the project's contributor
|
||||||
|
Raises exception or returns project detail
|
||||||
|
"""
|
||||||
|
if not request.user in Project.objects.get(id=pk).contributors.all():
|
||||||
|
raise PermissionDenied()
|
||||||
|
queryset = Project.objects.get(id=pk)
|
||||||
|
return Response(ProjectDetailSerializer(queryset).data)
|
||||||
|
|
||||||
|
def partial_update(self, request, pk, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
check if requestor is author
|
||||||
|
then save changes and returns project details
|
||||||
|
"""
|
||||||
|
if not request.user == Project.objects.get(id=pk).author:
|
||||||
|
raise PermissionDenied()
|
||||||
|
queryset = Project.objects.get(id=pk)
|
||||||
|
serialized = ProjectDetailSerializer(queryset, data=request.data, partial=True)
|
||||||
|
if serialized.is_valid(raise_exception=True):
|
||||||
|
serialized.save()
|
||||||
|
return Response(serialized.data)
|
||||||
|
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
"""set authenticated user as author and contributor on creation"""
|
"""set authenticated user as author and contributor on creation"""
|
||||||
@ -39,60 +65,102 @@ class ProjectViewSet(ModelViewSet):
|
|||||||
if contributor_serializer.is_valid():
|
if contributor_serializer.is_valid():
|
||||||
contributor_serializer.save()
|
contributor_serializer.save()
|
||||||
|
|
||||||
@action(detail=True, methods=['patch'],
|
|
||||||
permission_classes=[IsContributor],
|
@action(detail=True, methods=['get'], permission_classes=[IsContributor])
|
||||||
basename='add_contributor')
|
def test(self, request, pk):
|
||||||
def add_contributor(self, request, pk):
|
"""only for testing purpose, should be deleted not published"""
|
||||||
"""Create the user/project contributor's relation"""
|
if not request.user in Project.objects.get(id=pk).contributors.all():
|
||||||
if 'contributor' in request.data:
|
raise PermissionDenied()
|
||||||
|
return Response("OK")
|
||||||
|
|
||||||
|
@action(detail=True, methods=['patch'], permission_classes=[IsContributor])
|
||||||
|
def contributor(self, request, pk):
|
||||||
|
"""Add a contributor to a project
|
||||||
|
by creating a ProjectContributor's instance
|
||||||
|
"""
|
||||||
|
#check if requestor is contributor
|
||||||
|
if not request.user in Project.objects.get(id=pk).contributors.all():
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
if request.data is None or not 'contributor' in request.data:
|
||||||
|
return Response(f"Key error;`contributor` is expected",
|
||||||
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
#get the user's instance
|
||||||
contributor = User.objects.get(username=request.data['contributor'])
|
contributor = User.objects.get(username=request.data['contributor'])
|
||||||
data = {'contributor': contributor.id, 'project': pk}
|
data = {'contributor': contributor.id, 'project': int(pk)}
|
||||||
serializer = ContributorSerializer(data=data)
|
serializer = ContributorSerializer(data=data)
|
||||||
|
project = Project.objects.get(id=pk)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response(f"User {contributor} added",
|
return Response(f"User {contributor} "
|
||||||
|
f"added to project {project}",
|
||||||
status=status.HTTP_202_ACCEPTED)
|
status=status.HTTP_202_ACCEPTED)
|
||||||
return Response("This user is already contributing",
|
return Response("This user is already contributing",
|
||||||
status=status.HTTP_226_IM_USED)
|
status=status.HTTP_226_IM_USED)
|
||||||
return Response(f"Key error;`contributor` is expected, "
|
|
||||||
f"not `{list(request.data)[0]}`",
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class IssueViewSet(ModelViewSet):
|
class IssueViewSet(ModelViewSet):
|
||||||
permission_classes = [IsContributor]
|
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||||
|
|
||||||
serializer_class = IssueSerializer
|
serializer_class = IssueSerializer
|
||||||
|
detail_serializer_class = IssueDetailSerializer
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
if self.action == 'retrieve':
|
||||||
|
return self.detail_serializer_class
|
||||||
|
return super().get_serializer_class()
|
||||||
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
#check for the right query string or return nothing
|
"""
|
||||||
|
returns only the issues related to projects
|
||||||
|
where requestor is contributor or empty list
|
||||||
|
"""
|
||||||
if self.request.GET.get('project'):
|
if self.request.GET.get('project'):
|
||||||
project_id = int(self.request.GET.get('project'))
|
project_id = int(self.request.GET.get('project'))
|
||||||
project = Project.objects.get(id=project_id)
|
if not self.request.user in Project.objects.get(
|
||||||
self.check_object_permissions(self.request, project)
|
id=project_id).contributors.all():
|
||||||
|
raise PermissionDenied()
|
||||||
return Issue.objects.filter(project=project_id)
|
return Issue.objects.filter(project=project_id)
|
||||||
|
projects = Project.objects.filter(contributors=self.request.user).values('id')
|
||||||
|
#query on a list
|
||||||
|
return Issue.objects.filter(project__in=projects)
|
||||||
|
|
||||||
|
|
||||||
def get_contributors(self, project):
|
def perform_update(self, serializer):
|
||||||
queryset = ProjectContributor.objects.filter(project=project)
|
if self.request.user == serializer.author:
|
||||||
contributors_serializer = ContributorSerializer(queryset, many=True)
|
return Response("OK")
|
||||||
return contributors_serializer.data
|
if serializer.is_valid(raise_exception=True):
|
||||||
|
serializer.save(author=author)
|
||||||
|
return Response(serializer.data)
|
||||||
|
return Response("Data error", status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@action(detail=True, methods=['get'])
|
||||||
|
def contributors(self, request, pk):
|
||||||
|
"""
|
||||||
|
check if requestor is contributor then returns the list
|
||||||
|
of the contributors to the issue's project or raise unauthorized
|
||||||
|
"""
|
||||||
|
issue = Issue.objects.get(id=pk)
|
||||||
|
if ProjectContributor.objects.filter(project=issue.project).filter(contributor=request.user):
|
||||||
|
return Response(UserListSerializer(issue.project.contributors.all(), many=True).data)
|
||||||
|
else:
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
print(request.data['project'])
|
if not 'project' in request.data:
|
||||||
|
return Response("Need project")
|
||||||
project = Project.objects.get(id=request.data['project'])
|
project = Project.objects.get(id=request.data['project'])
|
||||||
serializer = IssueSerializer(data=request.data)
|
serializer = IssueSerializer(data=request.data)
|
||||||
|
if self.request.user not in project.contributors.all():
|
||||||
|
return Response("Requestor isn't contributor for this project",
|
||||||
print(request.data['project'], type(request.data['project']))
|
status=status.HTTP_403_FORBIDDEN)
|
||||||
print(self.get_contributors(request.data['project']))
|
|
||||||
|
|
||||||
if self.request.user in project.contributors:
|
|
||||||
if serializer.is_valid(raise_exception=True):
|
if serializer.is_valid(raise_exception=True):
|
||||||
serializer.author = self.request.user
|
serializer.save(author=self.request.user)
|
||||||
serializer.save()
|
|
||||||
response = {
|
response = {
|
||||||
"message": f"Issue created for project {project}",
|
"message": f"Issue created for project {project}",
|
||||||
"data": serializer.data
|
"data": serializer.data
|
||||||
@ -105,17 +173,67 @@ class IssueViewSet(ModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class ContributorViewSet(ModelViewSet):
|
class ContributorViewSet(ModelViewSet):
|
||||||
|
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||||
|
|
||||||
serializer_class = ContributorSerializer
|
serializer_class = ContributorSerializer
|
||||||
queryset = ProjectContributor.objects.all()
|
queryset = ProjectContributor.objects.all()
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.GET.get('project'):
|
||||||
|
serializer_class = ContributorListSerializer
|
||||||
|
return (ProjectContributor.objects.
|
||||||
|
filter(project=self.request.GET.get('project')))
|
||||||
|
elif self.request.GET.get('user'):
|
||||||
|
user_id = User.objects.get(username=self.request.GET.get('user')).id
|
||||||
|
return (ProjectContributor.objects.
|
||||||
|
filter(contributor=user_id))
|
||||||
|
return self.queryset
|
||||||
|
|
||||||
|
|
||||||
class CommentViewSet(ModelViewSet):
|
class CommentViewSet(ModelViewSet):
|
||||||
|
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||||
|
|
||||||
serializer_class = CommentListSerializer
|
serializer_class = CommentListSerializer
|
||||||
detail_serializer_class = CommentDetailSerializer
|
detail_serializer_class = CommentDetailSerializer
|
||||||
|
|
||||||
queryset = Comment.objects.all()
|
queryset = Comment.objects.all()
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
"""returns only comments associated with issue where the requestor
|
||||||
|
is project's contributor
|
||||||
|
"""
|
||||||
|
if self.request.GET.get('issue'):
|
||||||
|
issue_id = int(self.request.GET.get('issue'))
|
||||||
|
print(issue_id, type(issue_id))
|
||||||
|
project = Issue.objects.get(id=issue_id).project
|
||||||
|
if not self.request.user in Issue.objects.get(
|
||||||
|
id=issue_id).project.contributors.all():
|
||||||
|
raise PermissionDenied()
|
||||||
|
return Comment.objects.filter(issue=issue_id)
|
||||||
|
if self.request.data:
|
||||||
|
return Comment.objects.filter(issue=self.request.data['issue'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.action == 'retrieve':
|
if self.action == 'retrieve':
|
||||||
return self.detail_serializer_class
|
return self.detail_serializer_class
|
||||||
return super().get_serializer_class()
|
return super().get_serializer_class()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
user = request.user
|
||||||
|
issue = Issue.objects.get(id=request.data['issue'])
|
||||||
|
project = issue.project
|
||||||
|
if issue.project.contributors.filter(username=request.user.username):
|
||||||
|
serializer = CommentDetailSerializer(data=request.data)
|
||||||
|
if serializer.is_valid(raise_exception=True):
|
||||||
|
#serializer.author = request.user.username
|
||||||
|
serializer.save(author=request.user.username)
|
||||||
|
response = {"message": "comment created",
|
||||||
|
"data": serializer.data}
|
||||||
|
return Response(response, status=status.HTTP_201_CREATED)
|
||||||
|
return Response("Not allowed; "
|
||||||
|
f"{user} isn't contributor for project {project}",
|
||||||
|
status=status.HTTP_403_FORBIDDEN)
|
Loading…
x
Reference in New Issue
Block a user