Skip to content

Commit

Permalink
Add guest permission in GET apis.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rup-Narayan-Rajbanshi committed Jul 31, 2024
1 parent e04a84d commit 16c2434
Show file tree
Hide file tree
Showing 14 changed files with 119 additions and 57 deletions.
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ exclude: |
(?x)^(
\.git|
__pycache__|
.*snap_test_.*\.py|
.+\/.+\/migrations\/.*|
legacy|
\.venv
Expand Down
10 changes: 3 additions & 7 deletions api/drf_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# 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


class Migration(migrations.Migration):

dependencies = [
("api", "0210_profile_accepted_montandon_license_terms"),
("api", "0211_alter_countrydirectory_unique_together_and_more"),
]

operations = [
Expand All @@ -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",
),
),
Expand Down
6 changes: 4 additions & 2 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down
20 changes: 10 additions & 10 deletions api/snapshots/snap_test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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": [],
Expand All @@ -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",
Expand All @@ -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": [],
Expand Down Expand Up @@ -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",
Expand Down
84 changes: 74 additions & 10 deletions api/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/",
Expand All @@ -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):
Expand Down Expand Up @@ -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="[email protected]", password="12345678")
super_user = UserFactory(username="baz", email="[email protected]", 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)
Expand Down
4 changes: 2 additions & 2 deletions api/visibility_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
12 changes: 9 additions & 3 deletions dref/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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")
Expand Down Expand Up @@ -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

Expand Down
11 changes: 5 additions & 6 deletions flash_update/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -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")
Expand Down
4 changes: 3 additions & 1 deletion flash_update/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading

0 comments on commit 16c2434

Please sign in to comment.