""" Middleware for PX Source User access restriction. Provides global route-level protection to ensure source users can only access their designated pages. """ from django.urls import resolve from django.shortcuts import redirect from django.contrib import messages from django.utils.deprecation import MiddlewareMixin class SourceUserRestrictionMiddleware(MiddlewareMixin): """ STRICT middleware that restricts source users to ONLY: 1. /px-sources/* pages (their dashboard, complaints, inquiries) 2. Password change page 3. Logout ALL other routes are BLOCKED. """ # URL path prefixes that source users CAN access (whitelist) ALLOWED_PATH_PREFIXES = [ '/px-sources/', # Source user portal ] # Specific URL names that source users CAN access ALLOWED_URL_NAMES = { # Password change 'accounts:password_change', 'accounts:password_change_done', # Settings (limited) 'accounts:settings', # Logout 'accounts:logout', # Login (for redirect after logout) 'accounts:login', # Static files (for CSS/JS) None, # Static files don't have URL names } # Explicitly blocked paths (even if they match allowed prefixes) BLOCKED_PATHS = [ '/px-sources/new/', '/px-sources/create/', '/px-sources//edit/', '/px-sources//delete/', '/px-sources//toggle/', '/px-sources/ajax/', '/px-sources/api/', ] def process_request(self, request): # Skip for unauthenticated users if not request.user.is_authenticated: return None # Skip for superusers if request.user.is_superuser: return None # Check if user is a source user if not self._is_source_user(request.user): return None # Source user detected - apply strict restrictions path = request.path # Get current route name try: resolver = resolve(path) route_name = f"{resolver.namespace}:{resolver.url_name}" if resolver.namespace else resolver.url_name except: route_name = None # Check if URL name is explicitly allowed if route_name in self.ALLOWED_URL_NAMES: return None # Check if path starts with allowed prefixes for prefix in self.ALLOWED_PATH_PREFIXES: if path.startswith(prefix): # Check if it's a blocked sub-path for blocked in self.BLOCKED_PATHS: if blocked in path: return self._block_access(request) # Path is allowed return None # Check for static/media files (allow these) if path.startswith('/static/') or path.startswith('/media/'): return None # Check for i18n URLs if path.startswith('/i18n/'): return None # Everything else is BLOCKED for source users return self._block_access(request) def _is_source_user(self, user): """Check if user is an active source user.""" if not hasattr(user, 'source_user_profile'): return False source_user = user.source_user_profile return source_user.is_active def _block_access(self, request): """Block access and redirect to source user dashboard.""" return redirect('px_sources:source_user_dashboard') class SourceUserSessionMiddleware(MiddlewareMixin): """ Middleware to set shorter session timeout for source users. Source users have limited access, so their sessions expire faster for security purposes. """ SOURCE_USER_SESSION_TIMEOUT = 3600 # 1 hour NORMAL_SESSION_TIMEOUT = 1209600 # 2 weeks def process_request(self, request): if not request.user.is_authenticated: return None if self._is_source_user(request.user): # Set shorter session for source users request.session.set_expiry(self.SOURCE_USER_SESSION_TIMEOUT) else: # Normal session for other users request.session.set_expiry(self.NORMAL_SESSION_TIMEOUT) return None def _is_source_user(self, user): """Check if user is an active source user.""" if not hasattr(user, 'source_user_profile'): return False source_user = user.source_user_profile return source_user.is_active