From e824b5ee65cc85e626e64073eb3a9f26998b94b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 6 Dec 2019 16:51:57 +0300 Subject: [PATCH] winery permission --- apps/establishment/models.py | 19 ----------- apps/establishment/tests.py | 57 ++++++++++++++++++++++++++++++-- apps/establishment/views/back.py | 28 ++++++++-------- apps/utils/permissions.py | 52 ++++++++++++++++++++++++++--- 4 files changed, 116 insertions(+), 40 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 3cdef691..20db3710 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -824,25 +824,6 @@ class ContactEmail(models.Model): return f'{self.email}' -# -# class Wine(TranslatedFieldsMixin, models.Model): -# """Wine model.""" -# establishment = models.ForeignKey( -# 'establishment.Establishment', verbose_name=_('establishment'), -# on_delete=models.CASCADE) -# bottles = models.IntegerField(_('bottles')) -# price_min = models.DecimalField( -# _('price min'), max_digits=14, decimal_places=2) -# price_max = models.DecimalField( -# _('price max'), max_digits=14, decimal_places=2) -# by_glass = models.BooleanField(_('by glass')) -# price_glass_min = models.DecimalField( -# _('price min'), max_digits=14, decimal_places=2) -# price_glass_max = models.DecimalField( -# _('price max'), max_digits=14, decimal_places=2) -# - - class Plate(TranslatedFieldsMixin, models.Model): """Plate model.""" STR_FIELD_NAME = 'name' diff --git a/apps/establishment/tests.py b/apps/establishment/tests.py index bd96b052..86606d6b 100644 --- a/apps/establishment/tests.py +++ b/apps/establishment/tests.py @@ -4,7 +4,8 @@ from account.models import User from rest_framework import status from http.cookies import SimpleCookie from main.models import Currency -from establishment.models import Establishment, EstablishmentType, Menu, SocialChoice, SocialNetwork +from establishment.models import Establishment, EstablishmentType, EstablishmentSubType,\ + Menu, SocialChoice, SocialNetwork # Create your tests here. from translation.models import Language from account.models import Role, UserRole @@ -87,7 +88,59 @@ class BaseTestCase(APITestCase): ) -class EstablishmentBTests(BaseTestCase): +class WineryBackTests(BaseTestCase): + def setUp(self): + super().setUp() + self.user_role.delete() + self.role.delete() + + def test_establishment_CRUD(self): + params = {'page': 1, 'page_size': 1, } + response = self.client.get('/api/back/establishments/', params, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + self.establishment_subtype = EstablishmentSubType.objects.create( + name={"en-GB":"some text"}, + index_name='Index name', + establishment_type_id=self.establishment_type.id + ) + + self.establishment_subtype.save() + self.role = Role.objects.create(role=Role.WINERY_REVIEWER, + establishment_subtype_id=self.establishment_subtype.id) + self.role.save() + self.establishment.add_establishment_subtype(self.establishment_subtype) + + data = { + 'name': 'Test establishment', + 'type_id': self.establishment_type.id, + 'is_publish': True, + 'slug': 'test-establishment-slug', + 'tz': py_tz('Europe/Moscow').zone, + 'address_id': self.address.id + } + + response = self.client.post('/api/back/establishments/', data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + response = self.client.get(f'/api/back/establishments/{self.establishment.id}/', format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + update_data = { + 'name': 'Test new establishment' + } + + response = self.client.patch(f'/api/back/establishments/{self.establishment.id}/', + data=update_data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/', + format='json') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + + +class EstablishmentBackTests(BaseTestCase): def test_establishment_CRUD(self): params = {'page': 1, 'page_size': 1, } response = self.client.get('/api/back/establishments/', params, format='json') diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 6fa4d821..e7cd8f73 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -3,10 +3,9 @@ from django.http import Http404, HttpResponse from django.shortcuts import get_object_or_404 from rest_framework import generics, permissions, status -from utils.permissions import IsCountryAdmin, IsEstablishmentManager from establishment import filters, models, serializers from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer -from utils.permissions import IsCountryAdmin, IsEstablishmentManager +from utils.permissions import IsCountryAdmin, IsEstablishmentManager, IsWineryReviewer from utils.views import CreateDestroyGalleryViewMixin from timetable.models import Timetable from rest_framework import status @@ -25,7 +24,8 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP """Establishment list/create view.""" filter_class = filters.EstablishmentFilter - permission_classes = [IsCountryAdmin | IsEstablishmentManager] + + permission_classes = [IsWineryReviewer | IsCountryAdmin | IsEstablishmentManager] queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentListCreateSerializer @@ -33,13 +33,13 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentRUDSerializer - permission_classes = [IsCountryAdmin | IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsCountryAdmin | IsEstablishmentManager] class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): """Establishment schedule RUD view""" serializer_class = ScheduleRUDSerializer - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer |IsEstablishmentManager] def get_object(self): """ @@ -64,21 +64,21 @@ class EstablishmentScheduleCreateView(generics.CreateAPIView): """Establishment schedule Create view""" serializer_class = ScheduleCreateSerializer queryset = Timetable.objects.all() - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class MenuListCreateView(generics.ListCreateAPIView): """Menu list create view.""" serializer_class = serializers.MenuSerializers queryset = models.Menu.objects.all() - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class MenuRUDView(generics.RetrieveUpdateDestroyAPIView): """Menu RUD view.""" serializer_class = serializers.MenuRUDSerializers queryset = models.Menu.objects.all() - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class SocialChoiceListCreateView(generics.ListCreateAPIView): @@ -116,14 +116,14 @@ class PlateListCreateView(generics.ListCreateAPIView): serializer_class = serializers.PlatesSerializers queryset = models.Plate.objects.all() pagination_class = None - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class PlateRUDView(generics.RetrieveUpdateDestroyAPIView): """Plate RUD view.""" serializer_class = serializers.PlatesSerializers queryset = models.Plate.objects.all() - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class PhonesListCreateView(generics.ListCreateAPIView): @@ -131,14 +131,14 @@ class PhonesListCreateView(generics.ListCreateAPIView): serializer_class = serializers.ContactPhoneBackSerializers queryset = models.ContactPhone.objects.all() pagination_class = None - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class PhonesRUDView(generics.RetrieveUpdateDestroyAPIView): """Phones RUD view.""" serializer_class = serializers.ContactPhoneBackSerializers queryset = models.ContactPhone.objects.all() - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class EmailListCreateView(generics.ListCreateAPIView): @@ -146,14 +146,14 @@ class EmailListCreateView(generics.ListCreateAPIView): serializer_class = serializers.ContactEmailBackSerializers queryset = models.ContactEmail.objects.all() pagination_class = None - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class EmailRUDView(generics.RetrieveUpdateDestroyAPIView): """Email RUD view.""" serializer_class = serializers.ContactEmailBackSerializers queryset = models.ContactEmail.objects.all() - permission_classes = [IsEstablishmentManager] + permission_classes = [IsWineryReviewer | IsEstablishmentManager] class EmployeeListCreateView(generics.ListCreateAPIView): diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 30055c44..432c653b 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -7,7 +7,8 @@ from rest_framework_simplejwt.tokens import AccessToken from account.models import UserRole, Role from authorization.models import JWTRefreshToken from utils.tokens import GMRefreshToken - +from establishment.models import EstablishmentSubType +from location.models import Address class IsAuthenticatedAndTokenIsValid(permissions.BasePermission): """ @@ -56,8 +57,9 @@ class IsGuest(permissions.IsAuthenticatedOrReadOnly): """ Object-level permission to only allow owners of an object to edit it. """ - + SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') def has_permission(self, request, view): + rules = [ request.user.is_superuser, request.method in permissions.SAFE_METHODS @@ -306,7 +308,6 @@ class IsEstablishmentManager(IsStandardUser): rules = [ # special! super().has_permission(request, view) - # super().has_object_permission(request, view, obj) ] role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER) \ @@ -319,7 +320,6 @@ class IsEstablishmentManager(IsStandardUser): ).exists(), # special! super().has_permission(request, view) - # super().has_object_permission(request, view, obj) ] return any(rules) @@ -368,7 +368,7 @@ class IsRestaurantReviewer(IsStandardUser): # 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' + .first() rules = [ UserRole.objects.filter(user=request.user, role=role, @@ -394,3 +394,45 @@ class IsRestaurantReviewer(IsStandardUser): ] return any(rules) + + +class IsWineryReviewer(IsStandardUser): + + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + + if 'type_id' in request.data and 'address_id' in request.data and request.user: + countries = Address.objects.filter(id=request.data['address_id']) + + est = EstablishmentSubType.objects.filter(establishment_type_id=request.data['type_id']) + if est.exists(): + role = Role.objects.filter(establishment_subtype_id__in=[type.id for type in est], + role=Role.WINERY_REVIEWER, + country_id__in=[country.id for country in countries]) \ + .first() + + rules.append( + UserRole.objects.filter(user=request.user, role=role).exists() + ) + + return any(rules) + + def has_object_permission(self, request, view, obj): + rules = [ + super().has_object_permission(request, view, obj) + ] + if hasattr(obj, 'type_id'): + est = EstablishmentSubType.objects.filter(establishment_type_id=obj.type_id) + role = Role.objects.filter(role=Role.WINERY_REVIEWER, + establishment_subtype_id__in=[id for type.id in est], + country_id=obj.country_id).first() + + rules = [ + UserRole.objects.filter(user=request.user, role=role, + establishment_id=obj.object_id + ).exists(), + super().has_object_permission(request, view, obj) + ] + return any(rules) \ No newline at end of file