Skip to content

Commit

Permalink
Add guest user permission for post apis.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rup-Narayan-Rajbanshi committed Jun 26, 2024
1 parent b26019f commit f7d6e8b
Show file tree
Hide file tree
Showing 15 changed files with 168 additions and 32 deletions.
9 changes: 5 additions & 4 deletions api/drf_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
from deployments.models import Personnel
from main.enums import GlobalEnumSerializer, get_enum_values
from main.filters import NullsLastOrderingFilter
from main.permissions import DenyGuestUserMutationPermission
from main.utils import is_tableau
from per.models import Overview
from per.serializers import CountryLatestOverviewSerializer
Expand Down Expand Up @@ -814,7 +815,7 @@ def get_serializer_class(self):
class ProfileViewset(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
permission_classes = (IsAuthenticated, DenyGuestUserMutationPermission)

def get_queryset(self):
return Profile.objects.filter(user=self.request.user)
Expand All @@ -823,7 +824,7 @@ def get_queryset(self):
class UserViewset(viewsets.ModelViewSet):
serializer_class = UserSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
permission_classes = (IsAuthenticated, DenyGuestUserMutationPermission)

def get_queryset(self):
return User.objects.filter(pk=self.request.user.pk)
Expand Down Expand Up @@ -859,7 +860,7 @@ class FieldReportViewset(ReadOnlyVisibilityViewsetMixin, viewsets.ModelViewSet):
) # for /docs
ordering_fields = ("summary", "event", "dtype", "created_at", "updated_at")
filterset_class = FieldReportFilter
authentication_class = [IsAuthenticated]
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]
queryset = FieldReport.objects.select_related("dtype", "event").prefetch_related(
"actions_taken", "actions_taken__actions", "countries", "districts", "regions"
)
Expand Down Expand Up @@ -1274,7 +1275,7 @@ def get(self, _):

class ExportViewSet(viewsets.ModelViewSet):
serializer_class = ExportSerializer
permission_classes = [IsAuthenticated]
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]

def get_queryset(self):
user = self.request.user
Expand Down
18 changes: 18 additions & 0 deletions api/migrations/0211_profile_limit_access_to_guest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.13 on 2024-06-20 10:02

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0210_profile_accepted_montandon_license_terms'),
]

operations = [
migrations.AddField(
model_name='profile',
name='limit_access_to_guest',
field=models.BooleanField(default=True, verbose_name='is not guest user'),
),
]
1 change: 1 addition & 0 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1836,6 +1836,7 @@ class OrgTypes(models.TextChoices):
phone_number = models.CharField(verbose_name=_("phone number"), blank=True, null=True, max_length=100)
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(verbose_name=_("is not guest user"), default=True)

class Meta:
verbose_name = _("user profile")
Expand Down
2 changes: 2 additions & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,7 @@ class UserMeSerializer(UserSerializer):
is_per_admin_for_regions = serializers.SerializerMethodField()
is_per_admin_for_countries = serializers.SerializerMethodField()
user_countries_regions = serializers.SerializerMethodField()
limit_access_to_guest = serializers.BooleanField(source="profile.limit_access_to_guest")

class Meta:
model = User
Expand All @@ -1710,6 +1711,7 @@ class Meta:
"is_per_admin_for_regions",
"is_per_admin_for_countries",
"user_countries_regions",
"limit_access_to_guest",
)

@staticmethod
Expand Down
74 changes: 74 additions & 0 deletions api/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,83 @@
EventFeaturedDocumentFactory,
EventLinkFactory,
)
from api.models import Profile
from main.test_case import APITestCase, SnapshotTestCase


class GuestUserPermissionTest(APITestCase):
def setUp(self):
self.guest_user = User.objects.create(username="guest")

self.go_user = User.objects.create(username="go-user")

profile = Profile.objects.get(user=self.guest_user)
profile.limit_access_to_guest = False
profile.save()

def test_guest_user_permission(self):
body = {}
guest_apis = [
"/api/v2/add_subscription/",
"/api/v2/del_subscription/",
]

