161 lines
5.4 KiB
Python
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()
|