Coverage for benefits/core/middleware.py: 95%
84 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-22 21:13 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-22 21:13 +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.routes import routes
16from . import analytics, recaptcha, session
19logger = logging.getLogger(__name__)
21HEALTHCHECK_PATH = "/healthcheck"
22TEMPLATE_USER_ERROR = "200-user-error.html"
25def user_error(request):
26 return TemplateResponse(request, TEMPLATE_USER_ERROR)
29class AgencySessionRequired(MiddlewareMixin):
30 """Middleware raises an exception for sessions lacking an agency configuration."""
32 def process_request(self, request):
33 if session.active_agency(request):
34 logger.debug("Session configured with agency")
35 return None
36 else:
37 logger.debug("Session not configured with agency")
38 return user_error(request)
41class EligibleSessionRequired(MiddlewareMixin):
42 """Middleware raises an exception for sessions lacking confirmed eligibility."""
44 def process_request(self, request):
45 if session.eligible(request):
46 logger.debug("Session has confirmed eligibility")
47 return None
48 else:
49 logger.debug("Session has no confirmed eligibility")
50 return user_error(request)
53class DebugSession(MiddlewareMixin):
54 """Middleware to configure debug context in the request session."""
56 def process_request(self, request):
57 session.update(request, debug=settings.DEBUG)
58 return None
61class Healthcheck:
62 """Middleware intercepts and accepts /healthcheck requests."""
64 def __init__(self, get_response):
65 self.get_response = get_response
67 def __call__(self, request):
68 if request.path == HEALTHCHECK_PATH:
69 return HttpResponse("Healthy", content_type="text/plain")
70 return self.get_response(request)
73class HealthcheckUserAgents(MiddlewareMixin):
74 """Middleware to return healthcheck for user agents specified in HEALTHCHECK_USER_AGENTS."""
76 def process_request(self, request):
77 if hasattr(request, "META"): 77 ↛ 82line 77 didn't jump to line 82 because the condition on line 77 was always true
78 user_agent = request.META.get("HTTP_USER_AGENT", "")
79 if user_agent in settings.HEALTHCHECK_USER_AGENTS:
80 return HttpResponse("Healthy", content_type="text/plain")
82 return self.get_response(request)
85class FlowSessionRequired(MiddlewareMixin):
86 """Middleware raises an exception for sessions lacking a configured enrollment flow."""
88 def process_request(self, request):
89 if session.flow(request):
90 logger.debug("Session configured with enrollment flow")
91 return None
92 else:
93 logger.debug("Session not configured with enrollment flow")
94 return user_error(request)
97class ViewedPageEvent(MiddlewareMixin):
98 """Middleware sends an analytics event for page views."""
100 def process_response(self, request, response):
101 event = analytics.ViewedPageEvent(request)
102 try:
103 analytics.send_event(event)
104 except Exception:
105 logger.warning(f"Failed to send event: {event}")
106 finally:
107 return response
110pageview_decorator = decorator_from_middleware(ViewedPageEvent)
113class ChangedLanguageEvent(MiddlewareMixin):
114 """Middleware hooks into django.views.i18n.set_language to send an analytics event."""
116 def process_view(self, request, view_func, view_args, view_kwargs):
117 if view_func == i18n.set_language:
118 new_lang = request.POST.get("language")
119 if new_lang:
120 event = analytics.ChangedLanguageEvent(request, new_lang)
121 analytics.send_event(event)
122 else:
123 logger.warning("i18n.set_language POST without language")
124 return None
127class RecaptchaEnabled(MiddlewareMixin):
128 """Middleware configures the request with required reCAPTCHA settings."""
130 def process_request(self, request):
131 if settings.RECAPTCHA_ENABLED: 131 ↛ 132line 131 didn't jump to line 132 because the condition on line 131 was never true
132 request.recaptcha = {
133 "data_field": recaptcha.DATA_FIELD,
134 "script_api": settings.RECAPTCHA_API_KEY_URL,
135 "site_key": settings.RECAPTCHA_SITE_KEY,
136 }
137 return None
140class IndexOrAgencyIndexOrigin(MiddlewareMixin):
141 """Middleware sets the session.origin to either the index or agency index route, depending on agency config."""
143 def process_request(self, request):
144 if session.active_agency(request):
145 session.update(request, origin=session.agency(request).index_url)
146 else:
147 session.update(request, origin=reverse(routes.INDEX))
148 return None
151index_or_agencyindex_origin_decorator = decorator_from_middleware(IndexOrAgencyIndexOrigin)