diff --git a/care/facility/api/serializers/bed.py b/care/facility/api/serializers/bed.py index ea8808785b..383c208862 100644 --- a/care/facility/api/serializers/bed.py +++ b/care/facility/api/serializers/bed.py @@ -26,7 +26,6 @@ from care.facility.models.patient import PatientRegistration from care.facility.models.patient_base import BedTypeChoices from care.facility.models.patient_consultation import PatientConsultation -from care.users.models import User from care.utils.assetintegration.asset_classes import AssetClasses from care.utils.queryset.consultation import get_consultation_queryset from care.utils.queryset.facility import get_facility_queryset @@ -186,9 +185,6 @@ def validate(self, attrs): user = self.context["request"].user bed = attrs["bed"] - if user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - raise ValidationError("You do not have permission to perform this action") - facilities = get_facility_queryset(user) if not facilities.filter(id=bed.facility_id).exists(): raise ValidationError("You do not have access to this facility") diff --git a/care/facility/api/serializers/file_upload.py b/care/facility/api/serializers/file_upload.py index ecc43ed1d6..9515885917 100644 --- a/care/facility/api/serializers/file_upload.py +++ b/care/facility/api/serializers/file_upload.py @@ -123,14 +123,6 @@ class Meta: ) write_only_fields = ("associating_id",) - def validate(self, attrs): - user = self.context["request"].user - if user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - raise serializers.ValidationError( - {"permission": "Only Nurses and above can upload files."} - ) - return super().validate(attrs) - def create(self, validated_data): user = self.context["request"].user internal_id = check_permissions( diff --git a/care/facility/api/serializers/patient_external_test.py b/care/facility/api/serializers/patient_external_test.py index df7e67d9d4..34bb886223 100644 --- a/care/facility/api/serializers/patient_external_test.py +++ b/care/facility/api/serializers/patient_external_test.py @@ -97,14 +97,6 @@ def validate_empty_values(self, data, *args, **kwargs): return super().validate_empty_values(data, *args, **kwargs) - def validate(self, attrs): - user = self.context["request"].user - if user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - raise ValidationError( - {"user": ["User is not allowed to perform this action"]} - ) - return super().validate(attrs) - def create(self, validated_data): if "srf_id" in validated_data: if PatientRegistration.objects.filter( diff --git a/care/facility/api/serializers/patient_investigation.py b/care/facility/api/serializers/patient_investigation.py index 5e111b1131..bc209b1e68 100644 --- a/care/facility/api/serializers/patient_investigation.py +++ b/care/facility/api/serializers/patient_investigation.py @@ -7,7 +7,6 @@ PatientInvestigation, PatientInvestigationGroup, ) -from care.users.models import User class PatientInvestigationGroupSerializer(serializers.ModelSerializer): @@ -60,14 +59,6 @@ class Meta: ) exclude = TIMESTAMP_FIELDS + ("external_id",) - def validate(self, attrs): - user = self.context["request"].user - if user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - raise serializers.ValidationError( - "You do not have permission to perform this action" - ) - return super().validate(attrs) - def update(self, instance, validated_data): if instance.consultation.discharge_date: raise serializers.ValidationError( @@ -91,14 +82,6 @@ class Meta: read_only_fields = TIMESTAMP_FIELDS exclude = TIMESTAMP_FIELDS + ("external_id",) - def validate(self, attrs): - user = self.context["request"].user - if user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - raise serializers.ValidationError( - "You do not have permission to perform this action" - ) - return super().validate(attrs) - class ValueSerializer(serializers.ModelSerializer): class Meta: diff --git a/care/facility/api/viewsets/bed.py b/care/facility/api/viewsets/bed.py index 263f6e7627..eaae7a61e5 100644 --- a/care/facility/api/viewsets/bed.py +++ b/care/facility/api/viewsets/bed.py @@ -2,6 +2,7 @@ from django.db.models import OuterRef, Subquery from django_filters import rest_framework as filters from drf_spectacular.utils import extend_schema, extend_schema_view +from dry_rest_permissions.generics import DRYPermissions from rest_framework import filters as drf_filters from rest_framework import status from rest_framework.exceptions import PermissionDenied @@ -55,7 +56,7 @@ class BedViewSet( serializer_class = BedSerializer lookup_field = "external_id" filter_backends = (filters.DjangoFilterBackend, drf_filters.SearchFilter) - permission_classes = [IsAuthenticated] + permission_classes = (IsAuthenticated,) search_fields = ["name"] filterset_class = BedFilter @@ -215,6 +216,7 @@ class ConsultationBedViewSet( .order_by("-created_date") ) serializer_class = ConsultationBedSerializer + permission_classes = (DRYPermissions,) filter_backends = (filters.DjangoFilterBackend,) filterset_class = ConsultationBedFilter lookup_field = "external_id" diff --git a/care/facility/api/viewsets/file_upload.py b/care/facility/api/viewsets/file_upload.py index 5a238e476c..9419d96df1 100644 --- a/care/facility/api/viewsets/file_upload.py +++ b/care/facility/api/viewsets/file_upload.py @@ -1,4 +1,5 @@ from django_filters import rest_framework as filters +from dry_rest_permissions.generics import DRYPermissions from rest_framework.exceptions import ValidationError from rest_framework.mixins import ( CreateModelMixin, @@ -36,20 +37,23 @@ class FileUploadViewSet( queryset = ( FileUpload.objects.all().select_related("uploaded_by").order_by("-created_date") ) - permission_classes = [IsAuthenticated] + permission_classes = ( + IsAuthenticated, + DRYPermissions, + ) lookup_field = "external_id" filter_backends = (filters.DjangoFilterBackend,) filterset_class = FileUploadFilter + serializer_class = FileUploadUpdateSerializer def get_serializer_class(self): if self.action == "retrieve": return FileUploadRetrieveSerializer - elif self.action == "list": + if self.action == "list": return FileUploadListSerializer - elif self.action == "create": + if self.action == "create": return FileUploadCreateSerializer - else: - return FileUploadUpdateSerializer + return super().get_serializer_class() def get_queryset(self): if "file_type" not in self.request.GET: diff --git a/care/facility/api/viewsets/patient_external_test.py b/care/facility/api/viewsets/patient_external_test.py index 422d0bb512..0976160fcf 100644 --- a/care/facility/api/viewsets/patient_external_test.py +++ b/care/facility/api/viewsets/patient_external_test.py @@ -6,6 +6,7 @@ from django_filters.filters import DateFromToRangeFilter from djqscsv import render_to_csv_response from drf_spectacular.utils import extend_schema +from dry_rest_permissions.generics import DRYPermissions from rest_framework import status from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied, ValidationError @@ -77,7 +78,10 @@ class PatientExternalTestViewSet( .all() .order_by("-id") ) - permission_classes = (IsAuthenticated,) + permission_classes = ( + IsAuthenticated, + DRYPermissions, + ) filter_backends = (filters.DjangoFilterBackend,) filterset_class = PatientExternalTestFilter parser_classes = (MultiPartParser, FormParser, JSONParser) diff --git a/care/facility/api/viewsets/patient_investigation.py b/care/facility/api/viewsets/patient_investigation.py index d83fd8ebaa..c23e5c55cd 100644 --- a/care/facility/api/viewsets/patient_investigation.py +++ b/care/facility/api/viewsets/patient_investigation.py @@ -6,6 +6,7 @@ from django_filters import Filter from django_filters import rest_framework as filters from drf_spectacular.utils import extend_schema +from dry_rest_permissions.generics import DRYPermissions from rest_framework import mixins, status, viewsets from rest_framework.decorators import action from rest_framework.exceptions import ValidationError @@ -160,7 +161,10 @@ class InvestigationValueViewSet( serializer_class = InvestigationValueSerializer queryset = InvestigationValue.objects.all() lookup_field = "external_id" - permission_classes = (IsAuthenticated,) + permission_classes = ( + IsAuthenticated, + DRYPermissions, + ) filterset_class = PatientInvestigationFilter filter_backends = (filters.DjangoFilterBackend,) pagination_class = InvestigationValueSetPagination diff --git a/care/facility/api/viewsets/patient_sample.py b/care/facility/api/viewsets/patient_sample.py index 419e7a0126..4a3f0516c3 100644 --- a/care/facility/api/viewsets/patient_sample.py +++ b/care/facility/api/viewsets/patient_sample.py @@ -88,12 +88,11 @@ class PatientSampleViewSet( http_method_names = ["get", "post", "patch", "delete"] def get_serializer_class(self): - serializer_class = self.serializer_class if self.action == "retrieve": - serializer_class = PatientSampleDetailSerializer - elif self.action == "partial_update": - serializer_class = PatientSamplePatchSerializer - return serializer_class + return PatientSampleDetailSerializer + if self.action == "partial_update": + return PatientSamplePatchSerializer + return super().get_serializer_class() def get_queryset(self): queryset = super(PatientSampleViewSet, self).get_queryset() diff --git a/care/facility/api/viewsets/shifting.py b/care/facility/api/viewsets/shifting.py index e5e40d1b54..a5498b24af 100644 --- a/care/facility/api/viewsets/shifting.py +++ b/care/facility/api/viewsets/shifting.py @@ -122,7 +122,10 @@ class ShiftingViewSet( ) ordering_fields = ["id", "created_date", "modified_date", "emergency"] - permission_classes = (IsAuthenticated, DRYPermissions) + permission_classes = ( + IsAuthenticated, + DRYPermissions, + ) filter_backends = ( ShiftingFilterBackend, filters.DjangoFilterBackend, @@ -131,10 +134,9 @@ class ShiftingViewSet( filterset_class = ShiftingFilterSet def get_serializer_class(self): - serializer_class = self.serializer_class if self.action == "retrieve": - serializer_class = ShiftingDetailSerializer - return serializer_class + return ShiftingDetailSerializer + return super().get_serializer_class() @extend_schema(tags=["shift"]) @action(detail=True, methods=["POST"]) diff --git a/care/facility/models/bed.py b/care/facility/models/bed.py index 205127711f..f77ea17983 100644 --- a/care/facility/models/bed.py +++ b/care/facility/models/bed.py @@ -10,6 +10,7 @@ from care.facility.models.asset import Asset, AssetLocation from care.facility.models.facility import Facility +from care.facility.models.mixins.permissions.facility import FacilityUserPermissionMixin from care.facility.models.patient_base import BedType, BedTypeChoices from care.facility.models.patient_consultation import PatientConsultation from care.utils.models.base import BaseModel @@ -65,7 +66,7 @@ def __str__(self): return f"{self.asset.name} - {self.bed.name}" -class ConsultationBed(BaseModel): +class ConsultationBed(BaseModel, FacilityUserPermissionMixin): consultation = models.ForeignKey( PatientConsultation, on_delete=models.PROTECT, null=False, blank=False ) diff --git a/care/facility/models/file_upload.py b/care/facility/models/file_upload.py index 3fb68eac6a..4b76be2608 100644 --- a/care/facility/models/file_upload.py +++ b/care/facility/models/file_upload.py @@ -7,11 +7,12 @@ from django.db import models from care.facility.models import FacilityBaseModel +from care.facility.models.mixins.permissions.facility import FacilityPermissionMixin from care.users.models import User from care.utils.csp import config as cs_provider -class FileUpload(FacilityBaseModel): +class FileUpload(FacilityBaseModel, FacilityPermissionMixin): """ Stores data about all file uploads the file can belong to any type ie Patient , Consultation , Daily Round and so on ... diff --git a/care/facility/models/mixins/permissions/facility.py b/care/facility/models/mixins/permissions/facility.py index a2932ca6b5..5afd5a2126 100644 --- a/care/facility/models/mixins/permissions/facility.py +++ b/care/facility/models/mixins/permissions/facility.py @@ -129,3 +129,28 @@ def has_object_write_permission(self, request): or request.user.is_superuser or request.user in self.facility.users.all() ) + + +class FacilityUserPermissionMixin: + @staticmethod + def has_read_permission(request): + return True + + @staticmethod + def has_write_permission(request): + if request.user.user_type in READ_ONLY_USER_TYPES: + return False + + return ( + request.user.is_superuser + or request.user.user_type >= User.TYPE_VALUE_MAP["Nurse"] + ) + + def has_object_read_permission(self, request): + return True + + def has_object_write_permission(self, request): + return self.has_write_permission(request) + + def has_object_update_permission(self, request): + return self.has_write_permission(request) diff --git a/care/facility/models/patient_external_test.py b/care/facility/models/patient_external_test.py index 1e13c8f5c2..ed47c5cba8 100644 --- a/care/facility/models/patient_external_test.py +++ b/care/facility/models/patient_external_test.py @@ -1,10 +1,11 @@ from django.db import models from care.facility.models import FacilityBaseModel, pretty_boolean +from care.facility.models.mixins.permissions.facility import FacilityPermissionMixin from care.users.models import District, LocalBody, Ward -class PatientExternalTest(FacilityBaseModel): +class PatientExternalTest(FacilityBaseModel, FacilityPermissionMixin): srf_id = models.CharField(max_length=255) name = models.CharField(max_length=1000) age = models.IntegerField() diff --git a/care/facility/models/patient_investigation.py b/care/facility/models/patient_investigation.py index 9e94e23153..d54666185e 100644 --- a/care/facility/models/patient_investigation.py +++ b/care/facility/models/patient_investigation.py @@ -2,6 +2,7 @@ from django.db import models +from care.facility.models.mixins.permissions.facility import FacilityUserPermissionMixin from care.facility.models.patient_consultation import PatientConsultation from care.users.models import User from care.utils.models.base import BaseModel @@ -50,7 +51,7 @@ class Meta: ] -class InvestigationValue(BaseModel): +class InvestigationValue(BaseModel, FacilityUserPermissionMixin): investigation = models.ForeignKey( PatientInvestigation, on_delete=models.PROTECT, blank=False, null=False ) diff --git a/care/facility/models/patient_sample.py b/care/facility/models/patient_sample.py index 8482035e6a..5d2d46925d 100644 --- a/care/facility/models/patient_sample.py +++ b/care/facility/models/patient_sample.py @@ -2,6 +2,7 @@ from care.facility.models import FacilityBaseModel, PatientRegistration, reverse_choices from care.facility.models.base import READ_ONLY_USER_TYPES +from care.facility.models.mixins.permissions.facility import FacilityUserPermissionMixin from care.users.models import User SAMPLE_TYPE_CHOICES = [ @@ -19,8 +20,13 @@ REVERSE_SAMPLE_TYPE_CHOICES = reverse_choices(SAMPLE_TYPE_CHOICES) -class PatientSample(FacilityBaseModel): - SAMPLE_TEST_RESULT_MAP = {"POSITIVE": 1, "NEGATIVE": 2, "AWAITING": 3, "INVALID": 4} +class PatientSample(FacilityBaseModel, FacilityUserPermissionMixin): + SAMPLE_TEST_RESULT_MAP = { + "POSITIVE": 1, + "NEGATIVE": 2, + "AWAITING": 3, + "INVALID": 4, + } SAMPLE_TEST_RESULT_CHOICES = [(v, k) for k, v in SAMPLE_TEST_RESULT_MAP.items()] REVERSE_SAMPLE_TEST_RESULT_CHOICES = reverse_choices(SAMPLE_TEST_RESULT_CHOICES) @@ -151,15 +157,6 @@ def flow(self): except AttributeError: return self.patientsampleflow_set.order_by("-created_date") - @staticmethod - def has_write_permission(request): - if request.user.user_type in READ_ONLY_USER_TYPES: - return False - return ( - request.user.is_superuser - or request.user.user_type >= User.TYPE_VALUE_MAP["Nurse"] - ) - @staticmethod def has_read_permission(request): return ( diff --git a/care/facility/models/shifting.py b/care/facility/models/shifting.py index 2d6f92891f..12da9472bd 100644 --- a/care/facility/models/shifting.py +++ b/care/facility/models/shifting.py @@ -2,11 +2,11 @@ from care.facility.models import ( FACILITY_TYPES, - READ_ONLY_USER_TYPES, FacilityBaseModel, pretty_boolean, reverse_choices, ) +from care.facility.models.mixins.permissions.facility import FacilityUserPermissionMixin from care.users.models import User from care.utils.models.validators import mobile_or_landline_number_validator @@ -44,7 +44,7 @@ REVERSE_SHIFTING_STATUS_CHOICES = reverse_choices(SHIFTING_STATUS_CHOICES) -class ShiftingRequest(FacilityBaseModel): +class ShiftingRequest(FacilityBaseModel, FacilityUserPermissionMixin): origin_facility = models.ForeignKey( "Facility", on_delete=models.PROTECT, @@ -150,38 +150,9 @@ class Meta: models.Index(fields=["status", "deleted"]), ] - @staticmethod - def has_write_permission(request): - if request.user.user_type in READ_ONLY_USER_TYPES: - return False - if request.user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - return False - return True - - @staticmethod - def has_read_permission(request): - return True - - def has_object_read_permission(self, request): - return True - - def has_object_write_permission(self, request): - if request.user.user_type in READ_ONLY_USER_TYPES: - return False - if request.user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - return False - return True - def has_object_transfer_permission(self, request): return True - def has_object_update_permission(self, request): - if request.user.user_type in READ_ONLY_USER_TYPES: - return False - if request.user.user_type < User.TYPE_VALUE_MAP["Nurse"]: - return False - return True - class ShiftingRequestComment(FacilityBaseModel): request = models.ForeignKey(