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 Jul 1, 2024
1 parent b26019f commit 45cd5fb
Show file tree
Hide file tree
Showing 15 changed files with 190 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
31 changes: 31 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,31 @@
# Generated by Django 4.2.13 on 2024-07-01 09:17

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=False,
help_text="User having this value as true explictly is treated as guest user.Despite of having all the permission, if this value is set to true, the user is deprived of all the non-guest user permission.",
verbose_name="limit access to guest user permissions",
),
),
migrations.AlterField(
model_name="profile",
name="limit_access_to_guest",
field=models.BooleanField(
default=True,
help_text="User having this value as true explictly is treated as guest user.Despite of having all the permission, if this value is set to true, the user is deprived of all the non-guest user permission.",
verbose_name="limit access to guest user permissions",
),
),
]
6 changes: 6 additions & 0 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1836,6 +1836,12 @@ 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(
help_text="User having this value as true explictly is treated as guest user.Despite of having all the \
permission, if this value is set to true, the user is deprived of all the non-guest user permission.",
verbose_name=_("limit access to guest user permissions"),
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
78 changes: 78 additions & 0 deletions api/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,87 @@
EventFeaturedDocumentFactory,
EventLinkFactory,
)
from api.models import Profile
from main.test_case import APITestCase, SnapshotTestCase


class GuestUserPermissionTest(APITestCase):
def setUp(self):
# Create guest user
self.guest_user = User.objects.create(username="guest")
guest_profile = Profile.objects.get(user=self.guest_user)
guest_profile.limit_access_to_guest = True
guest_profile.save()

# Create go user
self.go_user = User.objects.create(username="go-user")
go_user_profile = Profile.objects.get(user=self.go_user)
go_user_profile.limit_access_to_guest = False
go_user_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
Loading

0 comments on commit 45cd5fb

Please sign in to comment.