testes and fixes for space, userspace and invitelink apis

This commit is contained in:
vabene1111 2022-06-09 22:02:58 +02:00
parent 30e4ee855c
commit deeed4b65b
8 changed files with 378 additions and 12 deletions

View File

@ -6,6 +6,7 @@
<w>csrftoken</w>
<w>gunicorn</w>
<w>ical</w>
<w>invitelink</w>
<w>mealie</w>
<w>pepperplate</w>
<w>safron</w>

View File

@ -300,6 +300,9 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
def get_owner(self):
return self.created_by
def get_space(self):
return self
def __str__(self):
return self.name

View File

@ -200,7 +200,7 @@ class UserSpaceSerializer(WritableNestedModelSerializer):
groups = GroupSerializer(many=True)
def validate(self, data):
if self.instance.user == self.context['request'].space.created_by: # cant change space owner permission
if self.instance.user == self.context['request'].space.created_by: # can't change space owner permission
raise serializers.ValidationError(_('Cannot modify Space owner permission.'))
return super().validate(data)

View File

@ -0,0 +1,119 @@
import json
import pytest
from django.contrib import auth
from django.db.models import OuterRef, Subquery
from django.urls import reverse
from django_scopes import scopes_disabled
from cookbook.models import Ingredient, Step, InviteLink
LIST_URL = 'api:invitelink-list'
DETAIL_URL = 'api:invitelink-detail'
@pytest.mark.parametrize("arg", [
['a_u', 403, 0],
['g1_s1', 403, 0],
['u1_s1', 403, 0],
['a1_s1', 200, 1],
['a2_s1', 200, 0],
])
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.save()
InviteLink.objects.create(group_id=1, created_by=auth.get_user(a1_s1), space=space_1)
c = request.getfixturevalue(arg[0])
result = c.get(reverse(LIST_URL))
assert result.status_code == arg[1]
if arg[1] == 200:
assert len(json.loads(result.content)) == arg[2]
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 403],
['u1_s1', 403],
['a1_s1', 200],
['g1_s2', 403],
['u1_s2', 403],
['a1_s2', 404],
])
def test_update(arg, request, space_1, u1_s1, a1_s1):
with scopes_disabled():
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
il = InviteLink.objects.create(group_id=1, created_by=auth.get_user(a1_s1), space=space_1)
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={il.id}
),
{'email': 'test@mail.de'},
content_type='application/json'
)
response = json.loads(r.content)
print(response)
assert r.status_code == arg[1]
if r.status_code == 200:
assert response['email'] == 'test@mail.de'
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 403],
['u1_s1', 403],
['a1_s1', 201],
['a2_s1', 403],
])
def test_add(arg, request, a1_s1, space_1):
with scopes_disabled():
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'group': {'id': 3, 'name': 'admin'}},
content_type='application/json'
)
print(r.content)
assert r.status_code == arg[1]
def test_delete(u1_s1, u1_s2, a1_s1, a2_s1, space_1):
with scopes_disabled():
il = InviteLink.objects.create(group_id=1, created_by=auth.get_user(a1_s1), space=space_1)
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
# user cant delete
r = u1_s1.delete(
reverse(
DETAIL_URL,
args={il.id}
)
)
assert r.status_code == 403
# admin cant delete
r = a2_s1.delete(
reverse(
DETAIL_URL,
args={il.id}
)
)
assert r.status_code == 404
# owner can delete
r = a1_s1.delete(
reverse(
DETAIL_URL,
args={il.id}
)
)
assert r.status_code == 204

View File