go_apis = [
"/api/v2/dref/",
"/api/v2/dref-final-report/",
"/api/v2/dref-final-report/{id}/publish/",
"/api/v2/dref-op-update/",
"/api/v2/dref-op-update/{id}/publish/",
"/api/v2/dref-share/",
"/api/v2/dref/{id}/publish/",
"/api/v2/external-token/",
"/api/v2/flash-update/",
"/api/v2/flash-update-file/multiple/",
"/api/v2/local-units/",
"/api/v2/local-units/{id}/validate/",
"/api/v2/pdf-export/",
"/api/v2/per-assessment/",
"/api/v2/per-document-upload/",
"/api/v2/per-file/multiple/",
"/api/v2/per-prioritization/",
"/api/v2/per-work-plan/",
"/api/v2/project/",
"/api/v2/dref-files/",
"/api/v2/dref-files/multiple/",
"/api/v2/field-report/",
"/api/v2/flash-update-file/",
"/api/v2/per-file/",
"/api/v2/share-flash-update/",
"/api/v2/add_cronjob_log/",
"/api/v2/profile/",
"/api/v2/subscription/",
"/api/v2/user/",
]

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/",
]

self.authenticate(user=self.guest_user)
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])

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])

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])


class AuthTokenTest(APITestCase):
def setUp(self):
user = User.objects.create(username="jo")
Expand Down
3 changes: 2 additions & 1 deletion api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
Statuses,
)
from flash_update.models import FlashUpdate
from main.permissions import DenyGuestUserMutationPermission
from notifications.models import Subscription, SurgeAlert
from notifications.notification import send_notification
from registrations.models import Pending, Recovery
Expand Down Expand Up @@ -973,7 +974,7 @@ def post(self, request):

class AddCronJobLog(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permissions_classes = (permissions.IsAuthenticated,)
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]

def post(self, request):
errors, created = CronJob.sync_cron(request.data)
Expand Down
3 changes: 2 additions & 1 deletion deployments/drf_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from api.models import Country, Region
from api.view_filters import ListFilter
from api.visibility_class import ReadOnlyVisibilityViewsetMixin
from main.permissions import DenyGuestUserMutationPermission
from main.serializers import CsvListMixin
from main.utils import is_tableau

Expand Down Expand Up @@ -434,7 +435,7 @@ def get_permissions(self):
if self.action in ["list", "retrieve"]:
permission_classes = []
else:
permission_classes = [IsAuthenticated]
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]
return [permission() for permission in permission_classes]


Expand Down
19 changes: 10 additions & 9 deletions dref/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
DrefShareUserSerializer,
MiniDrefSerializer,
)
from main.permissions import DenyGuestUserMutationPermission


def filter_dref_queryset_by_user_access(user, queryset):
Expand All @@ -58,7 +59,7 @@ def filter_dref_queryset_by_user_access(user, queryset):

class DrefViewSet(RevisionMixin, viewsets.ModelViewSet):
serializer_class = DrefSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]
filterset_class = DrefFilter

def get_queryset(self):
Expand All @@ -75,7 +76,7 @@ def get_queryset(self):
url_path="publish",
methods=["post"],
serializer_class=DrefSerializer,
permission_classes=[permissions.IsAuthenticated, PublishDrefPermission],
permission_classes=[permissions.IsAuthenticated, PublishDrefPermission, DenyGuestUserMutationPermission],
)
def get_published(self, request, pk=None, version=None):
dref = self.get_object()
Expand All @@ -88,7 +89,7 @@ def get_published(self, request, pk=None, version=None):

class DrefOperationalUpdateViewSet(RevisionMixin, viewsets.ModelViewSet):
serializer_class = DrefOperationalUpdateSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]
filterset_class = DrefOperationalUpdateFilter

def get_queryset(self):
Expand Down Expand Up @@ -122,7 +123,7 @@ def get_queryset(self):
url_path="publish",
methods=["post"],
serializer_class=DrefOperationalUpdateSerializer,
permission_classes=[permissions.IsAuthenticated, PublishDrefPermission],
permission_classes=[permissions.IsAuthenticated, PublishDrefPermission, DenyGuestUserMutationPermission],
)
def get_published(self, request, pk=None, version=None):
operational_update = self.get_object()
Expand All @@ -135,7 +136,7 @@ def get_published(self, request, pk=None, version=None):

class DrefFinalReportViewSet(RevisionMixin, viewsets.ModelViewSet):
serializer_class = DrefFinalReportSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]

