Coverage for benefits/core/middleware.py: 96%

91 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-10-21 19:31 +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.shortcuts import redirect 

10from django.template.response import TemplateResponse 

11from django.urls import reverse 

12from django.utils.decorators import decorator_from_middleware 

13from django.utils.deprecation import MiddlewareMixin 

14from django.views import i18n 

15 

16from benefits.routes import routes 

17from . import analytics, recaptcha, session 

18 

19 

20logger = logging.getLogger(__name__) 

21 

22HEALTHCHECK_PATH = "/healthcheck" 

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

24 

25 

26def user_error(request): 

27 return TemplateResponse(request, TEMPLATE_USER_ERROR) 

28 

29 

30class AgencySessionRequired(MiddlewareMixin): 

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

32 

33 def process_request(self, request): 

34 if session.active_agency(request): 

35 logger.debug("Session configured with agency") 

36 return None 

37 else: 

38 logger.debug("Session not configured with agency") 

39 return user_error(request) 

40 

41 

42class EligibleSessionRequired(MiddlewareMixin): 

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

44 

45 def process_request(self, request): 

46 if session.eligible(request): 

47 logger.debug("Session has confirmed eligibility") 

48 return None 

49 else: 

50 logger.debug("Session has no confirmed eligibility") 

51 return user_error(request) 

52 

53 

54class DebugSession(MiddlewareMixin): 

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

56 

57 def process_request(self, request): 

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

59 return None 

60 

61 

62class Healthcheck: 

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

64 

65 def __init__(self, get_response): 

66 self.get_response = get_response 

67 

68 def __call__(self, request): 

69 if request.path == HEALTHCHECK_PATH: 

70 return HttpResponse("Healthy", content_type="text/plain") 

71 return self.get_response(request) 

72 

73 

74class HealthcheckUserAgents(MiddlewareMixin): 

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

76 

77 def process_request(self, request): 

78 if hasattr(request, "META"): 78 ↛ 83line 78 didn't jump to line 83 because the condition on line 78 was always true

79 user_agent = request.META.get("HTTP_USER_AGENT", "") 

80 if user_agent in settings.HEALTHCHECK_USER_AGENTS: 

81 return HttpResponse("Healthy", content_type="text/plain") 

82 

83 return self.get_response(request) 

84 

85 

86class FlowSessionRequired(MiddlewareMixin): 

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

88 

89 def process_request(self, request): 

90 if session.flow(request): 

91 logger.debug("Session configured with enrollment flow") 

92 return None 

93 else: 

94 logger.debug("Session not configured with enrollment flow") 

95 return user_error(request) 

96 

97 

98class ViewedPageEvent(MiddlewareMixin): 

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

100 

101 def process_response(self, request, response): 

102 event = analytics.ViewedPageEvent(request) 

103 try: 

104 analytics.send_event(event) 

105 except Exception: 

106 logger.warning(f"Failed to send event: {event}") 

107 finally: 

108 return response 

109 

110 

111pageview_decorator = decorator_from_middleware(ViewedPageEvent) 

112 

113 

114class ChangedLanguageEvent(MiddlewareMixin): 

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

116 

117 def process_view(self, request, view_func, view_args, view_kwargs): 

118 if view_func == i18n.set_language: 

119 new_lang = request.POST.get("language") 

120 if new_lang: 

121 event = analytics.ChangedLanguageEvent(request, new_lang) 

122 analytics.send_event(event) 

123 else: 

124 logger.warning("i18n.set_language POST without language") 

125 return None 

126 

127 

128class LoginRequired(MiddlewareMixin): 

129 """Middleware that checks whether a user is logged in.""" 

130 

131 def process_view(self, request, view_func, view_args, view_kwargs): 

132 # only require login if flow uses claims verification 

133 flow = session.flow(request) 

134 if not flow or not flow.uses_claims_verification or session.logged_in(request): 

135 # pass through 

136 return None 

137 

138 return redirect(routes.OAUTH_LOGIN) 

139 

140 

141class RecaptchaEnabled(MiddlewareMixin): 

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

143 

144 def process_request(self, request): 

145 if settings.RECAPTCHA_ENABLED: 145 ↛ 146line 145 didn't jump to line 146

146 request.recaptcha = { 

147 "data_field": recaptcha.DATA_FIELD, 

148 "script_api": settings.RECAPTCHA_API_KEY_URL, 

149 "site_key": settings.RECAPTCHA_SITE_KEY, 

150 } 

151 return None 

152 

153 

154class IndexOrAgencyIndexOrigin(MiddlewareMixin): 

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

156 

157 def process_request(self, request): 

158 if session.active_agency(request): 

159 session.update(request, origin=session.agency(request).index_url) 

160 else: 

161 session.update(request, origin=reverse(routes.INDEX)) 

162 return None 

163 

164 

165index_or_agencyindex_origin_decorator = decorator_from_middleware(IndexOrAgencyIndexOrigin)