@ -0,0 +1,102 @@
import json
import pytest
from django.contrib import auth
from django.db.models import OuterRef, Subquery
from django.urls import reverse
from django_scopes import scopes_disabled
from cookbook.models import Ingredient, Step
LIST_URL = 'api:space-list'
DETAIL_URL = 'api:space-detail'
@pytest.mark.parametrize("arg", [
['a_u', 403, 0],
['g1_s1', 403, 0],
['u1_s1', 403, 0],
['a1_s1', 200, 1],
['a2_s1', 200, 0],
])
def test_list_permission(arg, request, space_1, a1_s1):
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
c = request.getfixturevalue(arg[0])
result = c.get(reverse(LIST_URL))
assert result.status_code == arg[1]
if arg[1] == 200:
assert len(json.loads(result.content)) == arg[2]
def test_list_permission_owner(u1_s1, space_1):
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 0
space_1.created_by = auth.get_user(u1_s1)
space_1.save()
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 404],
['u1_s1', 404],
['a1_s1', 200],
['g1_s2', 404],
['u1_s2', 404],
['a1_s2', 404],
])
def test_update(arg, request, space_1, a1_s1):
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
with scopes_disabled():
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={space_1.id}
),
{'message': 'new'},
content_type='application/json'
)
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
assert response['message'] == 'new'
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 405],
['u1_s1', 405],
['a1_s1', 405],
])
def test_add(arg, request, u1_s2):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'name': 'test'},
content_type='application/json'
)
assert r.status_code == arg[1]
def test_delete(u1_s1, u1_s2, a1_s1, space_1):
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
# user cannot delete space
r = u1_s1.delete(
reverse(
DETAIL_URL,
args={space_1.id}
)
)
assert r.status_code == 405
# 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(
reverse(
DETAIL_URL,
args={space_1.id}
)
)
assert r.status_code == 204

View File

@ -0,0 +1,113 @@
import json
import pytest
from django.contrib import auth
from django.db.models import OuterRef, Subquery
from django.urls import reverse
from django_scopes import scopes_disabled
from cookbook.models import Ingredient, Step
LIST_URL = 'api:userspace-list'
DETAIL_URL = 'api:userspace-detail'
@pytest.mark.parametrize("arg", [
['a_u', 403, 0],
['g1_s1', 200, 1], # sees only own user space
['u1_s1', 200, 1],
['a1_s1', 200, 3], # sees user space of all users in space
['a2_s1', 200, 1],
])
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.save()
c = request.getfixturevalue(arg[0])
result = c.get(reverse(LIST_URL))
assert result.status_code == arg[1]
if arg[1] == 200:
assert len(json.loads(result.content)) == arg[2]
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 403],
['u1_s1', 403],
['a1_s1', 200],
['g1_s2', 403],
['u1_s2', 403],
['a1_s2', 403],
])
def test_update(arg, request, space_1, u1_s1, a1_s1):
with scopes_disabled():
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
user_space = auth.get_user(u1_s1).userspace_set.first()
c = request.getfixturevalue(arg[0])
r = c.patch(
reverse(
DETAIL_URL,
args={user_space.id}
),
{'groups': [{'id': 3, 'name': 'admin'}]},
content_type='application/json'
)
response = json.loads(r.content)
assert r.status_code == arg[1]
if r.status_code == 200:
assert response['groups'] == [{'id': 3, 'name': 'admin'}]
def test_update_space_owner(a1_s1, space_1):
# space owners cannot modify their own permission so that they can't lock themselves out
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
r = a1_s1.patch(
reverse(
DETAIL_URL,
args={auth.get_user(a1_s1).userspace_set.first().id}
),
{'groups': [{'id': 2, 'name': 'user'}]},
content_type='application/json'
)
assert r.status_code == 400
@pytest.mark.parametrize("arg", [
['a_u', 403],
['g1_s1', 405],
['u1_s1', 405],
['a1_s1', 405],
])
def test_add(arg, request, u1_s1, space_1):
c = request.getfixturevalue(arg[0])
r = c.post(
reverse(LIST_URL),
{'user': {'id': auth.get_user(u1_s1).id, 'space': space_1.id}},
content_type='application/json'
)
assert r.status_code == arg[1]
def test_delete(u1_s1, u1_s2, a1_s1, space_1):
space_1.created_by = auth.get_user(a1_s1)
space_1.save()
r = u1_s1.delete(
reverse(
DETAIL_URL,
args={auth.get_user(u1_s1).userspace_set.first().id}
)
)
assert r.status_code == 403
r = a1_s1.delete(
reverse(
DETAIL_URL,
args={auth.get_user(u1_s1).userspace_set.first().id}
)
)
assert r.status_code == 204

View File

