Skip to content

Commit

Permalink
Fix permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
vigneshhari committed Dec 30, 2024
1 parent eed116a commit 9bdfa76
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 16 deletions.
66 changes: 57 additions & 9 deletions care/emr/api/viewsets/file_upload.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,79 @@
from django.utils import timezone
from pydantic import BaseModel
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response

from care.emr.api.viewsets.base import EMRModelViewSet
from care.emr.models import FileUpload
from care.emr.api.viewsets.base import (
EMRBaseViewSet,
EMRCreateMixin,
EMRListMixin,
EMRRetrieveMixin,
EMRUpdateMixin,
)
from care.emr.models import Encounter, FileUpload, Patient
from care.emr.resources.file_upload.spec import (
FileTypeChoices,
FileUploadCreateSpec,
FileUploadListSpec,
FileUploadRetrieveSpec,
FileUploadUpdateSpec,
)
from care.security.authorization import AuthorizationController


def file_authorizer(user, file_type, associating_id, permission):
allowed = False
if file_type == FileTypeChoices.patient.value:
patient_obj = get_object_or_404(Patient, external_id=associating_id)
if permission == "read":
allowed = AuthorizationController.call(
"can_view_clinical_data", user, patient_obj
)
elif permission == "write":
allowed = AuthorizationController.call(
"can_write_patient_obj", user, patient_obj
)

encounter_obj = get_object_or_404(Encounter, external_id=associating_id)
if permission == "read":
allowed = AuthorizationController.call(
"can_view_encounter_obj", user, encounter_obj
)
elif permission == "write":
allowed = AuthorizationController.call(
"can_update_encounter_obj", user, encounter_obj
)

def file_authorizer(file_type, associating_id, permission):
return
if not allowed:
raise PermissionDenied("Cannot View File")


class FileUploadViewSet(EMRModelViewSet):
class FileUploadViewSet(
EMRCreateMixin, EMRRetrieveMixin, EMRUpdateMixin, EMRListMixin, EMRBaseViewSet
):
database_model = FileUpload
pydantic_model = FileUploadCreateSpec
pydantic_retrieve_model = FileUploadRetrieveSpec
pydantic_update_model = FileUploadUpdateSpec
pydantic_read_model = FileUploadListSpec

def authorize_create(self, instance):
pass
file_authorizer(
self.request.user,
instance.file_type,
instance.associating_id,
"write",
)

def authorize_update(self, request_obj, model_instance):
file_authorizer(
self.request.user,
model_instance.file_type,
model_instance.associating_id,
"write",
)

def get_queryset(self):
if self.action == "list":
Expand All @@ -36,6 +83,7 @@ def get_queryset(self):
):
raise PermissionError("Cannot filter files")
file_authorizer(
self.request.user,
self.request.GET.get("file_type"),
self.request.GET.get("associating_id"),
"read",
Expand All @@ -49,13 +97,13 @@ def get_queryset(self):
)
)
obj = get_object_or_404(FileUpload, external_id=self.kwargs["external_id"])
file_authorizer(obj.file_type, obj.associating_id, "read")
file_authorizer(self.request.user, obj.file_type, obj.associating_id, "read")
return super().get_queryset()

@action(detail=True, methods=["POST"])
def mark_upload_completed(self, request, *args, **kwargs):
obj = self.get_object()
file_authorizer(obj.file_type, obj.associating_id, "write")
file_authorizer(request.user, obj.file_type, obj.associating_id, "write")
obj.upload_completed = True
obj.save(update_fields=["upload_completed"])
return Response(FileUploadListSpec.serialize(obj).to_json())
Expand All @@ -67,7 +115,7 @@ class ArchiveRequestSpec(BaseModel):
def archive(self, request, *args, **kwargs):
obj = self.get_object()
request_data = self.ArchiveRequestSpec(**request.data)
file_authorizer(obj.file_type, obj.associating_id, "write")
file_authorizer(request.user, obj.file_type, obj.associating_id, "write")
obj.is_archived = True
obj.archive_reason = request_data.archive_reason
obj.archived_datetime = timezone.now()
Expand Down
9 changes: 8 additions & 1 deletion care/emr/api/viewsets/medication_administration.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django_filters import rest_framework as filters
from rest_framework.exceptions import PermissionDenied

