Skip to content

Commit e3cf7df

Browse files
authored
Merge pull request #2573 from IFRCGo/feat/add-edit-permission-for-country-admin
Add permission for country admin to edit local unit
2 parents 2960f07 + 26daaa2 commit e3cf7df

File tree

2 files changed

+120
-3
lines changed

2 files changed

+120
-3
lines changed

local_units/permissions.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from django.contrib.auth.models import Permission
12
from rest_framework import permissions
23

34
from api.models import Country
@@ -24,7 +25,7 @@ def has_object_permission(self, request, view, obj):
2425

2526

2627
class IsAuthenticatedForLocalUnit(permissions.BasePermission):
27-
message = "Only validators or superusers are allowed to update Local Units"
28+
message = "Only Country Admins, Local Unit Validators, Region Admins, or Superusers are allowed to update Local Units."
2829

2930
def has_object_permission(self, request, view, obj):
3031
if request.method not in ["PUT", "PATCH"]:
@@ -35,6 +36,28 @@ def has_object_permission(self, request, view, obj):
3536
if user.is_superuser:
3637
return True
3738

39+
country_id = obj.country_id
40+
country = Country.objects.get(id=country_id)
41+
region_id = country.region_id
42+
# Country admin specific permissions
43+
country_admin_ids = [
44+
int(codename.replace("country_admin_", ""))
45+
for codename in Permission.objects.filter(
46+
group__user=user,
47+
codename__startswith="country_admin_",
48+
).values_list("codename", flat=True)
49+
]
50+
# Regional admin specific permissions
51+
region_admin_ids = [
52+
int(codename.replace("region_admin_", ""))
53+
for codename in Permission.objects.filter(
54+
group__user=user,
55+
codename__startswith="region_admin_",
56+
).values_list("codename", flat=True)
57+
]
58+
if country_id in country_admin_ids or region_id in region_admin_ids:
59+
return True
60+
3861
return (
3962
get_local_unit_country_validators(obj).filter(id=user.id).exists()
4063
or get_local_unit_region_validators(obj).filter(id=user.id).exists()

local_units/test_views.py

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,21 +428,28 @@ def setUp(self):
428428
)
429429

430430
self.local_unit_type = LocalUnitType.objects.create(code=1, name="administrative")
431-
431+
management.call_command("make_permissions")
432432
management.call_command("make_local_unit_validator_permissions")
433-
433+
self.country_admin_user = UserFactory.create()
434434
self.country_validator_user = UserFactory.create()
435435
self.region_validator_user = UserFactory.create()
436436
self.global_validator_user = UserFactory.create()
437437
# permissions
438+
country_admin_codename = f"country_admin_{self.country.id}"
438439
country_codename = f"local_unit_country_validator_{self.local_unit_type.id}_{self.country.id}"
439440
region_codename = f"local_unit_region_validator_{self.local_unit_type.id}_{self.region.id}"
440441
global_codename = f"local_unit_global_validator_{self.local_unit_type.id}"
441442

443+
country_admin_permission = Permission.objects.get(codename=country_admin_codename)
442444
country_permission = Permission.objects.get(codename=country_codename)
443445
region_permission = Permission.objects.get(codename=region_codename)
444446
global_permission = Permission.objects.get(codename=global_codename)
445447

448+
# Country admin group
449+
country__admin_group_name = "%s Admins" % self.country.name
450+
country__admin_group = Group.objects.get(name=country__admin_group_name)
451+
country__admin_group.permissions.add(country_admin_permission)
452+
self.country_admin_user.groups.add(country__admin_group)
446453
# Country validator group
447454
country_group_name = f"Local unit validator for {self.local_unit_type.name} {self.country.name}"
448455
country_group = Group.objects.get(name=country_group_name)
@@ -875,6 +882,93 @@ def test_create_local_unit_with_externally_managed_country_and_type(self):
875882
self.assertEqual(response.status_code, 400)
876883
self.assertEqual(LocalUnitChangeRequest.objects.count(), 0)
877884