@ -6,8 +6,8 @@ from django_scopes import scopes_disabled
from cookbook.forms import ImportExportBase
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.permission_helper import has_group_permission, is_space_owner, switch_user_active_space
from cookbook.models import ExportLog, UserSpace, Food
from cookbook.helper.permission_helper import has_group_permission, is_space_owner, switch_user_active_space, is_object_owner
from cookbook.models import ExportLog, UserSpace, Food, Space, Comment, RecipeBook, RecipeBookEntry
def test_has_group_permission(u1_s1, a_u, space_2):
@ -38,6 +38,30 @@ def test_has_group_permission(u1_s1, a_u, space_2):
assert not has_group_permission(auth.get_user(a_u), ('admin',))
def test_is_owner(u1_s1, u2_s1, u1_s2, a_u, space_1, recipe_1_s1):
with scopes_disabled():
s = Space.objects.create(name='Test', created_by=auth.get_user(u1_s1))
assert is_object_owner(auth.get_user(u1_s1), s)
assert not is_object_owner(auth.get_user(u2_s1), s)
assert not is_object_owner(auth.get_user(u1_s2), s)
assert not is_object_owner(auth.get_user(a_u), s)
rb = RecipeBook.objects.create(name='Test', created_by=auth.get_user(u1_s1), space=space_1)
assert is_object_owner(auth.get_user(u1_s1), rb)
assert not is_object_owner(auth.get_user(u2_s1), rb)
assert not is_object_owner(auth.get_user(u1_s2), rb)
assert not is_object_owner(auth.get_user(a_u), rb)
rbe = RecipeBookEntry.objects.create(book=rb, recipe=recipe_1_s1)
assert is_object_owner(auth.get_user(u1_s1), rbe)
assert not is_object_owner(auth.get_user(u2_s1), rbe)
assert not is_object_owner(auth.get_user(u1_s2), rbe)
assert not is_object_owner(auth.get_user(a_u), rbe)
def test_is_space_owner(u1_s1, u2_s1, space_1, space_2):
with scopes_disabled():
f = Food.objects.create(name='Test', space=space_1)
@ -70,5 +94,3 @@ def test_switch_user_active_space(u1_s1, u1_s2, space_1, space_2):
# can switch into newly created space
assert switch_user_active_space(auth.get_user(u1_s1), space_2) == us

View File

@ -45,7 +45,7 @@ from cookbook.helper.image_processing import handle_image
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, CustomIsOwner,
CustomIsShare, CustomIsShared, CustomIsUser,
group_required, CustomIsSpaceOwner, switch_user_active_space)
group_required, CustomIsSpaceOwner, switch_user_active_space, is_space_owner)
from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
@ -376,11 +376,11 @@ class GroupViewSet(viewsets.ModelViewSet):
class SpaceViewSet(viewsets.ModelViewSet):
queryset = Space.objects
serializer_class = SpaceSerializer
permission_classes = [CustomIsOwner]
permission_classes = [CustomIsOwner & CustomIsAdmin]
http_method_names = ['get', 'patch']
def get_queryset(self):
return self.queryset.filter(id=self.request.space.id)
return self.queryset.filter(id=self.request.space.id, created_by=self.request.user)
class UserSpaceViewSet(viewsets.ModelViewSet):
@ -395,7 +395,10 @@ class UserSpaceViewSet(viewsets.ModelViewSet):
return super().destroy(request, *args, **kwargs)
def get_queryset(self):
return self.queryset.filter(user=self.request.user)
if is_space_owner(self.request.user, self.request.space):
return self.queryset.filter(space=self.request.space)
else:
return self.queryset.filter(user=self.request.user, space=self.request.space)
class UserPreferenceViewSet(viewsets.ModelViewSet):
@ -1055,11 +1058,14 @@ class AutomationViewSet(viewsets.ModelViewSet, StandardFilterMixin):
class InviteLinkViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = InviteLink.objects
serializer_class = InviteLinkSerializer
permission_classes = [CustomIsSpaceOwner]
permission_classes = [CustomIsSpaceOwner & CustomIsAdmin]
def get_queryset(self):
self.queryset = self.queryset.filter(space=self.request.space).all()
return super().get_queryset()
if is_space_owner(self.request.user, self.request.space):
self.queryset = self.queryset.filter(space=self.request.space).all()
return super().get_queryset()
else:
return None
class CustomFilterViewSet(viewsets.ModelViewSet, StandardFilterMixin):