fixed permissions and tests

This commit is contained in:
vabene1111 2022-06-14 16:20:37 +02:00
parent f02eac8ac6
commit e7de6f62b6
5 changed files with 35 additions and 21 deletions

View File

@ -206,6 +206,14 @@ class CustomIsOwner(permissions.BasePermission):
return is_object_owner(request.user, obj) return is_object_owner(request.user, obj)
class CustomIsOwnerReadOnly(CustomIsOwner):
def has_permission(self, request, view):
return super().has_permission(request, view) and request.method in SAFE_METHODS
def has_object_permission(self, request, view, obj):
return super().has_object_permission(request, view) and request.method in SAFE_METHODS
class CustomIsSpaceOwner(permissions.BasePermission): class CustomIsSpaceOwner(permissions.BasePermission):
""" """
Custom permission class for django rest framework views Custom permission class for django rest framework views
@ -214,7 +222,7 @@ class CustomIsSpaceOwner(permissions.BasePermission):
message = _('You cannot interact with this object as it is not owned by you!') message = _('You cannot interact with this object as it is not owned by you!')
def has_permission(self, request, view): def has_permission(self, request, view):
return request.user.is_authenticated return request.user.is_authenticated and request.space.created_by == request.user
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
return is_space_owner(request.user, obj) return is_space_owner(request.user, obj)

View File

@ -2,11 +2,13 @@ import json
import pytest import pytest
from django.contrib import auth from django.contrib import auth
from django.contrib.auth.models import AnonymousUser
from django.db.models import OuterRef, Subquery from django.db.models import OuterRef, Subquery
from django.urls import reverse from django.urls import reverse
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from cookbook.models import Ingredient, Step, InviteLink from cookbook.helper.permission_helper import switch_user_active_space
from cookbook.models import Ingredient, Step, InviteLink, UserSpace
LIST_URL = 'api:invitelink-list' LIST_URL = 'api:invitelink-list'
DETAIL_URL = 'api:invitelink-detail' DETAIL_URL = 'api:invitelink-detail'
@ -17,7 +19,7 @@ DETAIL_URL = 'api:invitelink-detail'
['g1_s1', 403, 0], ['g1_s1', 403, 0],
['u1_s1', 403, 0], ['u1_s1', 403, 0],
['a1_s1', 200, 1], ['a1_s1', 200, 1],
['a2_s1', 200, 0], ['a2_s1', 403, 0],
]) ])
def test_list_permission(arg, request, space_1, g1_s1, u1_s1, a1_s1): def test_list_permission(arg, request, space_1, g1_s1, u1_s1, a1_s1):
space_1.created_by = auth.get_user(a1_s1) space_1.created_by = auth.get_user(a1_s1)
@ -38,7 +40,7 @@ def test_list_permission(arg, request, space_1, g1_s1, u1_s1, a1_s1):
['a1_s1', 200], ['a1_s1', 200],
['g1_s2', 403], ['g1_s2', 403],
['u1_s2', 403], ['u1_s2', 403],
['a1_s2', 404], ['a1_s2', 403],
]) ])
def test_update(arg, request, space_1, u1_s1, a1_s1): def test_update(arg, request, space_1, u1_s1, a1_s1):
with scopes_disabled(): with scopes_disabled():
@ -75,6 +77,7 @@ def test_add(arg, request, a1_s1, space_1):
space_1.created_by = auth.get_user(a1_s1) space_1.created_by = auth.get_user(a1_s1)
space_1.save() space_1.save()
c = request.getfixturevalue(arg[0]) c = request.getfixturevalue(arg[0])
r = c.post( r = c.post(
reverse(LIST_URL), reverse(LIST_URL),
{'group': {'id': 3, 'name': 'admin'}}, {'group': {'id': 3, 'name': 'admin'}},
@ -107,7 +110,7 @@ def test_delete(u1_s1, u1_s2, a1_s1, a2_s1, space_1):
args={il.id} args={il.id}
) )
) )
assert r.status_code == 404 assert r.status_code == 403
# owner can delete # owner can delete
r = a1_s1.delete( r = a1_s1.delete(

View File

@ -29,20 +29,23 @@ def test_list_permission(arg, request, space_1, a1_s1):
assert len(json.loads(result.content)) == arg[2] assert len(json.loads(result.content)) == arg[2]
def test_list_permission_owner(u1_s1, space_1): def test_list_permission_owner(u1_s1, a1_s1, space_1):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 0 space_1.created_by = auth.get_user(a1_s1)
space_1.save()
assert len(json.loads(a1_s1.get(reverse(LIST_URL)).content)) == 1
assert u1_s1.get(reverse(LIST_URL)).status_code == 403
space_1.created_by = auth.get_user(u1_s1) space_1.created_by = auth.get_user(u1_s1)
space_1.save() space_1.save()
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1 assert u1_s1.get(reverse(LIST_URL)).status_code == 403
@pytest.mark.parametrize("arg", [ @pytest.mark.parametrize("arg", [
['a_u', 403], ['a_u', 403],
['g1_s1', 404], ['g1_s1', 403],
['u1_s1', 404], ['u1_s1', 403],
['a1_s1', 200], ['a1_s1', 200],
['g1_s2', 404], ['g1_s2', 403],
['u1_s2', 404], ['u1_s2', 403],
['a1_s2', 404], ['a1_s2', 404],
]) ])
def test_update(arg, request, space_1, a1_s1): def test_update(arg, request, space_1, a1_s1):
@ -66,8 +69,8 @@ def test_update(arg, request, space_1, a1_s1):
@pytest.mark.parametrize("arg", [ @pytest.mark.parametrize("arg", [
['a_u', 403], ['a_u', 403],
['g1_s1', 405], ['g1_s1', 403],
['u1_s1', 405], ['u1_s1', 403],
['a1_s1', 405], ['a1_s1', 405],
]) ])
def test_add(arg, request, u1_s2): def test_add(arg, request, u1_s2):
@ -90,7 +93,7 @@ def test_delete(u1_s1, u1_s2, a1_s1, space_1):
args={space_1.id} args={space_1.id}
) )
) )
assert r.status_code == 405 assert r.status_code == 403
# event the space owner cannot delete his space over the api (this might change later but for now it's only available in the UI) # event the space owner cannot delete his space over the api (this might change later but for now it's only available in the UI)
r = a1_s1.delete( r = a1_s1.delete(
@ -99,4 +102,4 @@ def test_delete(u1_s1, u1_s2, a1_s1, space_1):
args={space_1.id} args={space_1.id}
) )
) )
assert r.status_code == 204 assert r.status_code == 405

