diff --git a/apps/comment/serializers/back.py b/apps/comment/serializers/back.py index d0cd47c8..325086c0 100644 --- a/apps/comment/serializers/back.py +++ b/apps/comment/serializers/back.py @@ -6,4 +6,4 @@ from rest_framework import serializers class CommentBaseSerializer(serializers.ModelSerializer): class Meta: model = models.Comment - fields = ('id', 'text', 'mark', 'user') \ No newline at end of file + fields = ('id', 'text', 'mark', 'user', 'object_id', 'content_type') \ No newline at end of file diff --git a/apps/comment/tests.py b/apps/comment/tests.py index bd22214a..ff3bd393 100644 --- a/apps/comment/tests.py +++ b/apps/comment/tests.py @@ -30,18 +30,48 @@ class CommentModeratorPermissionTests(BasePermissionTests): ) self.userRole.save() - content_type = ContentType.objects.get(app_label='location', model='country') + self.content_type = ContentType.objects.get(app_label='location', model='country') self.user_test = get_tokens_for_user() self.comment = Comment.objects.create(text='Test comment', mark=1, user=self.user_test["user"], - object_id= self.country_ru.pk, - content_type_id=content_type.id, + object_id=self.country_ru.pk, + content_type_id=self.content_type.id, country=self.country_ru ) self.comment.save() self.url = reverse('back:comment:comment-crud', kwargs={"id": self.comment.id}) + def test_post(self): + self.url = reverse('back:comment:comment-list-create') + + comment = { + "text": "Test comment POST", + "user": self.user_test["user"].id, + "object_id": self.country_ru.pk, + "content_type": self.content_type.id, + "country_id": self.country_ru.id + } + + response = self.client.post(self.url, format='json', data=comment) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + comment = { + "text": "Test comment POST moder", + "user": self.moderator.id, + "object_id": self.country_ru.id, + "content_type": self.content_type.id, + "country_id": self.country_ru.id + } + + tokens = User.create_jwt_tokens(self.moderator) + self.client.cookies = SimpleCookie( + {'access_token': tokens.get('access_token'), + 'refresh_token': tokens.get('access_token')}) + + response = self.client.post(self.url, format='json', data=comment) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + def test_put_moderator(self): tokens = User.create_jwt_tokens(self.moderator) self.client.cookies = SimpleCookie( @@ -52,7 +82,9 @@ class CommentModeratorPermissionTests(BasePermissionTests): "id": self.comment.id, "text": "test text moderator", "mark": 1, - "user": self.moderator.id + "user": self.moderator.id, + "object_id": self.comment.country_id, + "content_type": self.content_type.id } response = self.client.put(self.url, data=data, format='json') @@ -60,7 +92,7 @@ class CommentModeratorPermissionTests(BasePermissionTests): def test_get(self): response = self.client.get(self.url, format='json') - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(response.status_code, status.HTTP_200_OK) def test_put_other_user(self): other_user = User.objects.create_user(username='test', @@ -99,9 +131,10 @@ class CommentModeratorPermissionTests(BasePermissionTests): "id": self.comment.id, "text": "test text moderator", "mark": 1, - "user": super_user.id + "user": super_user.id, + "object_id": self.country_ru.id, + "content_type": self.content_type.id, } - response = self.client.put(self.url, data=data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/apps/comment/views/back.py b/apps/comment/views/back.py index 2895fdbe..3b96cbd2 100644 --- a/apps/comment/views/back.py +++ b/apps/comment/views/back.py @@ -8,12 +8,13 @@ class CommentLstView(generics.ListCreateAPIView): """Comment list create view.""" serializer_class = serializers.CommentBaseSerializer queryset = models.Comment.objects.all() - permission_classes = [permissions.IsAuthenticatedOrReadOnly,] + permission_classes = [permissions.IsAuthenticatedOrReadOnly| IsCommentModerator|IsCountryAdmin] class CommentRUDView(generics.RetrieveUpdateDestroyAPIView): """Comment RUD view.""" serializer_class = serializers.CommentBaseSerializer queryset = models.Comment.objects.all() - permission_classes = [IsCountryAdmin|IsCommentModerator] + + permission_classes = [IsCountryAdmin | IsCommentModerator] lookup_field = 'id' diff --git a/apps/location/serializers/back.py b/apps/location/serializers/back.py index f25aacf6..c178f7fd 100644 --- a/apps/location/serializers/back.py +++ b/apps/location/serializers/back.py @@ -16,4 +16,5 @@ class CountryBackSerializer(common.CountrySerializer): 'code', 'svg_image', 'name', + 'country_id' ] diff --git a/apps/location/tests.py b/apps/location/tests.py index 4e192831..3eaefd85 100644 --- a/apps/location/tests.py +++ b/apps/location/tests.py @@ -20,6 +20,7 @@ class BaseTestCase(APITestCase): username=self.username, email=self.email, password=self.password) tokens = User.create_jwt_tokens(self.user) + self.client.cookies = SimpleCookie( {'access_token': tokens.get('access_token'), 'refresh_token': tokens.get('refresh_token')}) diff --git a/apps/location/views/back.py b/apps/location/views/back.py index cb8246a4..bb64ff72 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -4,44 +4,48 @@ from rest_framework import generics from location import models, serializers from location.views import common from utils.permissions import IsCountryAdmin - +from rest_framework.permissions import IsAuthenticatedOrReadOnly # Address + + class AddressListCreateView(common.AddressViewMixin, generics.ListCreateAPIView): """Create view for model Address.""" serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] class AddressRUDView(common.AddressViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model Address.""" serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] # City class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView): """Create view for model City.""" serializer_class = serializers.CitySerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] + class CityRUDView(common.CityViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model City.""" serializer_class = serializers.CitySerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] # Region class RegionListCreateView(common.RegionViewMixin, generics.ListCreateAPIView): """Create view for model Region""" serializer_class = serializers.RegionSerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] + class RegionRUDView(common.RegionViewMixin, generics.RetrieveUpdateDestroyAPIView): """Retrieve view for model Region""" serializer_class = serializers.RegionSerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] # Country @@ -50,10 +54,11 @@ class CountryListCreateView(generics.ListCreateAPIView): queryset = models.Country.objects.all() serializer_class = serializers.CountryBackSerializer pagination_class = None - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] + class CountryRUDView(generics.RetrieveUpdateDestroyAPIView): """RUD view for model Country.""" serializer_class = serializers.CountryBackSerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin] queryset = models.Country.objects.all() \ No newline at end of file diff --git a/apps/news/tests.py b/apps/news/tests.py index 115763e5..77dbca8e 100644 --- a/apps/news/tests.py +++ b/apps/news/tests.py @@ -66,6 +66,22 @@ class NewsTestCase(BaseTestCase): def setUp(self): super().setUp() + def test_news_post(self): + test_news ={ + "title": {"en-GB": "Test news POST"}, + "news_type_id": self.test_news_type.id, + "description": {"en-GB": "Description test news"}, + "start": datetime.now() + timedelta(hours=-2), + "end": datetime.now() + timedelta(hours=2), + "state": News.PUBLISHED, + "slug": 'test-news-slug_post', + "country_id": self.country_ru.id, + } + + url = reverse("back:news:list-create") + response = self.client.post(url, data=test_news, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + def test_web_news(self): response = self.client.get(reverse('web:news:list')) self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 45d978a0..86a4be6f 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -20,8 +20,8 @@ class IsAuthenticatedAndTokenIsValid(permissions.BasePermission): access_token = request.COOKIES.get('access_token') if user.is_authenticated and access_token: access_token = AccessToken(access_token) - valid_tokens = user.access_tokens.valid()\ - .by_jti(jti=access_token.payload.get('jti')) + valid_tokens = user.access_tokens.valid() \ + .by_jti(jti=access_token.payload.get('jti')) return valid_tokens.exists() else: return False @@ -31,13 +31,14 @@ class IsRefreshTokenValid(permissions.BasePermission): """ Check if user has a valid refresh token and authenticated """ + def has_permission(self, request, view): """Check permissions by refresh token and default REST permission IsAuthenticated""" refresh_token = request.COOKIES.get('refresh_token') if refresh_token: refresh_token = GMRefreshToken(refresh_token) - refresh_token_qs = JWTRefreshToken.objects.valid()\ - .by_jti(jti=refresh_token.payload.get('jti')) + refresh_token_qs = JWTRefreshToken.objects.valid() \ + .by_jti(jti=refresh_token.payload.get('jti')) return refresh_token_qs.exists() else: return False @@ -55,11 +56,15 @@ class IsGuest(permissions.IsAuthenticatedOrReadOnly): """ Object-level permission to only allow owners of an object to edit it. """ + def has_permission(self, request, view): - return request.user.is_authenticated + rules = [ + request.user.is_superuser, + request.method in permissions.SAFE_METHODS + ] + return any(rules) def has_object_permission(self, request, view, obj): - rules = [ request.user.is_superuser, request.method in permissions.SAFE_METHODS @@ -72,6 +77,21 @@ class IsStandardUser(IsGuest): Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. """ + + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + + # and request.user.email_confirmed, + if hasattr(request, 'user'): + rules = [ + request.user.is_authenticated, + super().has_permission(request, view) + ] + + return any(rules) + def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request rules = [ @@ -92,11 +112,29 @@ class IsContentPageManager(IsStandardUser): Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. """ + + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + # and request.user.email_confirmed, + if hasattr(request, 'user'): + role = Role.objects.filter(role=Role.CONTENT_PAGE_MANAGER, + country_id=request.country_id) \ + .first() # 'Comments moderator' + + rules = [ + UserRole.objects.filter(user=request.user, role=role).exists(), + # and obj.user != request.user, + super().has_permission(request, view) + ] + return any(rules) + def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request. role = Role.objects.filter(role=Role.CONTENT_PAGE_MANAGER, - country_id=obj.country_id)\ + country_id=obj.country_id) \ .first() # 'Comments moderator' rules = [ @@ -112,6 +150,26 @@ class IsCountryAdmin(IsStandardUser): Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. """ + + def has_permission(self, request, view): + + rules = [ + super().has_permission(request, view) + ] + # and request.user.email_confirmed, + if hasattr(request.data, 'user') and hasattr(request.data, 'country_id'): + # Read permissions are allowed to any request. + + role = Role.objects.filter(role=Role.COUNTRY_ADMIN, + country_id=request.data.country_id) \ + .first() # 'Comments moderator' + + rules = [ + UserRole.objects.filter(user=request.user, role=role).exists(), + super().has_permission(request, view) + ] + return any(rules) + def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request. role = Role.objects.filter(role=Role.COUNTRY_ADMIN, @@ -119,9 +177,20 @@ class IsCountryAdmin(IsStandardUser): .first() # 'Comments moderator' rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_object_permission(request, view, obj), + super().has_object_permission(request, view, obj) ] + # and request.user.email_confirmed, + if hasattr(request, 'user') and request.user.is_authenticated: + rules = [ + UserRole.objects.filter(user=request.user, role=role).exists(), + super().has_object_permission(request, view, obj), + ] + + if hasattr(request.data, 'user'): + rules = [ + UserRole.objects.filter(user=request.data.user, role=role).exists(), + super().has_object_permission(request, view, obj), + ] return any(rules) @@ -131,10 +200,31 @@ class IsCommentModerator(IsStandardUser): Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. """ + + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + + # and request.user.email_confirmed, + if hasattr(request.data, 'user') and hasattr(request.data, 'country_id'): + # Read permissions are allowed to any request. + + role = Role.objects.filter(role=Role.COMMENTS_MODERATOR, + country_id=request.data.country_id) \ + .first() # 'Comments moderator' + + rules = [ + UserRole.objects.filter(user=request.user, role=role).exists(), + super().has_permission(request, view) + ] + + return any(rules) + def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request. role = Role.objects.filter(role=Role.COMMENTS_MODERATOR, - country_id=obj.country_id)\ + country_id=obj.country_id) \ .first() # 'Comments moderator' rules = [ @@ -147,10 +237,28 @@ class IsCommentModerator(IsStandardUser): class IsEstablishmentManager(IsStandardUser): - def has_object_permission(self, request, view, obj): - role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER)\ + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + + # and request.user.email_confirmed, + if hasattr(request.data, 'user') and hasattr(request.data, 'establishment_id'): + role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER) \ .first() # 'Comments moderator' + rules = [ + UserRole.objects.filter(user=request.user, role=role, + establishment_id=request.data.establishment_id + ).exists(), + super().has_permission(request, view) + ] + return any(rules) + + def has_object_permission(self, request, view, obj): + role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER) \ + .first() # 'Comments moderator' + rules = [ UserRole.objects.filter(user=request.user, role=role, establishment_id=obj.establishment_id @@ -163,11 +271,28 @@ class IsEstablishmentManager(IsStandardUser): class IsReviewerManager(IsStandardUser): - def has_object_permission(self, request, view, obj): + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + # and request.user.email_confirmed, + if hasattr(request.data, 'user') and hasattr(request.data, 'country_id'): + role = Role.objects.filter(role=Role.REVIEWER_MANGER) \ + .first() # 'Comments moderator' + + rules = [ + UserRole.objects.filter(user=request.user, role=role, + establishment_id=request.data.country_id + ).exists(), + super().has_permission(request, view) + ] + return any(rules) + + def has_object_permission(self, request, view, obj): role = Role.objects.filter(role=Role.REVIEWER_MANGER, - country_id=obj.country_id)\ - .first() + country_id=obj.country_id) \ + .first() rules = [ UserRole.objects.filter(user=request.user, role=role).exists(), @@ -179,8 +304,25 @@ class IsReviewerManager(IsStandardUser): class IsRestaurantReviewer(IsStandardUser): - def has_object_permission(self, request, view, obj): + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + # and request.user.email_confirmed, + if hasattr(request.data, 'user') and hasattr(request.data, 'object_id'): + role = Role.objects.filter(role=Role.RESTAURANT_REVIEWER) \ + .first() # 'Comments moderator' + + rules = [ + UserRole.objects.filter(user=request.user, role=role, + establishment_id=request.data.object_id + ).exists(), + super().has_permission(request, view) + ] + return any(rules) + + def has_object_permission(self, request, view, obj): content_type = ContentType.objects.get(app_lable='establishment', model='establishment') diff --git a/apps/utils/tests/tests_permissions.py b/apps/utils/tests/tests_permissions.py index edc1a5d7..3bba7b7d 100644 --- a/apps/utils/tests/tests_permissions.py +++ b/apps/utils/tests/tests_permissions.py @@ -9,10 +9,11 @@ class BasePermissionTests(APITestCase): title='Russia', locale='ru-RU' ) + self.lang.save() self.country_ru = Country.objects.get( name={"en-GB": "Russian"} ) - + self.country_ru.save()