Coverage for benefits / core / middleware.py: 84%
84 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-13 19:35 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-13 19:35 +0000
1"""
2The core application: middleware definitions for request/response cycle.
3"""
5import logging
7from django.conf import settings
8from django.http import HttpResponse
9from django.template.response import TemplateResponse
10from django.urls import reverse
11from django.utils.decorators import decorator_from_middleware
12from django.utils.deprecation import MiddlewareMixin
13from django.views import i18n
15from benefits.core import analytics, recaptcha, session
16from benefits.routes import routes
18logger = logging.getLogger(__name__)
20HEALTHCHECK_PATH = "/healthcheck"
21TEMPLATE_USER_ERROR = "200-user-error.html"
24def user_error(request):
25 return TemplateResponse(request, TEMPLATE_USER_ERROR)
28class AgencySessionRequired(MiddlewareMixin):
29 """Middleware raises an exception for sessions lacking an agency configuration."""
31 def process_request(self, request):
32 if session.active_agency(request): 32 ↛ 36line 32 didn't jump to line 36 because the condition on line 32 was always true
33 logger.debug("Session configured with agency")
34 return None
35 else:
36 logger.debug("Session not configured with agency")
37 return user_error(request)
40class EligibleSessionRequired(MiddlewareMixin):
41 """Middleware raises an exception for sessions lacking confirmed eligibility."""
43 def process_request(self, request):
44 if session.eligible(request):
45 logger.debug("Session has confirmed eligibility")
46 return None
47 else:
48 logger.debug("Session has no confirmed eligibility")
49 return user_error(request)
52class DebugSession(MiddlewareMixin):
53 """Middleware to configure debug context in the request session."""
55 def process_request(self, request):
56 session.update(request, debug=settings.DEBUG)
57 return None
60class Healthcheck:
61 """Middleware intercepts and accepts /healthcheck requests."""
63 def __init__(self, get_response):
64 self.get_response = get_response
66 def __call__(self, request):
67 if request.path == HEALTHCHECK_PATH:
68 return HttpResponse("Healthy", content_type="text/plain")
69 return self.get_response(request)
72class HealthcheckUserAgents(MiddlewareMixin):
73 """Middleware to return healthcheck for user agents specified in HEALTHCHECK_USER_AGENTS."""
75 def process_request(self, request):
76 if hasattr(request, "META"): 76 ↛ 81line 76 didn't jump to line 81 because the condition on line 76 was always true
77 user_agent = request.META.get("HTTP_USER_AGENT", "")
78 if user_agent in settings.HEALTHCHECK_USER_AGENTS:
79 return HttpResponse("Healthy", content_type="text/plain")
81 return self.get_response(request)
84class FlowSessionRequired(MiddlewareMixin):
85 """Middleware raises an exception for sessions lacking a configured enrollment flow."""
87 def process_request(self, request):
88 if session.flow(request):
89 logger.debug("Session configured with enrollment flow")
90 return None
91 else:
92 logger.debug("Session not configured with enrollment flow")
93 return user_error(request)
96class ViewedPageEvent(MiddlewareMixin):
97 """Middleware sends an analytics event for page views."""
99 def process_response(self, request, response):
100 event = analytics.ViewedPageEvent(request)
101 try:
102 analytics.send_event(event)
103 except Exception:
104 logger.warning(f"Failed to send event: {event}")
105 finally:
106 return response
109pageview_decorator = decorator_from_middleware(ViewedPageEvent)
112class ChangedLanguageEvent(MiddlewareMixin):
113 """Middleware hooks into django.views.i18n.set_language to send an analytics event."""
115 def process_view(self, request, view_func, view_args, view_kwargs):
116 if view_func == i18n.set_language:
117 new_lang = request.POST.get("language")
118 if new_lang:
119 event = analytics.ChangedLanguageEvent(request, new_lang)
120 analytics.send_event(event)
121 else:
122 logger.warning("i18n.set_language POST without language")
123 return None
126class RecaptchaEnabled(MiddlewareMixin):
127 """Middleware configures the request with required reCAPTCHA settings."""
129 def process_request(self, request):
130 if settings.RECAPTCHA_ENABLED: 130 ↛ 131line 130 didn't jump to line 131 because the condition on line 130 was never true
131 request.recaptcha = {
132 "data_field": recaptcha.DATA_FIELD,
133 "script_api": settings.RECAPTCHA_API_KEY_URL,
134 "site_key": settings.RECAPTCHA_SITE_KEY,
135 }
136 return None
139class IndexOrAgencyIndexOrigin(MiddlewareMixin):
140 """Middleware sets the session.origin to either the index or agency index route, depending on agency config."""
142 def process_request(self, request):
143 if session.active_agency(request):
144 session.update(request, origin=session.agency(request).index_url)
145 else:
146 session.update(request, origin=reverse(routes.INDEX))
147 return None
150index_or_agencyindex_origin_decorator = decorator_from_middleware(IndexOrAgencyIndexOrigin)