885+
def test_country_region_admin_permission_for_local_unit_update(self):
886+
region1 = RegionFactory.create(name=2, label="Asia Pacific")
887+
888+
region2 = RegionFactory.create(name=0, label="Africa")
889+
890+
country = CountryFactory.create(
891+
name="India",
892+
iso3="IND",
893+
record_type=CountryType.COUNTRY,
894+
is_deprecated=False,
895+
independent=True,
896+
region=region1,
897+
)
898+
self.asia_admin = UserFactory.create(email="[email protected]")
899+
self.africa_admin = UserFactory.create(email="[email protected]")
900+
self.india_admin = UserFactory.create(email="[email protected]")
901+
# India admin setup
902+
management.call_command("make_permissions")
903+
country_admin_codename = f"country_admin_{country.id}"
904+
country_admin_permission = Permission.objects.get(codename=country_admin_codename)
905+
country_admin_group_name = "%s Admins" % country.name
906+
country_admin_group = Group.objects.get(name=country_admin_group_name)
907+
country_admin_group.permissions.add(country_admin_permission)
908+
self.india_admin.groups.add(country_admin_group)
909+
# Asia admin
910+
asia_admin_codename = f"region_admin_{region1.id}"
911+
asia_admin_permission = Permission.objects.get(codename=asia_admin_codename)
912+
asia_admin_group_name = "%s Regional Admins" % region1.name
913+
asia_admin_group = Group.objects.get(name=asia_admin_group_name)
914+
asia_admin_group.permissions.add(asia_admin_permission)
915+
self.asia_admin.groups.add(asia_admin_group)
916+
917+
# Africa admin
918+
africa_admin_codename = f"region_admin_{region2.id}"
919+
africa_admin_permission = Permission.objects.get(codename=africa_admin_codename)
920+
africa_admin_group_name = "%s Regional Admins" % region2.name
921+
africa_admin_group = Group.objects.get(name=africa_admin_group_name)
922+
africa_admin_group.permissions.add(africa_admin_permission)
923+
self.africa_admin.groups.add(africa_admin_group)
924+
925+
local_unit = LocalUnitFactory.create(
926+
country=country,
927+
type=self.local_unit_type,
928+
draft=False,
929+
status=LocalUnit.Status.VALIDATED,
930+
date_of_data="2023-08-08",
931+
)
932+
local_unit2 = LocalUnitFactory.create(
933+
country=country,
934+
type=self.local_unit_type,
935+
draft=False,
936+
status=LocalUnit.Status.VALIDATED,
937+
date_of_data="2023-08-08",
938+
)
939+
url = f"/api/v2/local-units/{local_unit.id}/"
940+
data = {
941+
"local_branch_name": "Updated local branch name",
942+
"update_reason_overview": "Needed update for testing",
943+
"type": self.local_unit_type.id,
944+
"location_json": {
945+
"lat": 20.5937,
946+
"lng": 78.9629,
947+
},
948+
}
949+
response = self.client.patch(url, data=data, format="json")
950+
self.assert_401(response)
951+
# Test: different country admin
952+
self.authenticate(self.country_admin_user)
953+
response = self.client.patch(url, data=data, format="json")
954+
self.assert_403(response)
955+
# Test: actual country admin
956+
self.authenticate(self.india_admin)
957+
response = self.client.patch(url, data=data, format="json")
958+
self.assert_200(response)
959+
self.assertEqual(response.data["local_branch_name"], "Updated local branch name")
960+
961+
url = f"/api/v2/local-units/{local_unit2.id}/"
962+
# Test: different region admin
963+
self.authenticate(self.africa_admin)
964+
response = self.client.patch(url, data=data, format="json")
965+
self.assert_403(response)
966+
# Test: same region admin
967+
self.authenticate(self.asia_admin)
968+
response = self.client.patch(url, data=data, format="json")
969+
self.assert_200(response)
970+
self.assertEqual(response.data["local_branch_name"], "Updated local branch name")
971+
878972

879973
class TestExternallyManagedLocalUnit(APITestCase):
880974
def setUp(self):

0 commit comments

Comments
 (0)