Skip to content

Commit c9be21e

Browse files
Merge pull request #2246 from IFRCGo/fix/user-guest-permission-report
Fix guest user permissions
2 parents f8ffd2d + 506e7c3 commit c9be21e

File tree

12 files changed

+144
-78
lines changed

12 files changed

+144
-78
lines changed

api/drf_views.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
from deployments.models import Personnel
6060
from main.enums import GlobalEnumSerializer, get_enum_values
6161
from main.filters import NullsLastOrderingFilter
62-
from main.permissions import DenyGuestUserMutationPermission
62+
from main.permissions import DenyGuestUserMutationPermission, DenyGuestUserPermission
6363
from main.utils import is_tableau
6464
from per.models import Overview
6565
from per.serializers import CountryLatestOverviewSerializer
@@ -871,7 +871,7 @@ def get_serializer_class(self):
871871
class ProfileViewset(viewsets.ModelViewSet):
872872
serializer_class = ProfileSerializer
873873
authentication_classes = (TokenAuthentication,)
874-
permission_classes = (IsAuthenticated, DenyGuestUserMutationPermission)
874+
permission_classes = (IsAuthenticated, DenyGuestUserPermission)
875875

876876
def get_queryset(self):
877877
return Profile.objects.filter(user=self.request.user)
@@ -880,7 +880,7 @@ def get_queryset(self):
880880
class UserViewset(viewsets.ModelViewSet):
881881
serializer_class = UserSerializer
882882
authentication_classes = (TokenAuthentication,)
883-
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]
883+
permission_classes = [IsAuthenticated, DenyGuestUserPermission]
884884

