Coverage for jutil/middleware.py: 78%

91 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-07 16:40 -0500

1import logging 

2import traceback 

3from typing import Dict, Optional 

4from urllib.parse import urlencode 

5from django.conf import settings 

6from django.http import HttpRequest 

7from django.utils import timezone 

8from ipware import get_client_ip # type: ignore 

9from jutil.email import send_email 

10 

11logger = logging.getLogger(__name__) 

12 

13 

14class EnsureOriginMiddleware: 

15 """ 

16 Ensures that request META 'HTTP_ORIGIN' is set. 

17 Uses request get_host() to set it if missing. 

18 """ 

19 

20 def __init__(self, get_response=None): 

21 self.get_response = get_response 

22 

23 def __call__(self, request): 

24 # Code to be executed for each request before 

25 # the view (and later middleware) are called. 

26 if not request.META.get("HTTP_ORIGIN", None): 26 ↛ 30line 26 didn't jump to line 30, because the condition on line 26 was never false

27 request.META["HTTP_ORIGIN"] = request.get_host() 

28 

29 # get response 

30 response = self.get_response(request) 

31 

32 # Code to be executed for each request/response after 

33 # the view is called. 

34 return response 

35 

36 

37class LogExceptionMiddleware: 

38 """ 

39 Logs exception and sends email to admins about it. 

40 Uses list of emails from settings.ADMINS. 

41 """ 

42 

43 def __init__(self, get_response=None): 

44 self.get_response = get_response 

45 

46 def __call__(self, request): 

47 return self.get_response(request) 

48 

49 def process_exception(self, request, e): 

50 """ 

51 Logs exception error message and sends email to ADMINS if hostname is not testserver and DEBUG=False. 

52 :param request: HttpRequest 

53 :param e: Exception 

54 """ 

55 assert isinstance(request, HttpRequest) 

56 method = str(request.method).upper() 

57 uri = request.build_absolute_uri() 

58 user = request.user 

59 msg = "{method} {uri}\n{err} (IP={ip}, user={user}) {trace}".format( 

60 method=method, uri=uri, user=user, ip=get_client_ip(request)[0], err=e, trace=traceback.format_exc() 

61 ) 

62 logger.error(msg) 

63 hostname = request.get_host() 

64 if not settings.DEBUG and hostname != "testserver": 64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true

65 send_email(settings.ADMINS, "Error @ {}".format(hostname), msg) 

66 

67 

68class EnsureLanguageCookieMiddleware: 

69 """ 

70 Ensures language cookie (by name settings.LANGUAGE_COOKIE_NAME) is set. 

71 Sets it as settings.LANGUAGE_CODE if missing. 

72 Allows changing settings by passing querystring parameter named settings.LANGUAGE_COOKIE_NAME 

73 (default: django_language). 

74 

75 Order of preference for the language (must be one of settings.LANGUAGES to be used): 

76 1) Explicit querystring GET parameter (e.g. ?lang=en) 

77 2) Previously stored cookie 

78 3) settings.LANGUAGE_CODE 

79 """ 

80 

81 _languages: Optional[Dict[str, str]] 

82 

83 def __init__(self, get_response=None): 

84 self.get_response = get_response 

85 self._languages = None 

86 

87 @property 

88 def languages(self) -> Dict[str, str]: 

89 if self._languages is None: 

90 self._languages = dict(settings.LANGUAGES) 

91 return self._languages 

92 

93 def __call__(self, request): 

94 lang_cookie_name = settings.LANGUAGE_COOKIE_NAME if hasattr(settings, "LANGUAGE_COOKIE_NAME") else "django_language" 

95 lang_cookie = request.COOKIES.get(lang_cookie_name) 

96 lang = request.GET.get(lang_cookie_name) 

97 if not lang: 

98 lang = lang_cookie 

99 if not lang or lang not in self.languages: 

100 lang = settings.LANGUAGE_CODE if hasattr(settings, "LANGUAGE_CODE") else "en" 

101 request.COOKIES[lang_cookie_name] = lang 

102 

103 res = self.get_response(request) 

104 

105 if lang_cookie is None or lang != lang_cookie: 

106 secure = hasattr(settings, "LANGUAGE_COOKIE_SECURE") and settings.LANGUAGE_COOKIE_SECURE 

107 httponly = hasattr(settings, "LANGUAGE_COOKIE_HTTPONLY") and settings.LANGUAGE_COOKIE_HTTPONLY 

108 res.set_cookie(lang_cookie_name, lang, secure=secure, httponly=httponly) 

109 return res 

110 

111 

112class ActivateUserProfileTimezoneMiddleware: 

113 """ 

114 Uses 'timezone' string in request.user.profile to activate user-specific timezone. 

115 """ 

116 

117 def __init__(self, get_response=None): 

118 self.get_response = get_response 

119 

120 def __call__(self, request): 

121 # Code to be executed for each request before 

122 # the view (and later middleware) are called. 

123 activated = False 

124 if request.user.is_authenticated: 124 ↛ 137line 124 didn't jump to line 137, because the condition on line 124 was never false

125 user = request.user 

126 if hasattr(user, "profile") and user.profile: 126 ↛ 134line 126 didn't jump to line 134, because the condition on line 126 was never false

127 up = user.profile 

128 if hasattr(up, "timezone") and up.timezone: 128 ↛ 132line 128 didn't jump to line 132, because the condition on line 128 was never false

129 timezone.activate(up.timezone) 

130 activated = True 

131 else: 

132 logger.warning("User profile timezone missing so user.profile.timezone could not be activated") 

133 else: 

134 logger.warning("User profile missing so user.profile.timezone could not be activated") 

135 

136 # get response 

137 response = self.get_response(request) 

138 

139 # Code to be executed for each request/response after 

140 # the view is called. 

141 if activated: 141 ↛ 143line 141 didn't jump to line 143, because the condition on line 141 was never false

142 timezone.deactivate() 

143 return response 

144 

145 

146class TestClientLoggerMiddleware: 

147 """ 

148 Logs requests to be used with Django test framework client. 

149 """ 

150 

151 ignored_paths = { 

152 "/admin/jsi18n/", 

153 "/favicon.ico", 

154 } 

155 

156 def __init__(self, get_response=None): 

157 self.get_response = get_response 

158 

159 def __call__(self, request): 

160 if settings.DEBUG: 

161 assert isinstance(request, HttpRequest) 

162 if request.path not in self.ignored_paths: 

163 url = request.path 

164 qs = request.GET.dict() 

165 if qs: 

166 url += "?" + urlencode(request.GET.dict()) 

167 logger.debug('self.client.%s("%s", data=%s)', request.method.lower(), url, request.POST.dict()) 

168 response = self.get_response(request) 

169 return response