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
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import inspect
2import re
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
12from .signals import user_logged_in, user_logged_out, user_login_failed
14SESSION_KEY = "_auth_user_id"
15BACKEND_SESSION_KEY = "_auth_user_backend"
16HASH_SESSION_KEY = "_auth_user_hash"
17REDIRECT_FIELD_NAME = "next"
20def load_backend(path):
21 return import_string(path)()
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
37def get_backends():
38 return _get_backends(return_tuples=False)
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.
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
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])
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
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 )
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()
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()
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 )
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)
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
162 request.user = AnonymousUser()
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 )
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
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
209 return user or AnonymousUser()
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)
219def update_session_auth_hash(request, user):
220 """
221 Updating a user's password logs out all sessions for the user.
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()