def get_queryset(self):
user = self.request.user
Expand All @@ -154,7 +155,7 @@ def get_queryset(self):
url_path="publish",
methods=["post"],
serializer_class=DrefFinalReportSerializer,
permission_classes=[permissions.IsAuthenticated, PublishDrefPermission],
permission_classes=[permissions.IsAuthenticated, PublishDrefPermission, DenyGuestUserMutationPermission],
)
def get_published(self, request, pk=None, version=None):
field_report = self.get_object()
Expand All @@ -171,7 +172,7 @@ def get_published(self, request, pk=None, version=None):


class DrefFileViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
permission_class = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]
serializer_class = DrefFileSerializer

def get_queryset(self):
Expand All @@ -184,7 +185,7 @@ def get_queryset(self):
detail=False,
url_path="multiple",
methods=["POST"],
permission_classes=[permissions.IsAuthenticated],
permission_classes=[permissions.IsAuthenticated, DenyGuestUserMutationPermission],
)
def multiple_file(self, request, pk=None, version=None):
# converts querydict to original dict
Expand Down Expand Up @@ -225,7 +226,7 @@ def get_queryset(self):


class DrefShareView(views.APIView):
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]

@extend_schema(request=AddDrefUserSerializer, responses=None)
def post(self, request):
Expand Down
9 changes: 5 additions & 4 deletions flash_update/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from rest_framework.response import Response

from api.serializers import ActionSerializer
from main.permissions import DenyGuestUserMutationPermission

from .filter_set import FlashUpdateFilter
from .models import (
Expand All @@ -38,7 +39,7 @@

class FlashUpdateViewSet(viewsets.ModelViewSet):
serializer_class = FlashUpdateSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]
filterset_class = FlashUpdateFilter

def get_queryset(self):
Expand Down Expand Up @@ -68,7 +69,7 @@ def get_queryset(self):


class FlashUpdateFileViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
permission_class = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]
serializer_class = FlashGraphicMapSerializer

def get_queryset(self):
Expand All @@ -79,7 +80,7 @@ def get_queryset(self):
detail=False,
url_path="multiple",
methods=["POST"],
permission_classes=[permissions.IsAuthenticated],
permission_classes=[permissions.IsAuthenticated, DenyGuestUserMutationPermission],
)
def multiple_file(self, request, pk=None, version=None):
files = [files[0] for files in dict((request.data).lists()).values()]
Expand Down Expand Up @@ -112,7 +113,7 @@ class DonorsViewSet(viewsets.ReadOnlyModelViewSet):
class ShareFlashUpdateViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = FlashUpdateShare.objects.all()
serializer_class = ShareFlashUpdateSerializer
permission_class = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]


class ExportFlashUpdateView(views.APIView):
Expand Down
4 changes: 3 additions & 1 deletion lang/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import action as djaction

from main.permissions import DenyGuestUserMutationPermission

from .models import String
from .permissions import LangStringPermission
from .serializers import (
Expand All @@ -24,7 +26,7 @@
class LanguageViewSet(viewsets.ViewSet):
# TODO: Cache retrive response to file
authentication_classes = (TokenAuthentication,)
permission_classes = (LangStringPermission,)
permission_classes = (LangStringPermission, DenyGuestUserMutationPermission)
lookup_url_kwarg = "pk"

@extend_schema(request=None, responses=LanguageListSerializer)
Expand Down
5 changes: 3 additions & 2 deletions local_units/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
PrivateLocalUnitDetailSerializer,
PrivateLocalUnitSerializer,
)
from main.permissions import DenyGuestUserMutationPermission


class PrivateLocalUnitViewSet(viewsets.ModelViewSet):
Expand All @@ -47,7 +48,7 @@ class PrivateLocalUnitViewSet(viewsets.ModelViewSet):
"local_branch_name",
"english_branch_name",
)
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedForLocalUnit]
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedForLocalUnit, DenyGuestUserMutationPermission]

def get_serializer_class(self):
if self.action == "list":
Expand All @@ -63,7 +64,7 @@ def destroy(self, request, *args, **kwargs):
url_path="validate",
methods=["post"],
serializer_class=PrivateLocalUnitSerializer,
permission_classes=[permissions.IsAuthenticated, ValidateLocalUnitPermission],
permission_classes=[permissions.IsAuthenticated, ValidateLocalUnitPermission, DenyGuestUserMutationPermission],
)
def get_validate(self, request, pk=None, version=None):
local_unit = self.get_object()
Expand Down
Loading

0 comments on commit f7d6e8b

Please sign in to comment.