testes and fixes for space, userspace and invitelink apis
This commit is contained in:
parent
30e4ee855c
commit
deeed4b65b
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
119
cookbook/tests/api/test_api_invitelinke.py
Normal file
119
cookbook/tests/api/test_api_invitelinke.py
Normal 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
|
102
cookbook/tests/api/test_api_space.py
Normal file
102
cookbook/tests/api/test_api_space.py
Normal 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
|
113
cookbook/tests/api/test_api_userspace.py
Normal file
113
cookbook/tests/api/test_api_userspace.py
Normal 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
|
@ -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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user