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

46 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-22 19:08 +0000

1import logging 

2 

3from django.conf import settings 

4from django.forms import ValidationError 

5 

6from benefits.core import analytics 

7 

8from . import recaptcha, session 

9from .middleware import user_error 

10 

11logger = logging.getLogger(__name__) 

12 

13 

14class AgencySessionRequiredMixin: 

15 """Mixin intended for use with a class-based view as the first in the MRO. 

16 

17 Gets the active `TransitAgency` out of session and sets an attribute on `self`. 

18 

19 If the session is not configured with an agency, return a user error. 

20 """ 

21 

22 def dispatch(self, request, *args, **kwargs): 

23 if session.active_agency(request): 

24 self.agency = session.agency(request) 

25 return super().dispatch(request, *args, **kwargs) 

26 else: 

27 logger.warning("Session not configured with an active agency") 

28 return user_error(request) 

29 

30 

31class EligibleSessionRequiredMixin: 

32 """Mixin intended for use with a class-based view as the first in the MRO. 

33 

34 If the session is not marked as eligible (e.g. the user has verified their eligibility), return a user error. 

35 """ 

36 

37 def dispatch(self, request, *args, **kwargs): 

38 if session.eligible(request): 

39 return super().dispatch(request, *args, **kwargs) 

40 else: 

41 logger.warning("Session does not have verified eligibility") 

42 return user_error(request) 

43 

44 

45class FlowSessionRequiredMixin: 

46 """Mixin intended for use with a class-based view as the first in the MRO. 

47 

48 Gets the current `EnrollmentFlow` out of session and sets an attribute on `self`. 

49 

50 If the session is not configured with a flow, return a user error. 

51 """ 

52 

53 def dispatch(self, request, *args, **kwargs): 

54 flow = session.flow(request) 

55 if flow: 

56 self.flow = flow 

57 return super().dispatch(request, *args, **kwargs) 

58 else: 

59 logger.warning("Session not configured with enrollment flow") 

60 return user_error(request) 

61 

62 

63class PageViewMixin: 

64 """ 

65 Mixin sends an analytics event for page views. 

66 """ 

67 

68 def dispatch(self, request, *args, **kwargs): 

69 event = analytics.ViewedPageEvent(request) 

70 try: 

71 analytics.send_event(event) 

72 except Exception: 

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

74 finally: 

75 return super().dispatch(request, *args, **kwargs) 

76 

77 

78class RecaptchaEnabledMixin: 

79 """Mixin intended for use with a class-based view as the first in the MRO. 

80 

81 Configures the request with required reCAPTCHA settings.""" 

82 

83 def dispatch(self, request, *args, **kwargs): 

84 if settings.RECAPTCHA_ENABLED: 

85 request.recaptcha = { 

86 "data_field": recaptcha.DATA_FIELD, 

87 "script_api": settings.RECAPTCHA_API_KEY_URL, 

88 "site_key": settings.RECAPTCHA_SITE_KEY, 

89 } 

90 return super().dispatch(request, *args, **kwargs) 

91 

92 

93class ValidateRecaptchaMixin: 

94 """Mixin intended for use with forms where we need to verify reCAPTCHA. 

95 

96 Raises a ValidationError if it fails; otherwise continues the standard form verification. 

97 """ 

98 

99 def clean(self): 

100 if not recaptcha.verify(self.data): 

101 raise ValidationError("reCAPTCHA failed, please try again.") 

102 return super().clean()