HH/apps/core/middleware.py
ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

161 lines
5.4 KiB
Python

"""
Tenant-aware middleware for multi-tenancy
"""
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
class TenantMiddleware(MiddlewareMixin):
"""
Middleware that sets the current hospital context from the authenticated user.
This middleware ensures that:
- authenticated users have their tenant_hospital set from their profile
- PX admins can switch between hospitals via session
- PX admins without a selected hospital are redirected to select-hospital
- Source Users have their source context available
- All requests have tenant context available
"""
EXEMPT_PATHS = [
"/core/select-hospital/",
"/accounts/logout/",
"/accounts/password_reset/",
"/accounts/password_reset/done/",
"/accounts/reset/",
"/api/",
"/health/",
"/admin/",
"/__debug__/",
]
def process_request(self, request):
"""Set tenant hospital context on each request."""
if request.user and request.user.is_authenticated:
request.user_roles = request.user.get_role_names()
request.source_user = None
request.source_user_profile = None
if request.user.is_source_user():
profile = request.user.get_source_user_profile_active()
if profile:
request.source_user = profile
request.source_user_profile = profile
if request.user.is_px_admin():
hospital_id = request.session.get("selected_hospital_id")
if hospital_id:
from apps.organizations.models import Hospital
try:
request.tenant_hospital = Hospital.objects.get(id=hospital_id)
except Hospital.DoesNotExist:
request.tenant_hospital = None
request.session.pop("selected_hospital_id", None)
else:
request.tenant_hospital = None
else:
request.tenant_hospital = request.user.hospital
else:
request.tenant_hospital = None
request.user_roles = []
request.source_user = None
request.source_user_profile = None
return None
def process_view(self, request, view_func, view_args, view_kwargs):
"""Redirect PX admins without hospital to select-hospital page."""
if not request.user or not request.user.is_authenticated:
return None
if not request.user.is_px_admin():
return None
if request.tenant_hospital:
return None
path = request.path
for exempt_path in self.EXEMPT_PATHS:
if path.startswith(exempt_path):
return None
if request.headers.get("x-requested-with") == "XMLHttpRequest":
return None
return HttpResponseRedirect(reverse("core:select_hospital"))
class DepartmentRespondentMiddleware(MiddlewareMixin):
"""
Restrict Department Respondent users to only their department detail page
and inquiry department response pages.
"""
ALLOWED_PATH_PREFIXES = [
"/",
"/accounts/logout/",
"/accounts/password_reset/",
"/accounts/reset/",
"/core/select-hospital/",
"/organizations/departments/",
"/inquiries/",
"/api/",
"/health/",
"/admin/",
"/__debug__/",
"/i18n/",
"/static/",
]
def process_view(self, request, view_func, view_args, view_kwargs):
if not request.user or not request.user.is_authenticated:
return None
if not request.user.is_champion():
return None
if request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_department_manager() or request.user.is_director():
return None
if request.headers.get("x-requested-with") == "XMLHttpRequest":
return None
path = request.path
if path == "/":
return None
for prefix in self.ALLOWED_PATH_PREFIXES:
if path.startswith(prefix):
if path.startswith("/organizations/departments/"):
if "set-respondent" in path or "edit" in path or "delete" in path:
from django.http import HttpResponseForbidden
return HttpResponseForbidden()
return None
if path.startswith("/inquiries/"):
if not path.endswith("/department-response/"):
from apps.complaints.models import Inquiry
pk = view_kwargs.get("pk")
if pk:
try:
inquiry = Inquiry.objects.get(pk=pk)
user_dept = request.user.department
if inquiry.department == user_dept or inquiry.outgoing_department == user_dept:
return None
except Inquiry.DoesNotExist:
pass
from django.http import HttpResponseForbidden
return HttpResponseForbidden()
return None
return None
from django.http import HttpResponseForbidden
return HttpResponseForbidden()