885885
def get_queryset(self):
886886
return User.objects.filter(pk=self.request.user.pk)
@@ -912,7 +912,7 @@ class FieldReportViewset(ReadOnlyVisibilityViewsetMixin, viewsets.ModelViewSet):
912912
) # for /docs
913913
ordering_fields = ("summary", "event", "dtype", "created_at", "updated_at")
914914
filterset_class = FieldReportFilter
915-
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]
915+
permission_classes = [DenyGuestUserMutationPermission]
916916
queryset = FieldReport.objects.select_related("dtype", "event").prefetch_related(
917917
"actions_taken",
918918
"actions_taken__actions",
@@ -1315,7 +1315,7 @@ class UsersViewset(viewsets.ReadOnlyModelViewSet):
13151315
"""
13161316

13171317
serializer_class = UserSerializer
1318-
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]
1318+
permission_classes = [IsAuthenticated, DenyGuestUserPermission]
13191319
filterset_class = UserFilterSet
13201320

13211321
def get_queryset(self):
@@ -1353,7 +1353,7 @@ def get(self, _):
13531353

13541354
class ExportViewSet(viewsets.ModelViewSet):
13551355
serializer_class = ExportSerializer
1356-
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]
1356+
permission_classes = [IsAuthenticated, DenyGuestUserPermission]
13571357

13581358
def get_queryset(self):
13591359
user = self.request.user

api/test_views.py

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
EventFeaturedDocumentFactory,
1111
EventLinkFactory,
1212
)
13-
from api.models import Profile
13+
from api.factories.field_report import FieldReportFactory
14+
from api.models import Profile, VisibilityChoices
1415
from deployments.factories.user import UserFactory
1516
from main.test_case import APITestCase, SnapshotTestCase
1617

@@ -24,21 +25,34 @@ def setUp(self):
2425
guest_profile.save()
2526

2627
# Create go user
27-
self.go_user = User.objects.create(username="go-user")
28+
self.go_user = User.objects.create(username="go-user", is_superuser=True, is_staff=True)
2829
go_user_profile = Profile.objects.get(user=self.go_user)
2930
go_user_profile.limit_access_to_guest = False
3031
go_user_profile.save()
3132

33+
# Create public field reports
34+
FieldReportFactory.create_batch(4, visibility=VisibilityChoices.PUBLIC)
35+
# Create non-public field reports
36+
FieldReportFactory.create_batch(5, visibility=VisibilityChoices.IFRC)
37+
3238
def test_guest_user_permission(self):
3339
body = {}
40+
id = 1 # NOTE: id is used just to test api that requires id, it doesnot indicate real id. It can be any number.
41+
3442
guest_apis = [
3543
"/api/v2/add_subscription/",
3644
"/api/v2/del_subscription/",
3745
"/api/v2/external-token/",
46+
]
47+
guest_get_apis = [
3848
"/api/v2/user/me/",
49+
"/api/v2/field-report/",
50+
f"/api/v2/field-report/{id}/",
51+
"/api/v2/language/",
52+
f"/api/v2/language/{id}/",
3953
]
40-
id = 1 # NOTE: id is used just to test api that requires id, it doesnot indicate real id. It can be any number.
41-
go_apis = [
54+
55+
go_post_apis = [
4256
"/api/v2/dref/",
4357
"/api/v2/dref-final-report/",
4458
f"/api/v2/dref-final-report/{id}/publish/",
@@ -76,13 +90,9 @@ def test_guest_user_permission(self):
7690
f"/api/v2/dref-final-report/{id}/",
7791
"/api/v2/dref-op-update/",
7892
f"/api/v2/dref/{id}/",
79-
"/api/v2/field-report/",
80-
f"/api/v2/field-report/{id}/",
8193
"/api/v2/flash-update/",
8294
"/api/v2/flash-update-file/",
8395
f"/api/v2/flash-update/{id}/",
84-
"/api/v2/language/",
85-
f"/api/v2/language/{id}/",
8696
"/api/v2/local-units/",
8797
f"/api/v2/local-units/{id}/",
8898
"/api/v2/ops-learning/",
@@ -106,6 +116,15 @@ def test_guest_user_permission(self):
106116
f"/api/v2/subscription/{id}/",
107117
"/api/v2/users/",
108118
f"/api/v2/users/{id}/",
119+
"/api/v2/per-stats/",
120+
"/api/v2/per-options/",
121+
"/api/v2/per-process-status/",
122+
"/api/v2/aggregated-per-process-status/",
123+
"/api/v2/completed-dref/",
124+
"/api/v2/active-dref/",
125+
"/api/v2/dref-share-user/",
126+
"/api/v2/personnel_deployment/",
127+
f"/api/v2/delegation-office/{id}/",
109128
# Exports
110129
f"/api/v2/export-flash-update/{1}/",
111130
]
@@ -115,56 +134,82 @@ def test_guest_user_permission(self):
115134
f"/api/v2/export-per/{1}/",
116135
]
117136

118-
go_apis_req_additional_perm = [
137+
go_post_apis_req_additional_perm = [
119138
"/api/v2/ops-learning/",
120139
"/api/v2/per-overview/",
121140
f"/api/v2/user/{id}/accepted_license_terms/",
122-
f"/api/v2/language/{id}/bulk-action/",
123141
]
124142

125-
self.authenticate(user=self.guest_user)
126-
127143
def _success_check(response): # NOTE: Only handles json responses
128144
self.assertNotIn(response.status_code, [401, 403], response.content)
129145
self.assertNotIn(response.json().get("error_code"), [401, 403], response.content)
130146

131-
def _failure_check(response, is_json=True):
147+
def _failure_check(response, check_json_error_code=True):
132148
self.assertIn(response.status_code, [401, 403], response.content)
133-
if is_json:
149+
if check_json_error_code:
134150
self.assertIn(response.json()["error_code"], [401, 403], response.content)
135151

152+
# check for unauthenticated user
153+
# Unauthenticated user should be able to view public field reports
154+
field_report_pub_response = self.client.get("/api/v2/field-report/")
155+
_success_check(field_report_pub_response)
156+
self.assertEqual(len(field_report_pub_response.json()["results"]), 4)
157+
158+
# Unauthenticated user should be not be able to do post operations in field reports
159+
field_report_pub_response = self.client.post("/api/v2/field-report/", json=body)
160+
_failure_check(field_report_pub_response, check_json_error_code=False)
161+
162+
# authenticate guest user
163+
self.authenticate(user=self.guest_user)
164+
136165
for api_url in get_custom_negotiation_apis:
137166
headers = {
138167
"Accept": "text/html",
139168
}
140169
response = self.client.get(api_url, headers=headers, stream=True)
141-
_failure_check(response, is_json=False)
170+
_failure_check(response, check_json_error_code=False)
142171

143-
# Guest user should not be able to access get apis that requires IsAuthenticated permission
172+
# # Guest user should not be able to access get apis that requires IsAuthenticated permission
144173
for api_url in get_apis:
145174
response = self.client.get(api_url)
146175
_failure_check(response)
147176

148-
# Guest user should not be able to hit post apis.
149-
for api_url in go_apis + go_apis_req_additional_perm:
177+
# # Guest user should not be able to hit post apis.
178+
for api_url in go_post_apis + go_post_apis_req_additional_perm:
150179
response = self.client.post(api_url, json=body)
151180
_failure_check(response)
152181

153-
# Guest user should be able to access guest apis
182+
# Guest user should be able to access guest post apis
154183
for api_url in guest_apis:
155184
response = self.client.post(api_url, json=body)
156185
_success_check(response)
157186

158-
# Go user should be able to access go_apis
187+
# Guest user should be able to access guest get apis
188+
for api_url in guest_get_apis:
189+
response = self.client.get(api_url)
190+
_success_check(response)
191+
192+
# Guest user should be able to view only public field reports
193+
field_report_pub_response = self.client.get("/api/v2/field-report/")
194+
_success_check(field_report_pub_response)
195+
self.assertEqual(len(field_report_pub_response.json()["results"]), 4)
196+
197+
# authenticate ifrc go user
198+
# Go user should be able to access go_post_apis
159199
self.authenticate(user=self.go_user)
160-
for api_url in go_apis:
200+
for api_url in go_post_apis:
161201
response = self.client.post(api_url, json=body)
162202
_success_check(response)
163203

164204
for api_url in get_apis:
165205
response = self.client.get(api_url)
166206
_success_check(response)
167207

208+
# Go user should be able to view both public + non-public field reports
209+
field_report_response = self.client.get("/api/v2/field-report/")
210+
_success_check(field_report_response)
211+
self.assertEqual(len(field_report_response.json()["results"]), 9)
212+
168213

169214
class AuthTokenTest(APITestCase):
170215
def setUp(self):

api/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
Statuses,
4444
)
4545
from flash_update.models import FlashUpdate
46-
from main.permissions import DenyGuestUserMutationPermission
46+
from main.permissions import DenyGuestUserPermission
4747
from notifications.models import Subscription, SurgeAlert
4848
from notifications.notification import send_notification
4949
from registrations.models import Pending, Recovery
@@ -977,7 +977,7 @@ def post(self, request):
977977

978978
class AddCronJobLog(APIView):
979979
authentication_classes = (authentication.TokenAuthentication,)
980-
permission_classes = [permissions.IsAuthenticated, DenyGuestUserMutationPermission]
980+
permission_classes = [permissions.IsAuthenticated, DenyGuestUserPermission]
981981

982982
def post(self, request):
983983
errors, created = CronJob.sync_cron(request.data)

databank/views.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from rest_framework.authentication import BasicAuthentication, TokenAuthentication
33
from rest_framework.permissions import IsAuthenticated
44

5+
from main.permissions import DenyGuestUserPermission
6+
57
from .filter_set import FDRSIncomeFilter
68
from .models import CountryOverview, FDRSIncome
79
from .serializers import CountryOverviewSerializer, FDRSIncomeSerializer
@@ -11,7 +13,7 @@ class CountryOverviewViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet)
1113
queryset = CountryOverview.objects.all()
1214
# TODO: Use global authentication class
1315
authentication_classes = (BasicAuthentication, TokenAuthentication)
14-
permission_classes = (IsAuthenticated,)
16+
permission_classes = (IsAuthenticated, DenyGuestUserPermission)
1517
serializer_class = CountryOverviewSerializer
1618
lookup_field = "country__iso__iexact"
1719

deployments/drf_views.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from api.models import Country, Region
2424
from api.view_filters import ListFilter
2525
from api.visibility_class import ReadOnlyVisibilityViewsetMixin
26-
from main.permissions import DenyGuestUserMutationPermission
26+
from main.permissions import DenyGuestUserPermission
2727
from main.serializers import CsvListMixin
2828
from main.utils import is_tableau
2929

@@ -143,7 +143,7 @@ class Meta:
143143

144144
class PersonnelDeploymentViewset(viewsets.ReadOnlyModelViewSet):
145145
authentication_classes = (TokenAuthentication,)
146-
permission_classes = (IsAuthenticated,)
146+
permission_classes = (IsAuthenticated, DenyGuestUserPermission)
147147
queryset = PersonnelDeployment.objects.all()
148148
serializer_class = PersonnelDeploymentSerializer
149149
filterset_class = PersonnelDeploymentFilter
@@ -456,7 +456,7 @@ def get_permissions(self):
456456
if self.action in ["list", "retrieve"]:
457457
permission_classes = []
458458
else:
459-
permission_classes = [IsAuthenticated, DenyGuestUserMutationPermission]
459+
permission_classes = [IsAuthenticated, DenyGuestUserPermission]
460460
return [permission() for permission in permission_classes]
461461

462462

0 commit comments

Comments
 (0)