diff --git a/BackEnd/administration/permissions.py b/BackEnd/administration/permissions.py new file mode 100644 index 000000000..ad2cdb974 --- /dev/null +++ b/BackEnd/administration/permissions.py @@ -0,0 +1,15 @@ +from rest_framework.permissions import BasePermission, SAFE_METHODS + + +class IsStaffUser(BasePermission): + """ + Custom is staff permission. + """ + + def has_permission(self, request, view): + return request.user.is_staff + + +class IsStaffUserOrReadOnly(BasePermission): + def has_permission(self, request, view): + return request.user.is_staff or request.method in SAFE_METHODS diff --git a/BackEnd/administration/serializers.py b/BackEnd/administration/serializers.py index f02a48a0e..dbb46a2b8 100644 --- a/BackEnd/administration/serializers.py +++ b/BackEnd/administration/serializers.py @@ -123,6 +123,8 @@ class Meta: class AutoModerationHoursSerializer(serializers.ModelSerializer): + auto_moderation_hours = serializers.IntegerField(min_value=1, max_value=48) + class Meta: model = AutoModeration fields = ("auto_moderation_hours",) diff --git a/BackEnd/administration/tests/test_automoderation_timeout_setting.py b/BackEnd/administration/tests/test_automoderation_timeout_setting.py new file mode 100644 index 000000000..b04cbd2b0 --- /dev/null +++ b/BackEnd/administration/tests/test_automoderation_timeout_setting.py @@ -0,0 +1,69 @@ +from rest_framework.test import APITestCase +from rest_framework import status + +from administration.factories import AdminUserFactory + + +class TestAutomoderationSettingisStaff(APITestCase): + def setUp(self): + self.user = AdminUserFactory() + self.client.force_authenticate(self.user) + + def test_positive_get_automoderation_timeout(self): + response = self.client.get("/api/admin/automoderation/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_positive_set_automoderation_timeout(self): + response = self.client.put( + "/api/admin/automoderation/", {"auto_moderation_hours": 24} + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + updated_response = self.client.get("/api/admin/automoderation/") + self.assertEqual(updated_response.status_code, status.HTTP_200_OK) + self.assertEqual(updated_response.data["auto_moderation_hours"], 24) + + def test_negative_set_incorrect_timeout(self): + response = self.client.put( + "/api/admin/automoderation/", {"auto_moderation_hours": 50} + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_negative_set_incorrect_negative_timeout(self): + response = self.client.put( + "/api/admin/automoderation/", {"auto_moderation_hours": -12} + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_negative_set_nonnumeric_timeout(self): + response = self.client.put( + "/api/admin/automoderation/", {"auto_moderation_hours": "four"} + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + +class TestAutomoderationSettingRegularUser(APITestCase): + def setUp(self): + self.user = AdminUserFactory(is_staff=False) + self.client.force_authenticate(self.user) + + def test_positive_get_automoderation_common_user(self): + response = self.client.get("/api/admin/automoderation/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_negative_set_automoderation_common_user(self): + response = self.client.put( + "/api/admin/automoderation/", {"auto_moderation_hours": 20} + ) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + +class TestAutomoderationSettingAnonymousUser(APITestCase): + def test_negative_get_automoderation_anonymous_user(self): + response = self.client.put("/api/admin/automoderation/") + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_negative_set_automoderation_anonymous_user(self): + response = self.client.put( + "/api/admin/automoderation/", {"auto_moderation_hours": 20} + ) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) diff --git a/BackEnd/administration/views.py b/BackEnd/administration/views.py index f7ac4ab3e..3ba12440a 100644 --- a/BackEnd/administration/views.py +++ b/BackEnd/administration/views.py @@ -1,3 +1,9 @@ +from drf_spectacular.utils import ( + extend_schema, + OpenApiExample, + OpenApiResponse, +) + from rest_framework.permissions import ( BasePermission, ) @@ -19,15 +25,7 @@ from administration.models import AutoModeration from authentication.models import CustomUser from profiles.models import Profile - - -class IsStaffUser(BasePermission): - """ - Custom is staff permission. - """ - - def has_permission(self, request, view): - return request.user.is_staff +from .permissions import IsStaffUser, IsStaffUserOrReadOnly class UsersListView(ListAPIView): @@ -85,13 +83,41 @@ class ProfileDetailView(RetrieveUpdateDestroyAPIView): ) +@extend_schema( + request=AutoModerationHoursSerializer, + responses={ + 200: OpenApiResponse( + response=AutoModerationHoursSerializer, + examples=[ + OpenApiExample( + "Valid example", + summary="Valid example", + description="A valid example with auto_moderation_hours set to 24", + value={"auto_moderation_hours": 24}, + ) + ], + ), + 400: OpenApiResponse( + response=AutoModerationHoursSerializer, + examples=[ + OpenApiExample( + "Invalid example", + summary="Invalid example", + description="An invalid example with auto_moderation_hours set to 50 (out of range)", + value={"auto_moderation_hours": 50}, + ) + ], + ), + }, +) class AutoModerationHoursView(RetrieveUpdateAPIView): """ View for retrieving and updating 'auto_moderation_hours' - a value that sets - the auto-approve delay (part of the moderation functionality) + the auto-approve delay (part of the moderation functionality). + Value must be an integer between 1 and 48 """ - permission_classes = [IsStaffUser] + permission_classes = [IsStaffUserOrReadOnly] serializer_class = AutoModerationHoursSerializer def get_object(self): diff --git a/BackEnd/forum/schema.py b/BackEnd/forum/schema.py index 380f4254f..3d8d7cd61 100644 --- a/BackEnd/forum/schema.py +++ b/BackEnd/forum/schema.py @@ -7,7 +7,6 @@ class TokenDestroyViewExtension(OpenApiViewExtension): target_class = TokenDestroyView def view_replacement(self): - class Fixed(self.target_class): serializer_class = None diff --git a/BackEnd/images/models.py b/BackEnd/images/models.py index a5601751f..d9613cab5 100644 --- a/BackEnd/images/models.py +++ b/BackEnd/images/models.py @@ -9,7 +9,6 @@ def image_directory_path(instance, filename): class ProfileImage(models.Model): - BANNER = "banner" LOGO = "logo" diff --git a/BackEnd/images/tests/test_add_images.py b/BackEnd/images/tests/test_add_images.py index 8db33324d..928c89905 100644 --- a/BackEnd/images/tests/test_add_images.py +++ b/BackEnd/images/tests/test_add_images.py @@ -8,7 +8,6 @@ class TestBannerChange(APITestCase): - def setUp(self) -> None: self.right_banner = open( os.path.join(os.getcwd(), "images/tests/img/img_2mb.png"), diff --git a/BackEnd/images/tests/test_delete_images.py b/BackEnd/images/tests/test_delete_images.py index 179c5c3a2..b2a86ab8d 100644 --- a/BackEnd/images/tests/test_delete_images.py +++ b/BackEnd/images/tests/test_delete_images.py @@ -8,7 +8,6 @@ class TestBannerChange(APITestCase): - def setUp(self) -> None: self.banner_path = os.path.join( os.getcwd(), "images", "tests", "img", "img_2mb.png" diff --git a/BackEnd/profiles/serializers.py b/BackEnd/profiles/serializers.py index b2f38985d..f56bcb26a 100644 --- a/BackEnd/profiles/serializers.py +++ b/BackEnd/profiles/serializers.py @@ -30,7 +30,6 @@ class Meta: class ProfileImageField(serializers.Field): - def to_representation(self, value): if value.is_deleted == False: return { diff --git a/BackEnd/search/serializers.py b/BackEnd/search/serializers.py index 3447caab8..e5da26941 100644 --- a/BackEnd/search/serializers.py +++ b/BackEnd/search/serializers.py @@ -11,7 +11,6 @@ class ProfileImageField(serializers.Field): - def to_representation(self, value): if value.is_deleted == False: return {