added permission classes for sharing + tests

This commit is contained in:
vabene1111
2020-10-15 23:41:38 +02:00
parent 5c1cecb7e7
commit cd46203d55
4 changed files with 69 additions and 8 deletions

View File

@ -72,6 +72,23 @@ def is_object_owner(user, obj):
return False
def is_object_shared(user, obj):
"""
Tests if a given user is shared for a given object
test performed by checking user against the objects shared table
superusers bypass all checks, unauthenticated users cannot own anything
:param user django auth user object
:param obj any object that should be tested
:return: true if user is shared for object, false otherwise
"""
# TODO this could be improved/cleaned up by adding share checks for relevant objects
if not user.is_authenticated:
return False
if user.is_superuser:
return True
return user in obj.shared.all()
def share_link_valid(recipe, share):
"""
Verifies the validity of a share uuid
@ -147,6 +164,21 @@ class CustomIsOwner(permissions.BasePermission):
return is_object_owner(request.user, obj)
class CustomIsShared(permissions.BasePermission): # TODO function duplicate name
"""
Custom permission class for django rest framework views
verifies user is shared for the object he is trying to access
"""
message = _('You cannot interact with this object as its not owned by you!')
def has_permission(self, request, view):
return request.user.is_authenticated
def has_object_permission(self, request, view, obj):
print("called is shared")
return is_object_shared(request.user, obj)
class CustomIsGuest(permissions.BasePermission):
"""
Custom permission class for django rest framework views

View File

@ -0,0 +1,27 @@
import json
from django.contrib import auth
from django.db.models import ProtectedError
from django.urls import reverse
from cookbook.models import Storage, Sync, Keyword, ShoppingList
from cookbook.tests.views.test_views import TestViews
class TestApiShopping(TestViews):
def setUp(self):
super(TestApiShopping, self).setUp()
self.list_1 = ShoppingList.objects.create(created_by=auth.get_user(self.user_client_1))
self.list_2 = ShoppingList.objects.create(created_by=auth.get_user(self.user_client_2))
def test_shopping_view_permissions(self):
self.batch_requests([(self.anonymous_client, 403), (self.guest_client_1, 404), (self.user_client_1, 200), (self.user_client_2, 404), (self.admin_client_1, 404), (self.superuser_client, 200)],
reverse('api:shoppinglist-detail', args={self.list_1.id}))
self.list_1.shared.add(auth.get_user(self.user_client_2))
self.batch_requests([(self.anonymous_client, 403), (self.guest_client_1, 404), (self.user_client_1, 200), (self.user_client_2, 200), (self.admin_client_1, 404), (self.superuser_client, 200)],
reverse('api:shoppinglist-detail', args={self.list_1.id}))
# TODO add tests for editing

View File

@ -26,7 +26,7 @@ from rest_framework.parsers import JSONParser, FileUploadParser, MultiPartParser
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser, CustomIsGuest, CustomIsShare
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser, CustomIsGuest, CustomIsShare, CustomIsShared
from cookbook.helper.recipe_url_import import get_from_html
from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword, Unit, SyncLog, ShoppingListRecipe, ShoppingList, ShoppingListEntry
from cookbook.provider.dropbox import Dropbox
@ -155,7 +155,7 @@ class MealPlanViewSet(viewsets.ModelViewSet):
"""
queryset = MealPlan.objects.all()
serializer_class = MealPlanSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated] # TODO fix permissions
def get_queryset(self):
queryset = MealPlan.objects.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).distinct().all()
@ -244,7 +244,7 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
queryset = ShoppingListRecipe.objects.all()
serializer_class = ShoppingListRecipeSerializer
permission_classes = [CustomIsUser] # TODO add custom validation
permission_classes = [CustomIsUser, ] # TODO add custom validation
# TODO custom get qs
@ -252,7 +252,7 @@ class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
class ShoppingListEntryViewSet(viewsets.ModelViewSet):
queryset = ShoppingListEntry.objects.all()
serializer_class = ShoppingListEntrySerializer
permission_classes = [CustomIsOwner] # TODO add custom validation
permission_classes = [CustomIsOwner, ] # TODO add custom validation
# TODO custom get qs
@ -260,11 +260,12 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
class ShoppingListViewSet(viewsets.ModelViewSet):
queryset = ShoppingList.objects.all()
serializer_class = ShoppingListSerializer
permission_classes = [CustomIsOwner]
permission_classes = [CustomIsOwner | CustomIsShared]
def get_queryset(self):
queryset = self.queryset.filter(created_by=self.request.user).all()
return queryset
if self.request.user.is_superuser:
return self.queryset
return self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).all()
def get_serializer_class(self):
autosync = self.request.query_params.get('autosync', None)

View File

@ -1,6 +1,7 @@
from datetime import datetime
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from django.db.models.functions import Lower
from django.shortcuts import render
from django.utils.translation import gettext as _
@ -49,7 +50,7 @@ def food(request):
@group_required('user')
def shopping_list(request):
f = ShoppingListFilter(request.GET, queryset=ShoppingList.objects.filter(created_by=request.user).all().order_by('finished', 'created_at'))
f = ShoppingListFilter(request.GET, queryset=ShoppingList.objects.filter(Q(created_by=request.user) | Q(shared=request.user)).all().order_by('finished', 'created_at'))
table = ShoppingListTable(f.qs)
RequestConfig(request, paginate={'per_page': 25}).configure(table)