diff --git a/care/facility/api/serializers/patient_sample.py b/care/facility/api/serializers/patient_sample.py index 2308dcac1b..f227ce731a 100644 --- a/care/facility/api/serializers/patient_sample.py +++ b/care/facility/api/serializers/patient_sample.py @@ -12,6 +12,7 @@ PatientSampleFlow, ) from care.users.api.serializers.user import UserBaseMinimumSerializer +from care.users.models import User from care.utils.serializer.external_id_field import ExternalIdSerializerField from config.serializers import ChoiceField @@ -103,7 +104,12 @@ class PatientSamplePatchSerializer(PatientSampleSerializer): notes = serializers.CharField(required=False) def update(self, instance, validated_data): - instance.last_edited_by = self.context["request"].user + user = self.context["request"].user + if user.user_type < User.TYPE_VALUE_MAP["Doctor"]: + raise ValidationError( + {"status": ["User is not allowed to update sample details"]} + ) + instance.last_edited_by = user try: is_completed = validated_data.get("result") in [1, 2] new_status = validated_data.get( 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/notification.py b/care/facility/api/viewsets/notification.py index 05f42ff6e0..3281255a71 100644 --- a/care/facility/api/viewsets/notification.py +++ b/care/facility/api/viewsets/notification.py @@ -13,6 +13,7 @@ from care.facility.api.serializers.notification import NotificationSerializer from care.facility.models.notification import Notification +from care.users.models import User from care.utils.filters.choicefilter import CareChoiceFilter, inverse_choices from care.utils.notification_handler import NotificationGenerator from care.utils.queryset.facility import get_facility_queryset @@ -71,6 +72,10 @@ def notify(self, request, *args, **kwargs): raise ValidationError({"facility": "is required"}) if "message" not in request.data or request.data["message"] == "": raise ValidationError({"message": "is required"}) + if user.user_type < User.TYPE_VALUE_MAP["Doctor"] and request.data["facility"]: + raise ValidationError( + {"user": "You are not allowed to notify other hospitals"} + ) facilities = get_facility_queryset(user) facility = get_object_or_404( facilities.filter(external_id=request.data["facility"]) diff --git a/care/facility/api/viewsets/patient_external_test.py b/care/facility/api/viewsets/patient_external_test.py index 4587dc2992..7009dd3f1c 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 f575a33fe2..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 @@ -82,7 +83,7 @@ class PatientInvestigationViewSet( pagination_class = InvestigationResultsSetPagination -class PatientInvestigationFilter(filters.FilterSet): +class PatientInvestigationSummaryFilter(filters.FilterSet): created_date = filters.DateFromToRangeFilter(field_name="created_date") modified_date = filters.DateFromToRangeFilter(field_name="modified_date") investigation = filters.CharFilter(field_name="investigation__external_id") @@ -102,7 +103,7 @@ class PatientInvestigationSummaryViewSet( queryset = InvestigationValue.objects.all() lookup_field = "external_id" permission_classes = (IsAuthenticated,) - filterset_class = PatientInvestigationFilter + filterset_class = PatientInvestigationSummaryFilter filter_backends = (filters.DjangoFilterBackend,) pagination_class = InvestigationSummaryResultsSetPagination SESSION_PER_PAGE = 5 @@ -124,16 +125,19 @@ def get_queryset(self): * self.SESSION_PER_PAGE ] ) - if not sessions.exists(): + if ( + not sessions.exists() + or self.request.user.user_type < User.TYPE_VALUE_MAP["Nurse"] + ): return self.queryset.none() queryset = queryset.filter(session_id__in=sessions.values("session_id")) if self.request.user.is_superuser: return queryset - elif self.request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]: + if self.request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]: return queryset.filter( consultation__patient__facility__state=self.request.user.state ) - elif self.request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"]: + if self.request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"]: return queryset.filter( consultation__patient__facility__district=self.request.user.district ) @@ -157,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 @@ -173,11 +180,11 @@ def get_queryset(self): ) if self.request.user.is_superuser: return queryset - elif self.request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]: + if self.request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]: return queryset.filter( consultation__patient__facility__state=self.request.user.state ) - elif self.request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"]: + if self.request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"]: return queryset.filter( consultation__patient__facility__district=self.request.user.district ) 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/base.py b/care/facility/models/base.py index a8993980c9..6928ca54d1 100644 --- a/care/facility/models/base.py +++ b/care/facility/models/base.py @@ -2,8 +2,9 @@ from care.utils.models.base import BaseModel READ_ONLY_USER_TYPES = [ - User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"], User.TYPE_VALUE_MAP["StateReadOnlyAdmin"], + User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"], + User.TYPE_VALUE_MAP["NurseReadOnly"], User.TYPE_VALUE_MAP["StaffReadOnly"], ] 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/daily_round.py b/care/facility/models/daily_round.py index 0b95a0720d..0e8c132aa6 100644 --- a/care/facility/models/daily_round.py +++ b/care/facility/models/daily_round.py @@ -12,7 +12,7 @@ COVID_CATEGORY_CHOICES, PatientBaseModel, ) -from care.facility.models.base import covert_choice_dict +from care.facility.models.base import READ_ONLY_USER_TYPES, covert_choice_dict from care.facility.models.bed import AssetBed from care.facility.models.json_schema.daily_round import ( BLOOD_PRESSURE, @@ -522,11 +522,9 @@ def save(self, *args, **kwargs): @staticmethod def has_write_permission(request): if "/analyse" not in request.get_full_path(): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + 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 DailyRound.has_read_permission(request) @@ -582,11 +580,9 @@ def has_object_read_permission(self, request): ) def has_object_write_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + 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 ( request.user.is_superuser 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/asset.py b/care/facility/models/mixins/permissions/asset.py index 8affe28606..9b9877606d 100644 --- a/care/facility/models/mixins/permissions/asset.py +++ b/care/facility/models/mixins/permissions/asset.py @@ -1,7 +1,7 @@ from dry_rest_permissions.generics import DRYPermissions +from care.facility.models.base import READ_ONLY_USER_TYPES from care.facility.models.mixins.permissions.base import BasePermissionMixin -from care.users.models import User class IsAssetUser: @@ -30,11 +30,7 @@ def has_object_read_permission(self, request): return True def has_object_write_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False return True diff --git a/care/facility/models/mixins/permissions/base.py b/care/facility/models/mixins/permissions/base.py index 1b9056238a..d41648bd15 100644 --- a/care/facility/models/mixins/permissions/base.py +++ b/care/facility/models/mixins/permissions/base.py @@ -1,3 +1,4 @@ +from care.facility.models.base import READ_ONLY_USER_TYPES from care.users.models import User @@ -8,11 +9,7 @@ def has_read_permission(request): @staticmethod def has_write_permission(request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False return ( request.user.is_superuser @@ -36,11 +33,7 @@ def has_object_read_permission(self, request): ) def has_object_update_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False return (request.user.is_superuser) or ( (hasattr(self, "created_by") and request.user == self.created_by) @@ -57,11 +50,7 @@ def has_object_update_permission(self, request): ) def has_object_destroy_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False return request.user.is_superuser or ( hasattr(self, "created_by") and request.user == self.created_by diff --git a/care/facility/models/mixins/permissions/facility.py b/care/facility/models/mixins/permissions/facility.py index 468463822f..4b95bb97fc 100644 --- a/care/facility/models/mixins/permissions/facility.py +++ b/care/facility/models/mixins/permissions/facility.py @@ -1,3 +1,4 @@ +from care.facility.models.base import READ_ONLY_USER_TYPES from care.facility.models.mixins.permissions.base import BasePermissionMixin from care.users.models import User @@ -65,11 +66,7 @@ def has_object_read_permission(self, request): ) def has_object_write_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False if request.user.user_type < User.TYPE_VALUE_MAP["Staff"]: # todo Temporary return False @@ -92,11 +89,7 @@ class FacilityRelatedPermissionMixin(BasePermissionMixin): def has_write_permission(request): from care.facility.models.facility import Facility - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False facility = False @@ -129,14 +122,35 @@ def has_object_read_permission(self, request): ) def has_object_write_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False return ( super().has_write_permission(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 self.has_read_permission(request) + + 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/mixins/permissions/patient.py b/care/facility/models/mixins/permissions/patient.py index c81685392b..7e2673295b 100644 --- a/care/facility/models/mixins/permissions/patient.py +++ b/care/facility/models/mixins/permissions/patient.py @@ -1,4 +1,5 @@ from care.facility.models import Facility, User +from care.facility.models.base import READ_ONLY_USER_TYPES from care.facility.models.mixins.permissions.base import BasePermissionMixin @@ -7,24 +8,20 @@ class PatientPermissionMixin(BasePermissionMixin): def has_write_permission(request): if request.user.asset: return False - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False return ( request.user.is_superuser or request.user.verified - and request.user.user_type >= User.TYPE_VALUE_MAP["Staff"] + and request.user.user_type >= User.TYPE_VALUE_MAP["Nurse"] ) def has_object_read_permission(self, request): doctor_allowed = False if self.last_consultation: - doctor_allowed = ( - self.last_consultation.assigned_to == request.user - or request.user == self.assigned_to + doctor_allowed = request.user in ( + self.last_consultation.assigned_to, + self.assigned_to, ) return request.user.is_superuser or ( (hasattr(self, "created_by") and request.user == self.created_by) @@ -55,18 +52,17 @@ def has_object_read_permission(self, request): def has_object_write_permission(self, request): if request.user.asset: return False + if request.user.user_type in READ_ONLY_USER_TYPES: + return False + if request.user.user_type < User.TYPE_VALUE_MAP["Nurse"]: + return False + doctor_allowed = False if self.last_consultation: - doctor_allowed = ( - self.last_consultation.assigned_to == request.user - or request.user == self.assigned_to + doctor_allowed = request.user in ( + self.last_consultation.assigned_to, + self.assigned_to, ) - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): - return False return request.user.is_superuser or ( (hasattr(self, "created_by") and request.user == self.created_by) or (doctor_allowed) @@ -92,43 +88,7 @@ def has_object_write_permission(self, request): ) def has_object_update_permission(self, request): - if request.user.asset: - return False - doctor_allowed = False - if self.last_consultation: - doctor_allowed = ( - self.last_consultation.assigned_to == request.user - or request.user == self.assigned_to - ) - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): - return False - return ( - request.user.is_superuser - or (hasattr(self, "created_by") and request.user == self.created_by) - or (self.facility and self.facility == request.user.home_facility) - or (doctor_allowed) - or ( - request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"] - and ( - request.user.district == self.district - or ( - self.facility - and request.user.district == self.facility.district - ) - ) - ) - or ( - request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"] - and ( - request.user.state == self.state - or (self.facility and request.user.state == self.facility.state) - ) - ) - ) + return self.has_object_write_permission(request) def has_object_icmr_sample_permission(self, request): return self.has_object_read_permission(request) @@ -136,12 +96,11 @@ def has_object_icmr_sample_permission(self, request): def has_object_transfer_permission(self, request): if request.user.asset: return False - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: + return False + if request.user.user_type < User.TYPE_VALUE_MAP["Nurse"]: return False + new_facility = Facility.objects.filter( id=request.data.get("facility", None) ).first() @@ -156,23 +115,20 @@ def get_related_consultation(self): @staticmethod def has_write_permission(request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False - return True + return ( + request.user.is_superuser + or request.user.verified + and request.user.user_type >= User.TYPE_VALUE_MAP["Nurse"] + ) def has_object_read_permission(self, request): - # This is because, `get_queryset` for related models already filters by consultation. return True def has_object_update_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + 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 diff --git a/care/facility/models/patient_external_test.py b/care/facility/models/patient_external_test.py index 1e13c8f5c2..6bab947831 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 FacilityUserPermissionMixin from care.users.models import District, LocalBody, Ward -class PatientExternalTest(FacilityBaseModel): +class PatientExternalTest(FacilityBaseModel, FacilityUserPermissionMixin): 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 a3a7405521..5d2d46925d 100644 --- a/care/facility/models/patient_sample.py +++ b/care/facility/models/patient_sample.py @@ -1,6 +1,8 @@ from django.db import models 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 = [ @@ -18,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) @@ -150,19 +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 == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): - return False - return ( - request.user.is_superuser - or request.user.user_type >= User.TYPE_VALUE_MAP["Staff"] - ) - @staticmethod def has_read_permission(request): return ( @@ -190,11 +184,7 @@ def has_object_read_permission(self, request): ) def has_object_update_permission(self, request): - if ( - request.user.user_type == User.TYPE_VALUE_MAP["DistrictReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StateReadOnlyAdmin"] - or request.user.user_type == User.TYPE_VALUE_MAP["StaffReadOnly"] - ): + if request.user.user_type in READ_ONLY_USER_TYPES: return False if not self.has_object_read_permission(request): return False diff --git a/care/facility/models/shifting.py b/care/facility/models/shifting.py index 4b88925b60..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,32 +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 - 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 - 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 - return True - class ShiftingRequestComment(FacilityBaseModel): request = models.ForeignKey( diff --git a/care/users/api/serializers/user.py b/care/users/api/serializers/user.py index 0613b3437f..c7f2df0b7d 100644 --- a/care/users/api/serializers/user.py +++ b/care/users/api/serializers/user.py @@ -197,7 +197,8 @@ def validate(self, attrs): ) if ( - self.context["created_by"].user_type == User.TYPE_VALUE_MAP["Staff"] + self.context["created_by"].user_type + in (User.TYPE_VALUE_MAP["Staff"], User.TYPE_VALUE_MAP["Nurse"]) and validated["user_type"] == User.TYPE_VALUE_MAP["Doctor"] ): pass diff --git a/care/users/migrations/0009_alter_user_user_type.py b/care/users/migrations/0009_alter_user_user_type.py new file mode 100644 index 0000000000..906f722649 --- /dev/null +++ b/care/users/migrations/0009_alter_user_user_type.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.2 on 2023-09-09 17:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0008_rename_skill_and_add_new_20230817_1937"), + ] + + operations = [ + migrations.AlterField( + model_name="user", + name="user_type", + field=models.IntegerField( + choices=[ + (2, "Transportation"), + (3, "Pharmacist"), + (5, "Volunteer"), + (7, "StaffReadOnly"), + (8, "Staff"), + (9, "NurseReadOnly"), + (10, "Nurse"), + (15, "Doctor"), + (20, "Reserved"), + (21, "WardAdmin"), + (23, "LocalBodyAdmin"), + (25, "DistrictLabAdmin"), + (29, "DistrictReadOnlyAdmin"), + (30, "DistrictAdmin"), + (35, "StateLabAdmin"), + (39, "StateReadOnlyAdmin"), + (40, "StateAdmin"), + ] + ), + ), + ] diff --git a/care/users/migrations/0010_merge_20230926_2015.py b/care/users/migrations/0010_merge_20230926_2015.py new file mode 100644 index 0000000000..03e00eb747 --- /dev/null +++ b/care/users/migrations/0010_merge_20230926_2015.py @@ -0,0 +1,12 @@ +# Generated by Django 4.2.2 on 2023-09-26 14:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0009_alter_user_user_type"), + ("users", "0009_userfacilityallocation"), + ] + + operations = [] diff --git a/care/users/migrations/0011_merge_0010_merge_20230926_2015_0010_rename_skills.py b/care/users/migrations/0011_merge_0010_merge_20230926_2015_0010_rename_skills.py new file mode 100644 index 0000000000..533276a0e5 --- /dev/null +++ b/care/users/migrations/0011_merge_0010_merge_20230926_2015_0010_rename_skills.py @@ -0,0 +1,12 @@ +# Generated by Django 4.2.5 on 2023-12-15 03:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0010_merge_20230926_2015"), + ("users", "0010_rename_skills"), + ] + + operations = [] diff --git a/care/users/models.py b/care/users/models.py index 28ec87ed9e..8626cae765 100644 --- a/care/users/models.py +++ b/care/users/models.py @@ -184,8 +184,10 @@ class User(AbstractUser): "Transportation": 2, "Pharmacist": 3, "Volunteer": 5, - "StaffReadOnly": 9, - "Staff": 10, + "StaffReadOnly": 7, + "Staff": 8, + "NurseReadOnly": 9, + "Nurse": 10, "Doctor": 15, "Reserved": 20, "WardAdmin": 21, diff --git a/care/utils/tests/test_utils.py b/care/utils/tests/test_utils.py index 1a960ef452..8b964d82ba 100644 --- a/care/utils/tests/test_utils.py +++ b/care/utils/tests/test_utils.py @@ -160,7 +160,7 @@ def create_user( "state": district.state, "district": district, "local_body": local_body, - "user_type": User.TYPE_VALUE_MAP["Staff"], + "user_type": User.TYPE_VALUE_MAP["Nurse"], } data.update(kwargs) user = User.objects.create_user(**data)