View File

@ -78,9 +78,9 @@ def test_update_space_owner(a1_s1, space_1):
@pytest.mark.parametrize("arg", [ @pytest.mark.parametrize("arg", [
['a_u', 403], ['a_u', 403],
['g1_s1', 405], ['g1_s1', 403],
['u1_s1', 405], ['u1_s1', 403],
['a1_s1', 405], ['a1_s1', 403],
]) ])
def test_add(arg, request, u1_s1, space_1): def test_add(arg, request, u1_s1, space_1):
c = request.getfixturevalue(arg[0]) c = request.getfixturevalue(arg[0])

View File

@ -45,7 +45,7 @@ from cookbook.helper.image_processing import handle_image
from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, CustomIsOwner, from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, CustomIsOwner,
CustomIsShare, CustomIsShared, CustomIsUser, CustomIsShare, CustomIsShared, CustomIsUser,
group_required, CustomIsSpaceOwner, switch_user_active_space, is_space_owner) group_required, CustomIsSpaceOwner, switch_user_active_space, is_space_owner, CustomIsOwnerReadOnly)
from cookbook.helper.recipe_html_import import get_recipe_from_source from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
@ -386,7 +386,7 @@ class SpaceViewSet(viewsets.ModelViewSet):
class UserSpaceViewSet(viewsets.ModelViewSet): class UserSpaceViewSet(viewsets.ModelViewSet):
queryset = UserSpace.objects queryset = UserSpace.objects
serializer_class = UserSpaceSerializer serializer_class = UserSpaceSerializer
permission_classes = [CustomIsSpaceOwner] permission_classes = [CustomIsSpaceOwner | CustomIsOwnerReadOnly]
http_method_names = ['get', 'patch', 'delete'] http_method_names = ['get', 'patch', 'delete']
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):