Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/contrib/auth/__init__.py: 38%

119 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1import inspect 

2import re 

3 

4from django.apps import apps as django_apps 

5from django.conf import settings 

6from django.core.exceptions import ImproperlyConfigured, PermissionDenied 

7from django.middleware.csrf import rotate_token 

8from django.utils.crypto import constant_time_compare 

9from django.utils.module_loading import import_string 

10from django.views.decorators.debug import sensitive_variables 

11 

12from .signals import user_logged_in, user_logged_out, user_login_failed 

13 

14SESSION_KEY = "_auth_user_id" 

15BACKEND_SESSION_KEY = "_auth_user_backend" 

16HASH_SESSION_KEY = "_auth_user_hash" 

17REDIRECT_FIELD_NAME = "next" 

18 

19 

20def load_backend(path): 

21 return import_string(path)() 

22 

23 

24def _get_backends(return_tuples=False): 

25 backends = [] 

26 for backend_path in settings.AUTHENTICATION_BACKENDS: 

27 backend = load_backend(backend_path) 

28 backends.append((backend, backend_path) if return_tuples else backend) 

29 if not backends: 29 ↛ 30line 29 didn't jump to line 30, because the condition on line 29 was never true

30 raise ImproperlyConfigured( 

31 "No authentication backends have been defined. Does " 

32 "AUTHENTICATION_BACKENDS contain anything?" 

33 ) 

34 return backends 

35 

36 

37def get_backends(): 

38 return _get_backends(return_tuples=False) 

39 

40 

41@sensitive_variables("credentials") 

42def _clean_credentials(credentials): 

43 """ 

44 Clean a dictionary of credentials of potentially sensitive info before 

45 sending to less secure functions. 

46 

47 Not comprehensive - intended for user_login_failed signal 

48 """ 

49 SENSITIVE_CREDENTIALS = re.compile("api|token|key|secret|password|signature", re.I) 

50 CLEANSED_SUBSTITUTE = "********************" 

51 for key in credentials: 

52 if SENSITIVE_CREDENTIALS.search(key): 

53 credentials[key] = CLEANSED_SUBSTITUTE 

54 return credentials 

55 

56 

57def _get_user_session_key(request): 

58 # This value in the session is always serialized to a string, so we need 

59 # to convert it back to Python whenever we access it. 

60 return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY]) 

61 

62 

63@sensitive_variables("credentials") 

64def authenticate(request=None, **credentials): 

65 """ 

66 If the given credentials are valid, return a User object. 

67 """ 

68 for backend, backend_path in _get_backends(return_tuples=True): 68 ↛ 89line 68 didn't jump to line 89, because the loop on line 68 didn't complete

69 backend_signature = inspect.signature(backend.authenticate) 

70 try: 

71 backend_signature.bind(request, **credentials) 

72 except TypeError: 

73 # This backend doesn't accept these credentials as arguments. Try 

74 # the next one. 

75 continue 

76 try: 

77 user = backend.authenticate(request, **credentials) 

78 except PermissionDenied: 

79 # This backend says to stop in our tracks - this user should not be 

80 # allowed in at all. 

81 break 

82 if user is None: 82 ↛ 83line 82 didn't jump to line 83, because the condition on line 82 was never true

83 continue 

84 # Annotate the user object with the path of the backend. 

85 user.backend = backend_path 

86 return user 

87 

88 # The credentials supplied are invalid to all backends, fire signal 

89 user_login_failed.send( 

90 sender=__name__, credentials=_clean_credentials(credentials), request=request 

91 ) 

92 

93 

94def login(request, user, backend=None): 

95 """ 

96 Persist a user id and a backend in the request. This way a user doesn't 

97 have to reauthenticate on every request. Note that data set during 

98 the anonymous session is retained when the user logs in. 

99 """ 

100 session_auth_hash = "" 

101 if user is None: 

102 user = request.user 

103 if hasattr(user, "get_session_auth_hash"): 

104 session_auth_hash = user.get_session_auth_hash() 

105 

106 if SESSION_KEY in request.session: 

107 if _get_user_session_key(request) != user.pk or ( 

108 session_auth_hash 

109 and not constant_time_compare( 

110 request.session.get(HASH_SESSION_KEY, ""), session_auth_hash 

111 ) 

112 ): 

