From 16c2434c1483299877cfe698adcb8614dd714598 Mon Sep 17 00:00:00 2001 From: rup-narayan-rajbanshi Date: Fri, 12 Jul 2024 16:46:28 +0545 Subject: [PATCH] Add guest permission in GET apis. --- .pre-commit-config.yaml | 1 - api/drf_views.py | 10 +-- ... => 0212_profile_limit_access_to_guest.py} | 6 +- api/models.py | 6 +- api/snapshots/snap_test_views.py | 20 ++--- api/test_views.py | 84 ++++++++++++++++--- api/visibility_class.py | 4 +- dref/views.py | 12 ++- flash_update/test_views.py | 11 ++- flash_update/views.py | 4 +- lang/tests.py | 4 +- main/permissions.py | 9 +- per/drf_views.py | 2 +- pyproject.toml | 3 +- 14 files changed, 119 insertions(+), 57 deletions(-) rename api/migrations/{0211_profile_limit_access_to_guest.py => 0212_profile_limit_access_to_guest.py} (72%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ac2e9099e..920a7205c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,6 @@ exclude: | (?x)^( \.git| __pycache__| - .*snap_test_.*\.py| .+\/.+\/migrations\/.*| legacy| \.venv diff --git a/api/drf_views.py b/api/drf_views.py index 9cb166695..bc288087c 100644 --- a/api/drf_views.py +++ b/api/drf_views.py @@ -880,16 +880,12 @@ def get_queryset(self): class UserViewset(viewsets.ModelViewSet): serializer_class = UserSerializer authentication_classes = (TokenAuthentication,) - permission_classes = (IsAuthenticated, DenyGuestUserMutationPermission) + permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission] def get_queryset(self): return User.objects.filter(pk=self.request.user.pk) - @action( - detail=False, - url_path="me", - serializer_class=UserMeSerializer, - ) + @action(detail=False, url_path="me", serializer_class=UserMeSerializer, permission_classes=(IsAuthenticated,)) def get_authenticated_user_info(self, request, *args, **kwargs): return Response(self.get_serializer_class()(request.user).data) @@ -1309,7 +1305,7 @@ class UsersViewset(viewsets.ReadOnlyModelViewSet): """ serializer_class = UserSerializer - permission_classes = [IsAuthenticated] + permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission] filterset_class = UserFilterSet def get_queryset(self): diff --git a/api/migrations/0211_profile_limit_access_to_guest.py b/api/migrations/0212_profile_limit_access_to_guest.py similarity index 72% rename from api/migrations/0211_profile_limit_access_to_guest.py rename to api/migrations/0212_profile_limit_access_to_guest.py index b01836046..03a5b11f7 100644 --- a/api/migrations/0211_profile_limit_access_to_guest.py +++ b/api/migrations/0212_profile_limit_access_to_guest.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.13 on 2024-07-12 06:47 +# Generated by Django 4.2.13 on 2024-07-30 07:53 from django.db import migrations, models @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ("api", "0210_profile_accepted_montandon_license_terms"), + ("api", "0211_alter_countrydirectory_unique_together_and_more"), ] operations = [ @@ -15,7 +15,7 @@ class Migration(migrations.Migration): name="limit_access_to_guest", field=models.BooleanField( default=True, - help_text="If this value is set to true, the user is treated as a guest user regardless of any other permissions they may have, thereby depriving them of all non-guest user permissions.", + help_text="If this value is set to true, the user is treated as a guest user regardless of any other permissions they may have, thereby depriving them of all non-guest user permissions.", verbose_name="limit access to guest user permissions", ), ), diff --git a/api/models.py b/api/models.py index a26a01b30..60b802a59 100644 --- a/api/models.py +++ b/api/models.py @@ -1851,8 +1851,10 @@ class OrgTypes(models.TextChoices): last_frontend_login = models.DateTimeField(verbose_name=_("last frontend login"), null=True, blank=True) accepted_montandon_license_terms = models.BooleanField(verbose_name=_("has accepted montandon license terms?"), default=False) limit_access_to_guest = models.BooleanField( - help_text="If this value is set to true, the user is treated as a guest user regardless of any other permissions \ - they may have, thereby depriving them of all non-guest user permissions.", + help_text=( + "If this value is set to true, the user is treated as a guest user regardless of any other permissions" + " they may have, thereby depriving them of all non-guest user permissions." + ), verbose_name=_("limit access to guest user permissions"), default=True, ) diff --git a/api/snapshots/snap_test_views.py b/api/snapshots/snap_test_views.py index 4f1e4fc52..bc263648a 100644 --- a/api/snapshots/snap_test_views.py +++ b/api/snapshots/snap_test_views.py @@ -14,7 +14,7 @@ "countries": [], "countries_for_preview": [], "created_at": "2008-01-01T00:00:00.123456Z", - "disaster_start_date": "2015-04-21T17:45:23.476445Z", + "disaster_start_date": "2021-09-20T13:28:12.297843Z", "districts": [], "dtype": 1, "emergency_response_contact_email": None, @@ -56,12 +56,12 @@ }, ], "field_reports": [], - "glide": "xJKxDZJiNfetzTUEHA", + "glide": "bxJKxDZJiNfetzTUEH", "hide_attached_field_reports": True, "hide_field_report_map": True, "id": 2, - "ifrc_severity_level": 0, - "ifrc_severity_level_display": "Yellow", + "ifrc_severity_level": 1, + "ifrc_severity_level_display": "Orange", "is_featured": False, "is_featured_region": True, "key_figures": [], @@ -71,7 +71,7 @@ "parent_event": 1, "response_activity_count": 0, "slug": "ygwwmqzcudihyfjsonxkmtecqoxsfogyrdoxkxwnqrsrpemoki", - "summary": "NMGyDLJYVcCZKPmuMEGjdCgZvTfGPlcpTCCHHNkxxsyAXvRMdYOPvevgJRysqUQMjvfLQjwtPSQziMTftJyPYviQSVRHfPQBGxbxtlnvXFmoijesYgGXIVHcQvXNiMyjklSXNZkUCcAxRUpCNsWVYCoIptZYEmxRKCDXsXyGHAkmZMiqdPExJgTHhsfWkrCGjBfoCwbAdzGxpyfxobugTPvYjicsESiWTECNafbqnjJUMHBhXspthdpAOYNDehFMIbOGKpTjsBaNwpKAlQQfHxeHIGYGJbyEcOyxqVbwYewpUQOgXLVWvicwIvPlXRDSEOlZieTXDcsmcYmcutGzIEqcWPmswXdPvrhZxBzVCyvlFSFxZHrZfUBfBMlIsugfuQstCMTBkSCwCcUwNBrOYdeQOzxGZVRkbjMRYCciepXPxxyKcMjRCxxCWeKiHxzuPrphbVlFHyJhqXqTCnNsSFmhieClTCfZRuQwTeJIstkTTSOlYxGo", + "summary": "fNMGyDLJYVcCZKPmuMEGjdCgZvTfGPlcpTCCHHNkxxsyAXvRMdYOPvevgJRysqUQMjvfLQjwtPSQziMTftJyPYviQSVRHfPQBGxbxtlnvXFmoijesYgGXIVHcQvXNiMyjklSXNZkUCcAxRUpCNsWVYCoIptZYEmxRKCDXsXyGHAkmZMiqdPExJgTHhsfWkrCGjBfoCwbAdzGxpyfxobugTPvYjicsESiWTECNafbqnjJUMHBhXspthdpAOYNDehFMIbOGKpTjsBaNwpKAlQQfHxeHIGYGJbyEcOyxqVbwYewpUQOgXLVWvicwIvPlXRDSEOlZieTXDcsmcYmcutGzIEqcWPmswXdPvrhZxBzVCyvlFSFxZHrZfUBfBMlIsugfuQstCMTBkSCwCcUwNBrOYdeQOzxGZVRkbjMRYCciepXPxxyKcMjRCxxCWeKiHxzuPrphbVlFHyJhqXqTCnNsSFmhieClTCfZRuQwTeJIstkTTSOlYxG", "tab_one_title": "cPXKqPnXKANObFOIsPtEpZZRztDeSdkCAEDnvMjuTuUwziWxGJ", "tab_three_title": "gBiqUxWzxczdKJmxJseyGCWJrNRNhigzxYvJxWjmMGzGccciTv", "tab_two_title": "gupDhrCpjgdsyNApkuKUumWkFGDFtFbfzGDpnLwddsFMPREsIa", @@ -88,18 +88,18 @@ "countries": [], "countries_for_preview": [], "created_at": "2008-01-01T00:00:00.123456Z", - "disaster_start_date": "2015-04-21T17:45:23.476445Z", + "disaster_start_date": "2021-09-20T13:28:12.297843Z", "districts": [], "dtype": 1, "emergency_response_contact_email": None, "featured_documents": [], "field_reports": [], - "glide": "xJKxDZJiNfetzTUEHA", + "glide": "bxJKxDZJiNfetzTUEH", "hide_attached_field_reports": True, "hide_field_report_map": True, "id": 2, - "ifrc_severity_level": 0, - "ifrc_severity_level_display": "Yellow", + "ifrc_severity_level": 1, + "ifrc_severity_level_display": "Orange", "is_featured": False, "is_featured_region": True, "key_figures": [], @@ -145,7 +145,7 @@ "parent_event": 1, "response_activity_count": 0, "slug": "ygwwmqzcudihyfjsonxkmtecqoxsfogyrdoxkxwnqrsrpemoki", - "summary": "NMGyDLJYVcCZKPmuMEGjdCgZvTfGPlcpTCCHHNkxxsyAXvRMdYOPvevgJRysqUQMjvfLQjwtPSQziMTftJyPYviQSVRHfPQBGxbxtlnvXFmoijesYgGXIVHcQvXNiMyjklSXNZkUCcAxRUpCNsWVYCoIptZYEmxRKCDXsXyGHAkmZMiqdPExJgTHhsfWkrCGjBfoCwbAdzGxpyfxobugTPvYjicsESiWTECNafbqnjJUMHBhXspthdpAOYNDehFMIbOGKpTjsBaNwpKAlQQfHxeHIGYGJbyEcOyxqVbwYewpUQOgXLVWvicwIvPlXRDSEOlZieTXDcsmcYmcutGzIEqcWPmswXdPvrhZxBzVCyvlFSFxZHrZfUBfBMlIsugfuQstCMTBkSCwCcUwNBrOYdeQOzxGZVRkbjMRYCciepXPxxyKcMjRCxxCWeKiHxzuPrphbVlFHyJhqXqTCnNsSFmhieClTCfZRuQwTeJIstkTTSOlYxGo", + "summary": "fNMGyDLJYVcCZKPmuMEGjdCgZvTfGPlcpTCCHHNkxxsyAXvRMdYOPvevgJRysqUQMjvfLQjwtPSQziMTftJyPYviQSVRHfPQBGxbxtlnvXFmoijesYgGXIVHcQvXNiMyjklSXNZkUCcAxRUpCNsWVYCoIptZYEmxRKCDXsXyGHAkmZMiqdPExJgTHhsfWkrCGjBfoCwbAdzGxpyfxobugTPvYjicsESiWTECNafbqnjJUMHBhXspthdpAOYNDehFMIbOGKpTjsBaNwpKAlQQfHxeHIGYGJbyEcOyxqVbwYewpUQOgXLVWvicwIvPlXRDSEOlZieTXDcsmcYmcutGzIEqcWPmswXdPvrhZxBzVCyvlFSFxZHrZfUBfBMlIsugfuQstCMTBkSCwCcUwNBrOYdeQOzxGZVRkbjMRYCciepXPxxyKcMjRCxxCWeKiHxzuPrphbVlFHyJhqXqTCnNsSFmhieClTCfZRuQwTeJIstkTTSOlYxG", "tab_one_title": "cPXKqPnXKANObFOIsPtEpZZRztDeSdkCAEDnvMjuTuUwziWxGJ", "tab_three_title": "gBiqUxWzxczdKJmxJseyGCWJrNRNhigzxYvJxWjmMGzGccciTv", "tab_two_title": "gupDhrCpjgdsyNApkuKUumWkFGDFtFbfzGDpnLwddsFMPREsIa", diff --git a/api/test_views.py b/api/test_views.py index fbc217b2f..aba103c29 100644 --- a/api/test_views.py +++ b/api/test_views.py @@ -35,20 +35,21 @@ def test_guest_user_permission(self): "/api/v2/add_subscription/", "/api/v2/del_subscription/", "/api/v2/external-token/", + "/api/v2/user/me/", ] - + id = 1 # NOTE: id is used just to test api that requires id, it doesnot indicate real id. It can be any number. go_apis = [ "/api/v2/dref/", "/api/v2/dref-final-report/", - "/api/v2/dref-final-report/{id}/publish/", + f"/api/v2/dref-final-report/{id}/publish/", "/api/v2/dref-op-update/", - "/api/v2/dref-op-update/{id}/publish/", + f"/api/v2/dref-op-update/{id}/publish/", "/api/v2/dref-share/", - "/api/v2/dref/{id}/publish/", + f"/api/v2/dref/{id}/publish/", "/api/v2/flash-update/", "/api/v2/flash-update-file/multiple/", "/api/v2/local-units/", - "/api/v2/local-units/{id}/validate/", + f"/api/v2/local-units/{id}/validate/", "/api/v2/pdf-export/", "/api/v2/per-assessment/", "/api/v2/per-document-upload/", @@ -68,29 +69,89 @@ def test_guest_user_permission(self): "/api/v2/user/", ] + get_apis = [ + "/api/v2/dref/", + "/api/v2/dref-files/", + "/api/v2/dref-final-report/", + f"/api/v2/dref-final-report/{id}/", + "/api/v2/dref-op-update/", + f"/api/v2/dref/{id}/", + "/api/v2/field-report/", + f"/api/v2/field-report/{id}/", + "/api/v2/flash-update/", + "/api/v2/flash-update-file/", + f"/api/v2/flash-update/{id}/", + "/api/v2/language/", + f"/api/v2/language/{id}/", + "/api/v2/local-units/", + f"/api/v2/local-units/{id}/", + "/api/v2/ops-learning/", + f"/api/v2/ops-learning/{id}/", + f"/api/v2/pdf-export/{id}/", + "/api/v2/per-assessment/", + f"/api/v2/per-assessment/{id}/", + "/api/v2/per-document-upload/", + f"/api/v2/per-document-upload/{id}/", + "/api/v2/per-file/", + "/api/v2/per-overview/", + f"/api/v2/per-overview/{id}/", + "/api/v2/per-prioritization/", + f"/api/v2/per-prioritization/{id}/", + "/api/v2/per-work-plan/", + f"/api/v2/per-work-plan/{id}/", + "/api/v2/profile/", + f"/api/v2/profile/{id}/", + f"/api/v2/share-flash-update/{id}/", + "/api/v2/subscription/", + f"/api/v2/subscription/{id}/", + "/api/v2/users/", + f"/api/v2/users/{id}/", + ] + + # TODO Add test case for export apis + # get_export_apis = [ + # f"/api/v2/export-flash-update/{1}/", + # f"/api/v2/export-per/{1}/", + # ] + go_apis_req_additional_perm = [ "/api/v2/ops-learning/", "/api/v2/per-overview/", - "/api/v2/user/{id}/accepted_license_terms/", - "/api/v2/language/{id}/bulk-action/", + f"/api/v2/user/{id}/accepted_license_terms/", + f"/api/v2/language/{id}/bulk-action/", ] self.authenticate(user=self.guest_user) + + # Guest user should not be able to access get apis that requires IsAuthenticated permission + for api_url in get_apis: + response = self.client.get(api_url).json() + error_code = response.get("error_code") + self.assertIn(error_code, [403, 401]) + + # Guest user should not be able to hit post apis. for api_url in go_apis + go_apis_req_additional_perm: response = self.client.post(api_url, json=body).json() self.assertIn(response["error_code"], [401, 403]) + # Guest user should be able to access guest apis for api_url in guest_apis: response = self.client.post(api_url, json=body).json() error_code = response.get("error_code", None) self.assertNotIn(error_code, [403, 401]) + # Go user should be able to access go_apis self.authenticate(user=self.go_user) for api_url in go_apis: response = self.client.post(api_url, json=body).json() error_code = response.get("error_code", None) self.assertNotIn(error_code, [403, 401]) + for api_url in get_apis: + response = self.client.get(api_url).json() + error_code = response.get("error_code", None) + self.assertNotIn(error_code, [403, 401]) + class AuthTokenTest(APITestCase): def setUp(self): @@ -283,21 +344,24 @@ def test_country_snippet_visibility(self): self.assertEqual(response["count"], 0) # perform the request with an authenticated user - user = User.objects.create(username="foo") + user = UserFactory(username="foo") self.client.force_authenticate(user=user) response = self.client.get("/api/v2/country_snippet/").json() # one snippets available to anonymous user self.assertEqual(response["count"], 1) # perform the request with an ifrc user - user2 = User.objects.create(username="bar") + user2 = UserFactory(username="bar") user2.user_permissions.add(self.ifrc_permission) self.client.force_authenticate(user=user2) response = self.client.get("/api/v2/country_snippet/").json() self.assertEqual(response["count"], 2) # perform the request with a superuser - super_user = User.objects.create_superuser(username="baz", email="foo@baz.com", password="12345678") + super_user = UserFactory(username="baz", email="foo@baz.com", password="12345678") + super_user.is_superuser = True + super_user.save() + self.client.force_authenticate(user=super_user) response = self.client.get("/api/v2/country_snippet/").json() self.assertEqual(response["count"], 2) diff --git a/api/visibility_class.py b/api/visibility_class.py index 839c1df8a..31125cb89 100644 --- a/api/visibility_class.py +++ b/api/visibility_class.py @@ -17,7 +17,7 @@ def get_visibility_queryset(self, queryset): if queryset.model == Project: choices = VisibilityCharChoices - if self.request.user.is_authenticated: + if self.request.user.is_authenticated and not self.request.user.profile.limit_access_to_guest: if is_user_ifrc(self.request.user): return queryset else: @@ -37,7 +37,7 @@ class ReadOnlyVisibilityViewset(viewsets.ReadOnlyModelViewSet): def get_queryset(self): # FIXME: utils.py:43 # filter_visibility_by_auth(user=self.request.user, visibility_model_class=self.visibility_model_class) - if self.request.user.is_authenticated: + if self.request.user.is_authenticated and not self.request.user.profile.limit_access_to_guest: if is_user_ifrc(self.request.user): return self.visibility_model_class.objects.all() else: diff --git a/dref/views.py b/dref/views.py index ffb538c92..cf04e7834 100644 --- a/dref/views.py +++ b/dref/views.py @@ -200,7 +200,9 @@ def multiple_file(self, request, pk=None, version=None): class CompletedDrefOperationsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CompletedDrefOperationsSerializer - permission_classes = [permissions.IsAuthenticated] + permission_classes = [ + permissions.IsAuthenticated, + ] filterset_class = CompletedDrefOperationsFilterSet queryset = DrefFinalReport.objects.filter(is_published=True).order_by("-created_at").distinct() @@ -211,7 +213,9 @@ def get_queryset(self): class ActiveDrefOperationsViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = MiniDrefSerializer - permission_classes = [permissions.IsAuthenticated] + permission_classes = [ + permissions.IsAuthenticated, + ] filterset_class = ActiveDrefFilterSet queryset = ( Dref.objects.prefetch_related("planned_interventions", "needs_identified", "national_society_actions", "users") @@ -239,7 +243,9 @@ def post(self, request): class DrefShareUserViewSet(viewsets.ReadOnlyModelViewSet): - permissions_classes = [permissions.IsAuthenticated] + permission_classes = [ + permissions.IsAuthenticated, + ] serializer_class = DrefShareUserSerializer filterset_class = DrefShareUserFilterSet diff --git a/flash_update/test_views.py b/flash_update/test_views.py index 61393cca7..2e641aecc 100644 --- a/flash_update/test_views.py +++ b/flash_update/test_views.py @@ -2,7 +2,6 @@ from unittest import mock from django.conf import settings -from django.contrib.auth.models import User import api.models as models from deployments.factories.user import UserFactory @@ -22,7 +21,7 @@ class FlashUpdateTest(APITestCase): def setUp(self): - self.user = User.objects.create(username="jo") + self.user = UserFactory.create(username="jo") self.country1 = models.Country.objects.create(name="abc") self.country2 = models.Country.objects.create(name="xyz") self.district1 = models.District.objects.create(name="test district1", country=self.country1) @@ -142,7 +141,7 @@ def test_patch(self): self.assertEqual(flash_id.share_with, FlashUpdate.FlashShareWith.IFRC_SECRETARIAT) def test_get_flash_update(self): - user1 = User.objects.create(username="abc") + user1 = UserFactory.create(username="abc") flash_update1, flash_update2, flash_update3 = FlashUpdateFactory.create_batch(3, created_by=user1) self.client.force_authenticate(user=user1) response1 = self.client.get("/api/v2/flash-update/").json() @@ -158,7 +157,7 @@ def test_get_flash_update(self): self.assertEqual(response["id"], flash_update1.id) # try with another user - user2 = User.objects.create(username="xyz") + user2 = UserFactory.create(username="xyz") self.client.force_authenticate(user=user2) flash_update4, flash_update5 = FlashUpdateFactory.create_batch(2, created_by=user2) response2 = self.client.get("/api/v2/flash-update/").json() @@ -168,13 +167,13 @@ def test_get_flash_update(self): self.assertNotIn([data["id"] for data in response2["results"]], [data["id"] for data in response1["results"]]) # try with users who has no any flash update created - user3 = User.objects.create(username="ram") + user3 = UserFactory.create(username="ram") self.client.force_authenticate(user=user3) response3 = self.client.get("/api/v2/flash-update/").json() self.assertEqual(response3["count"], 5) def test_filter(self): - user = User.objects.create(username="xyz") + user = UserFactory.create(username="xyz") self.client.force_authenticate(user=user) hazard_type1 = models.DisasterType.objects.create(name="disaster_type1") hazard_type2 = models.DisasterType.objects.create(name="disaster_type2") diff --git a/flash_update/views.py b/flash_update/views.py index ebfe29207..c6766c93b 100644 --- a/flash_update/views.py +++ b/flash_update/views.py @@ -117,7 +117,9 @@ class ShareFlashUpdateViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin class ExportFlashUpdateView(views.APIView): - permission_classes = [permissions.IsAuthenticated] + permission_classes = [ + permissions.IsAuthenticated, + ] @extend_schema(request=None, responses=ExportFlashUpdateViewSerializer) def get(self, request, pk, format=None): diff --git a/lang/tests.py b/lang/tests.py index 76eb309eb..03561f14f 100644 --- a/lang/tests.py +++ b/lang/tests.py @@ -2,7 +2,7 @@ from unittest import mock from django.conf import settings -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.core import management from django.test import override_settings @@ -131,7 +131,7 @@ def test_bulk_action(self): self.assertEqual(first_string_key, string_1["key"]) def test_user_me(self): - user = User.objects.create_user( + user = UserFactory.create( username="user@test.com", first_name="User", last_name="Toot", diff --git a/main/permissions.py b/main/permissions.py index bae369460..d58662e96 100644 --- a/main/permissions.py +++ b/main/permissions.py @@ -14,17 +14,12 @@ def has_object_permission(self, request, view, obj): class DenyGuestUserMutationPermission(permissions.BasePermission): """ - Custom permission to deny mutation actions for logged-in guest users. + Custom permission to deny mutation and query actions for logged-in guest users. - This permission class allows all safe (read-only) operations but restricts - any mutation (write, update, delete) operations if the user is a guest. + This permission class restricts all (read, write, update, delete) operations if the user is a guest. """ def _has_permission(self, request, view): - # Allow all safe methods (GET, HEAD, OPTIONS) which are non-mutating. - if request.method in permissions.SAFE_METHODS: - return True - # For mutation methods (POST, PUT, DELETE, etc.): # Check if the user is authenticated. if not bool(request.user and request.user.is_authenticated): diff --git a/per/drf_views.py b/per/drf_views.py index 851fe4016..1caa75897 100644 --- a/per/drf_views.py +++ b/per/drf_views.py @@ -247,7 +247,7 @@ def get_queryset(self): class ExportPerView(views.APIView): - permission_classes = [permissions.IsAuthenticated] + permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission] content_negotiation_class = SpreadSheetContentNegotiation diff --git a/pyproject.toml b/pyproject.toml index 57c489da8..0bacb6164 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,7 @@ exclude = ''' )/ ''' # NOTE: Update in .pre-commit-config.yaml as well -extend-exclude = "^.*\\b(migrations)\\b.*$ (__pycache__|.*snap_test_.*\\.py|.+/+.+/+migrations/+.*)" +extend-exclude = "^.*\\b(migrations)\\b.*$ (__pycache__|.+/+.+/+migrations/+.*)" [tool.isort] profile = "black" @@ -138,7 +138,6 @@ multi_line_output = 3 # NOTE: Update in .pre-commit-config.yaml as well skip = [ "**/__pycache__", - "**/snap_test_*.py", ".venv/", "legacy/", "**/migrations/*.py",