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

1""" 

2The core application: middleware definitions for request/response cycle. 

3""" 

4 

5import logging 

6 

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 

14 

15from benefits.core import analytics, recaptcha, session 

16from benefits.routes import routes 

17 

18logger = logging.getLogger(__name__) 

19 

20HEALTHCHECK_PATH = "/healthcheck" 

21TEMPLATE_USER_ERROR = "200-user-error.html" 

22 

23 

24def user_error(request): 

25 return TemplateResponse(request, TEMPLATE_USER_ERROR) 

26 

27 

28class AgencySessionRequired(MiddlewareMixin): 

29 """Middleware raises an exception for sessions lacking an agency configuration.""" 

30 

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) 

38 

39 

40class EligibleSessionRequired(MiddlewareMixin): 

41 """Middleware raises an exception for sessions lacking confirmed eligibility.""" 

42 

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) 

50 

51 

52class DebugSession(MiddlewareMixin): 

53 """Middleware to configure debug context in the request session.""" 

54 

55 def process_request(self, request): 

56 session.update(request, debug=settings.DEBUG) 

57 return None 

58 

59 

60class Healthcheck: 

61 """Middleware intercepts and accepts /healthcheck requests.""" 

62 

63 def __init__(self, get_response): 

64 self.get_response = get_response 

65 

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) 

70 

71 

72class HealthcheckUserAgents(MiddlewareMixin): 

73 """Middleware to return healthcheck for user agents specified in HEALTHCHECK_USER_AGENTS.""" 

74 

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") 

80 

81 return self.get_response(request) 

82 

83 

84class FlowSessionRequired(MiddlewareMixin): 

85 """Middleware raises an exception for sessions lacking a configured enrollment flow.""" 

86 

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) 

94 

95 

96class ViewedPageEvent(MiddlewareMixin): 

97 """Middleware sends an analytics event for page views.""" 

98 

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 

107 

108 

109pageview_decorator = decorator_from_middleware(ViewedPageEvent) 

110 

111 

112class ChangedLanguageEvent(MiddlewareMixin): 

113 """Middleware hooks into django.views.i18n.set_language to send an analytics event.""" 

114 

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 

124 

125 

126class RecaptchaEnabled(MiddlewareMixin): 

127 """Middleware configures the request with required reCAPTCHA settings.""" 

128 

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 

137 

138 

139class IndexOrAgencyIndexOrigin(MiddlewareMixin): 

140 """Middleware sets the session.origin to either the index or agency index route, depending on agency config.""" 

141 

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 

148 

149 

150index_or_agencyindex_origin_decorator = decorator_from_middleware(IndexOrAgencyIndexOrigin)