113 # To avoid reusing another user's session, create a new, empty 

114 # session if the existing session corresponds to a different 

115 # authenticated user. 

116 request.session.flush() 

117 else: 

118 request.session.cycle_key() 

119 

120 try: 

121 backend = backend or user.backend 

122 except AttributeError: 

123 backends = _get_backends(return_tuples=True) 

124 if len(backends) == 1: 

125 _, backend = backends[0] 

126 else: 

127 raise ValueError( 

128 "You have multiple authentication backends configured and " 

129 "therefore must provide the `backend` argument or set the " 

130 "`backend` attribute on the user." 

131 ) 

132 else: 

133 if not isinstance(backend, str): 

134 raise TypeError( 

135 "backend must be a dotted import path string (got %r)." % backend 

136 ) 

137 

138 request.session[SESSION_KEY] = user._meta.pk.value_to_string(user) 

139 request.session[BACKEND_SESSION_KEY] = backend 

140 request.session[HASH_SESSION_KEY] = session_auth_hash 

141 if hasattr(request, "user"): 

142 request.user = user 

143 rotate_token(request) 

144 user_logged_in.send(sender=user.__class__, request=request, user=user) 

145 

146 

147def logout(request): 

148 """ 

149 Remove the authenticated user's ID from the request and flush their session 

150 data. 

151 """ 

152 # Dispatch the signal before the user is logged out so the receivers have a 

153 # chance to find out *who* logged out. 

154 user = getattr(request, "user", None) 

155 if not getattr(user, "is_authenticated", True): 

156 user = None 

157 user_logged_out.send(sender=user.__class__, request=request, user=user) 

158 request.session.flush() 

159 if hasattr(request, "user"): 

160 from django.contrib.auth.models import AnonymousUser 

161 

162 request.user = AnonymousUser() 

163 

164 

165def get_user_model(): 

166 """ 

167 Return the User model that is active in this project. 

168 """ 

169 try: 

170 return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False) 

171 except ValueError: 

172 raise ImproperlyConfigured( 

173 "AUTH_USER_MODEL must be of the form 'app_label.model_name'" 

174 ) 

175 except LookupError: 

176 raise ImproperlyConfigured( 

177 "AUTH_USER_MODEL refers to model '%s' that has not been installed" 

178 % settings.AUTH_USER_MODEL 

179 ) 

180 

181 

182def get_user(request): 

183 """ 

184 Return the user model instance associated with the given request session. 

185 If no user is retrieved, return an instance of `AnonymousUser`. 

186 """ 

187 from .models import AnonymousUser 

188 

189 user = None 

190 try: 

191 user_id = _get_user_session_key(request) 

192 backend_path = request.session[BACKEND_SESSION_KEY] 

193 except KeyError: 

194 pass 

195 else: 

196 if backend_path in settings.AUTHENTICATION_BACKENDS: 

197 backend = load_backend(backend_path) 

198 user = backend.get_user(user_id) 

199 # Verify the session 

200 if hasattr(user, "get_session_auth_hash"): 

201 session_hash = request.session.get(HASH_SESSION_KEY) 

202 session_hash_verified = session_hash and constant_time_compare( 

203 session_hash, user.get_session_auth_hash() 

204 ) 

205 if not session_hash_verified: 

206 request.session.flush() 

207 user = None 

208 

209 return user or AnonymousUser() 

210 

211 

212def get_permission_codename(action, opts): 

213 """ 

214 Return the codename of the permission for the specified action. 

215 """ 

216 return "%s_%s" % (action, opts.model_name) 

217 

218 

219def update_session_auth_hash(request, user): 

220 """ 

221 Updating a user's password logs out all sessions for the user. 

222 

223 Take the current request and the updated user object from which the new 

224 session hash will be derived and update the session hash appropriately to 

225 prevent a password change from logging out the session from which the 

226 password was changed. 

227 """ 

228 request.session.cycle_key() 

229 if hasattr(user, "get_session_auth_hash") and request.user == user: 

230 request.session[HASH_SESSION_KEY] = user.get_session_auth_hash()