HH/apps/core/middleware.py
2026-03-15 23:48:45 +03:00

89 lines
3.0 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"))