diff --git a/apps/account/models.py b/apps/account/models.py index 63486f96..96927c1e 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -24,11 +24,13 @@ class Role(ProjectBaseMixin): STANDARD_USER = 1 COMMENTS_MODERATOR = 2 COUNTRY_ADMIN = 3 + CONTENT_PAGE_MANAGER = 4 ROLE_CHOICES = ( (STANDARD_USER, 'Standard user'), (COMMENTS_MODERATOR, 'Comments moderator'), (COUNTRY_ADMIN, 'Country admin'), + (CONTENT_PAGE_MANAGER, 'Content page manager') ) role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, null=False, blank=False) diff --git a/apps/account/permissions.py b/apps/account/permissions.py deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/comment/views/back.py b/apps/comment/views/back.py index 7f066f30..2895fdbe 100644 --- a/apps/comment/views/back.py +++ b/apps/comment/views/back.py @@ -1,7 +1,7 @@ from rest_framework import generics, permissions from comment.serializers import back as serializers from comment import models -from utils.permissions import IsCommentModerator +from utils.permissions import IsCommentModerator, IsCountryAdmin class CommentLstView(generics.ListCreateAPIView): @@ -15,5 +15,5 @@ class CommentRUDView(generics.RetrieveUpdateDestroyAPIView): """Comment RUD view.""" serializer_class = serializers.CommentBaseSerializer queryset = models.Comment.objects.all() - permission_classes = [IsCommentModerator] + permission_classes = [IsCountryAdmin|IsCommentModerator] lookup_field = 'id' diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 2fc63bd5..c97562cc 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -382,6 +382,13 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): return Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)).latest( field_name='vintage_year') + @property + def country_id(self): + """ + Return Country object of establishment location + """ + return self.address.city.country.id + class Position(BaseAttributes, TranslatedFieldsMixin): """Position model.""" diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 5cba8255..fe9633ab 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -4,6 +4,7 @@ from rest_framework import generics from establishment import models from establishment import serializers +from utils.permissions import IsCountryAdmin class EstablishmentMixinViews: @@ -18,11 +19,13 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP """Establishment list/create view.""" queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentListCreateSerializer + permission_classes = [IsCountryAdmin] class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentRUDSerializer + permission_classes = [IsCountryAdmin] class MenuListCreateView(generics.ListCreateAPIView): diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 8f5d2a26..36f2a027 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -11,7 +11,7 @@ from main import methods from main.models import MetaDataContent from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer from utils.pagination import EstablishmentPortionPagination - +from utils.permissions import IsCountryAdmin class EstablishmentMixinView: """Establishment mixin.""" diff --git a/apps/location/models.py b/apps/location/models.py index 2298c28e..7f797811 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -112,6 +112,10 @@ class Address(models.Model): return {'lat': self.latitude, 'lon': self.longitude} + @property + def country_id(self): + return self.city.country_id + # todo: Make recalculate price levels @receiver(post_save, sender=Country) diff --git a/apps/location/views/back.py b/apps/location/views/back.py index ce6589ed..5c028545 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -3,50 +3,54 @@ from rest_framework import generics from location import models, serializers from location.views import common - +from utils.permissions import IsCountryAdmin # Address class AddressListCreateView(common.AddressViewMixin, generics.ListCreateAPIView): """Create view for model Address.""" serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() + permission_classes = [IsCountryAdmin] class AddressRUDView(common.AddressViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model Address.""" serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() + permission_classes = [IsCountryAdmin] # City class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView): """Create view for model City.""" serializer_class = serializers.CitySerializer - + permission_classes = [IsCountryAdmin] class CityRUDView(common.CityViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model City.""" serializer_class = serializers.CitySerializer + permission_classes = [IsCountryAdmin] # Region class RegionListCreateView(common.RegionViewMixin, generics.ListCreateAPIView): """Create view for model Region""" serializer_class = serializers.RegionSerializer - + permission_classes = [IsCountryAdmin] class RegionRUDView(common.RegionViewMixin, generics.RetrieveUpdateDestroyAPIView): """Retrieve view for model Region""" serializer_class = serializers.RegionSerializer - + permission_classes = [IsCountryAdmin] # Country class CountryListCreateView(common.CountryViewMixin, generics.ListCreateAPIView): """List/Create view for model Country.""" serializer_class = serializers.CountryBackSerializer pagination_class = None - + permission_classes = [IsCountryAdmin] class CountryRUDView(common.CountryViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model Country.""" serializer_class = serializers.CountryBackSerializer + permission_classes = [IsCountryAdmin] \ No newline at end of file diff --git a/apps/news/tests.py b/apps/news/tests.py index 2e24ac45..27ede62c 100644 --- a/apps/news/tests.py +++ b/apps/news/tests.py @@ -1,3 +1,4 @@ +from django.urls import reverse from http.cookies import SimpleCookie from rest_framework.test import APITestCase @@ -6,7 +7,8 @@ from datetime import datetime, timedelta from news.models import NewsType, News from account.models import User - +from translation.models import Language +from location.models import Country # Create your tests here. @@ -27,7 +29,20 @@ class BaseTestCase(APITestCase): playlist=1, start=datetime.now() + timedelta(hours=-2), end=datetime.now() + timedelta(hours=2), state=News.PUBLISHED, slug='test-news-slug',) + self.lang = Language.objects.create( + title='Russia', + locale='ru-RU' + ) + self.lang.save() + self.country_ru = Country.objects.create( + name='{"ru-RU":"Russia"}', + code='23', + low_price=15, + high_price=150000, + ) + self.country_ru.languages.add(self.lang) + self.country_ru.save() class NewsTestCase(BaseTestCase): @@ -50,3 +65,18 @@ class NewsTestCase(BaseTestCase): def test_news_type_list(self): response = self.client.get("/api/web/news/types/") self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_news_back_detail_put(self): + # retrieve-update-destroy + url = reverse('back:news:retrieve-update-destroy', kwargs={'pk': self.test_news.id}) + data = { + 'id': self.test_news.id, + 'description': {"en-GB": "Description test news!"}, + 'slug': self.test_news.slug, + 'start': self.test_news.start, + 'playlist': self.test_news.playlist, + 'news_type_id':self.test_news.news_type_id, + 'country_id': self.country_ru.id + } + response = self.client.put(url, data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) \ No newline at end of file diff --git a/apps/news/views.py b/apps/news/views.py index 61a57251..b5273a5e 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -2,6 +2,7 @@ from rest_framework import generics, permissions from news import filters, models, serializers from rating.tasks import add_rating +from utils.permissions import IsCountryAdmin class NewsMixinView: """News mixin.""" @@ -57,6 +58,7 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, serializer_class = serializers.NewsBackOfficeBaseSerializer create_serializers_class = serializers.NewsBackOfficeDetailSerializer + permission_classes = [IsCountryAdmin] def get_serializer_class(self): """Override serializer class.""" diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 2754a3c5..e2a2b80a 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -67,9 +67,34 @@ class IsStandardUser(IsGuest): """ def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request - if super().has_object_permission(request, view, obj) or\ - (obj.user == request.user and obj.user.email_confirmed): + if obj.user == request.user and obj.user.email_confirmed: return True + + if super().has_object_permission(request, view, obj): + return True + + return False + + +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_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)\ + .first() # 'Comments moderator' + + is_access = UserRole.objects.filter(user=request.user, role=role).exists() + if obj.user != request.user and is_access: + return True + + if super().has_object_permission(request, view, obj): + return True + return False @@ -80,17 +105,18 @@ class IsCountryAdmin(IsStandardUser): """ def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request. - if super().has_object_permission(request, view, obj): - return True - - # Must have role role = Role.objects.filter(role=Role.COUNTRY_ADMIN, country_id=obj.country_id) \ .first() # 'Comments moderator' is_access = UserRole.objects.filter(user=request.user, role=role).exists() + if obj.user != request.user and is_access: return True + + if super().has_object_permission(request, view, obj): + return True + return False @@ -102,17 +128,16 @@ class IsCommentModerator(IsCountryAdmin): def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request. - - if super().has_object_permission(request, view, obj): - return True - - # Must have role role = Role.objects.filter(role=Role.COMMENTS_MODERATOR, country_id=obj.country_id)\ .first() # 'Comments moderator' is_access = UserRole.objects.filter(user=request.user, role=role).exists() + if obj.user != request.user and is_access: return True + if super().has_object_permission(request, view, obj): + return True + return False