from care.emr.api.viewsets.authz_base import EncounterBasedAuthorizationBase
from care.emr.api.viewsets.base import EMRModelViewSet
from care.emr.models.medication_administration import MedicationAdministration
from care.emr.registries.system_questionnaire.system_questionnaire import (
Expand All @@ -10,6 +12,7 @@
MedicationAdministrationSpec,
)
from care.emr.resources.questionnaire.spec import SubjectType
from care.security.authorization import AuthorizationController


class MedicationAdministrationFilter(filters.FilterSet):
Expand All @@ -19,7 +22,7 @@ class MedicationAdministrationFilter(filters.FilterSet):
occurrence_period_end = filters.DateTimeFromToRangeFilter()


class MedicationAdministrationViewSet(EMRModelViewSet):
class MedicationAdministrationViewSet(EncounterBasedAuthorizationBase, EMRModelViewSet):
database_model = MedicationAdministration
pydantic_model = MedicationAdministrationSpec
pydantic_read_model = MedicationAdministrationReadSpec
Expand All @@ -31,6 +34,10 @@ class MedicationAdministrationViewSet(EMRModelViewSet):
filter_backends = [filters.DjangoFilterBackend]

def get_queryset(self):
if not AuthorizationController.call(
"can_view_clinical_data", self.request.user, self.get_patient_obj()
):
raise PermissionDenied("Permission denied to user")
return (
super()
.get_queryset()
Expand Down
2 changes: 1 addition & 1 deletion care/emr/api/viewsets/medication_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def get_queryset(self):
def discontinue(self, request, *args, **kwargs):
data = MedicationRequestDiscontinueRequest(**request.data)
request: MedicationRequest = self.get_object()

self.authorize_update({}, request)
request.status = MedicationRequestStatus.ended
request.status_changed = timezone.now()
request.status_reason = data.status_reason
Expand Down
18 changes: 13 additions & 5 deletions care/emr/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from rest_framework.response import Response

from care.emr.api.viewsets.base import EMRModelViewSet
from care.emr.models import PatientUser
from care.emr.models import Organization, PatientUser
from care.emr.models.patient import Patient
from care.emr.resources.patient.spec import (
PatientCreateSpec,
Expand All @@ -18,6 +18,7 @@
PatientRetrieveSpec,
)
from care.emr.resources.user.spec import UserSpec
from care.security.authorization import AuthorizationController
from care.security.models import RoleModel
from care.users.models import User

Expand All @@ -38,14 +39,21 @@ class PatientViewSet(EMRModelViewSet):
# TODO : Retrieve will work if an active encounter exists on the patient

def get_queryset(self):
return (
qs = (
super()
.get_queryset()
.select_related("created_by", "updated_by", "geo_organization")
)
# return AuthorizationController.call(
# "get_filtered_patients", qs, self.request.user
# )
if self.request.GET.get("geo_organization"):
geo_organization = get_object_or_404(
Organization,
external_id=self.request.GET["geo_organization"],
org_type="govt",
)
qs = qs.filter(organization_cache__overlap=geo_organization.id)
return AuthorizationController.call(
"get_filtered_patients", qs, self.request.user
)

class SearchRequestSpec(BaseModel):
name: str = ""
Expand Down
9 changes: 9 additions & 0 deletions care/security/authorization/encounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ def can_create_encounter_obj(self, user, facility):
[EncounterPermissions.can_create_encounter.name], user, facility=facility
)

def can_view_encounter_obj(self, user, facility):
"""
Check if the user has permission to read encounter under this facility
"""
return self.check_permission_in_facility_organization(
[EncounterPermissions.can_read_encounter.name], user, facility=facility
)

def can_update_encounter_obj(self, user, encounter):
"""
Check if the user has permission to create encounter under this facility
"""
# TODO check if encounter has been closed
return self.check_permission_in_facility_organization(
[EncounterPermissions.can_write_encounter.name],
user,
Expand Down
9 changes: 9 additions & 0 deletions care/security/authorization/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ def can_view_patient_obj(self, user, patient):
role__in=user_roles,
).exists()

def can_write_patient_obj(self, user, patient):
if user.is_superuser:
return True
user_roles = self.find_roles_on_patient(user, patient)
return RolePermission.objects.filter(
permission__slug__in=[PatientPermissions.can_write_patient.name],
role__in=user_roles,
).exists()

def can_view_clinical_data(self, user, patient):
if user.is_superuser:
return True
Expand Down

0 comments on commit 9bdfa76

Please sign in to comment.