Modules Python django.contrib.auth.{__init__, models, base_user, mixins, urls, decorators, context_processors}.py ¶
See also
Contents
Module Python django/contrib/auth/__init__.py ¶
1import inspect
2import re
3
4from django.apps import apps as django_apps
5from django.conf import settings
6from django.core.exceptions import ImproperlyConfigured
7from django.core.exceptions import PermissionDenied
8from django.middleware.csrf import rotate_token
9from django.utils.crypto import constant_time_compare
10from django.utils.module_loading import import_string
11
12from .signals import user_logged_in
13from .signals import user_logged_out
14from .signals import user_login_failed
15
16SESSION_KEY = "_auth_user_id"
17BACKEND_SESSION_KEY = "_auth_user_backend"
18HASH_SESSION_KEY = "_auth_user_hash"
19REDIRECT_FIELD_NAME = "next"
20
21
22def load_backend(path):
23 return import_string(path)()
24
25
26def _get_backends(return_tuples=False):
27 backends = []
28 for backend_path in settings.AUTHENTICATION_BACKENDS:
29 backend = load_backend(backend_path)
30 backends.append((backend, backend_path) if return_tuples else backend)
31 if not backends:
32 raise ImproperlyConfigured(
33 "No authentication backends have been defined. Does "
34 "AUTHENTICATION_BACKENDS contain anything?"
35 )
36 return backends
37
38
39def get_backends():
40 return _get_backends(return_tuples=False)
41
42
43def _clean_credentials(credentials):
44 """
45 Clean a dictionary of credentials of potentially sensitive info before
46 sending to less secure functions.
47
48 Not comprehensive - intended for user_login_failed signal
49 """
50 SENSITIVE_CREDENTIALS = re.compile("api|token|key|secret|password|signature", re.I)
51 CLEANSED_SUBSTITUTE = "********************"
52 for key in credentials:
53 if SENSITIVE_CREDENTIALS.search(key):
54 credentials[key] = CLEANSED_SUBSTITUTE
55 return credentials
56
57
58def _get_user_session_key(request):
59 # This value in the session is always serialized to a string, so we need
60 # to convert it back to Python whenever we access it.
61 return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
62
63
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):
69 try:
70 inspect.getcallargs(backend.authenticate, request, **credentials)
71 except TypeError:
72 # This backend doesn't accept these credentials as arguments. Try the next one.
73 continue
74 try:
75 user = backend.authenticate(request, **credentials)
76 except PermissionDenied:
77 # This backend says to stop in our tracks - this user should not be allowed in at all.
78 break
79 if user is None:
80 continue
81 # Annotate the user object with the path of the backend.
82 user.backend = backend_path
83 return user
84
85 # The credentials supplied are invalid to all backends, fire signal
86 user_login_failed.send(
87 sender=__name__, credentials=_clean_credentials(credentials), request=request
88 )
89
90
91def login(request, user, backend=None):
92 """
93 Persist a user id and a backend in the request. This way a user doesn't
94 have to reauthenticate on every request. Note that data set during
95 the anonymous session is retained when the user logs in.
96 """
97 session_auth_hash = ""
98 if user is None:
99 user = request.user
100 if hasattr(user, "get_session_auth_hash"):
101 session_auth_hash = user.get_session_auth_hash()
102
103 if SESSION_KEY in request.session:
104 if _get_user_session_key(request) != user.pk or (
105 session_auth_hash
106 and not constant_time_compare(
107 request.session.get(HASH_SESSION_KEY, ""), session_auth_hash
108 )
109 ):
110 # To avoid reusing another user's session, create a new, empty
111 # session if the existing session corresponds to a different
112 # authenticated user.
113 request.session.flush()
114 else:
115 request.session.cycle_key()
116
117 try:
118 backend = backend or user.backend
119 except AttributeError:
120 backends = _get_backends(return_tuples=True)
121 if len(backends) == 1:
122 _, backend = backends[0]
123 else:
124 raise ValueError(
125 "You have multiple authentication backends configured and "
126 "therefore must provide the `backend` argument or set the "
127 "`backend` attribute on the user."
128 )
129 else:
130 if not isinstance(backend, str):
131 raise TypeError(
132 "backend must be a dotted import path string (got %r)." % backend
133 )
134
135 request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
136 request.session[BACKEND_SESSION_KEY] = backend
137 request.session[HASH_SESSION_KEY] = session_auth_hash
138 if hasattr(request, "user"):
139 request.user = user
140 rotate_token(request)
141 user_logged_in.send(sender=user.__class__, request=request, user=user)
142
143
144def logout(request):
145 """
146 Remove the authenticated user's ID from the request and flush their session
147 data.
148 """
149 # Dispatch the signal before the user is logged out so the receivers have a
150 # chance to find out *who* logged out.
151 user = getattr(request, "user", None)
152 if not getattr(user, "is_authenticated", True):
153 user = None
154 user_logged_out.send(sender=user.__class__, request=request, user=user)
155 request.session.flush()
156 if hasattr(request, "user"):
157 from django.contrib.auth.models import AnonymousUser
158
159 request.user = AnonymousUser()
160
161
162def get_user_model():
163 """
164 Return the User model that is active in this project.
165 """
166 try:
167 return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)
168 except ValueError:
169 raise ImproperlyConfigured(
170 "AUTH_USER_MODEL must be of the form 'app_label.model_name'"
171 )
172 except LookupError:
173 raise ImproperlyConfigured(
174 "AUTH_USER_MODEL refers to model '%s' that has not been installed"
175 % settings.AUTH_USER_MODEL
176 )
177
178
179def get_user(request):
180 """
181 Return the user model instance associated with the given request session.
182 If no user is retrieved, return an instance of `AnonymousUser`.
183 """
184 from .models import AnonymousUser
185
186 user = None
187 try:
188 user_id = _get_user_session_key(request)
189 backend_path = request.session[BACKEND_SESSION_KEY]
190 except KeyError:
191 pass
192 else:
193 if backend_path in settings.AUTHENTICATION_BACKENDS:
194 backend = load_backend(backend_path)
195 user = backend.get_user(user_id)
196 # Verify the session
197 if hasattr(user, "get_session_auth_hash"):
198 session_hash = request.session.get(HASH_SESSION_KEY)
199 session_hash_verified = session_hash and constant_time_compare(
200 session_hash, user.get_session_auth_hash()
201 )
202 if not session_hash_verified:
203 request.session.flush()
204 user = None
205
206 return user or AnonymousUser()
207
208
209def get_permission_codename(action, opts):
210 """
211 Return the codename of the permission for the specified action.
212 """
213 return "%s_%s" % (action, opts.model_name)
214
215
216def update_session_auth_hash(request, user):
217 """
218 Updating a user's password logs out all sessions for the user.
219
220 Take the current request and the updated user object from which the new
221 session hash will be derived and update the session hash appropriately to
222 prevent a password change from logging out the session from which the
223 password was changed.
224 """
225 request.session.cycle_key()
226 if hasattr(user, "get_session_auth_hash") and request.user == user:
227 request.session[HASH_SESSION_KEY] = user.get_session_auth_hash()
228
229
230default_app_config = "django.contrib.auth.apps.AuthConfig"
Module Python django/contrib/auth/models.py ¶
1from django.contrib import auth
2from django.contrib.auth.base_user import AbstractBaseUser
3from django.contrib.auth.base_user import BaseUserManager
4from django.contrib.contenttypes.models import ContentType
5from django.core.exceptions import PermissionDenied
6from django.core.mail import send_mail
7from django.db import models
8from django.db.models.manager import EmptyManager
9from django.utils import timezone
10from django.utils.translation import gettext_lazy as _
11
12from .validators import UnicodeUsernameValidator
13
14
15def update_last_login(sender, user, **kwargs):
16 """
17 A signal receiver which updates the last_login date for
18 the user logging in.
19 """
20 user.last_login = timezone.now()
21 user.save(update_fields=["last_login"])
22
23
24class PermissionManager(models.Manager):
25 use_in_migrations = True
26
27 def get_by_natural_key(self, codename, app_label, model):
28 return self.get(
29 codename=codename,
30 content_type=ContentType.objects.db_manager(self.db).get_by_natural_key(
31 app_label, model
32 ),
33 )
34
35
36class Permission(models.Model):
37 """
38 The permissions system provides a way to assign permissions to specific
39 users and groups of users.
40
41 The permission system is used by the Django admin site, but may also be
42 useful in your own code. The Django admin site uses permissions as follows:
43
44 - The "add" permission limits the user's ability to view the "add" form
45 and add an object.
46 - The "change" permission limits a user's ability to view the change
47 list, view the "change" form and change an object.
48 - The "delete" permission limits the ability to delete an object.
49 - The "view" permission limits the ability to view an object.
50
51 Permissions are set globally per type of object, not per specific object
52 instance. It is possible to say "Mary may change news stories," but it's
53 not currently possible to say "Mary may change news stories, but only the
54 ones she created herself" or "Mary may only change news stories that have a
55 certain status or publication date."
56
57 The permissions listed above are automatically created for each model.
58 """
59
60 name = models.CharField(_("name"), max_length=255)
61 content_type = models.ForeignKey(
62 ContentType, models.CASCADE, verbose_name=_("content type")
63 )
64 codename = models.CharField(_("codename"), max_length=100)
65
66 objects = PermissionManager()
67
68 class Meta:
69 verbose_name = _("permission")
70 verbose_name_plural = _("permissions")
71 unique_together = (("content_type", "codename"),)
72 ordering = ("content_type__app_label", "content_type__model", "codename")
73
74 def __str__(self):
75 return "%s | %s" % (self.content_type, self.name)
76
77 def natural_key(self):
78 return (self.codename,) + self.content_type.natural_key()
79
80 natural_key.dependencies = ["contenttypes.contenttype"]
81
82
83class GroupManager(models.Manager):
84 """
85 The manager for the auth's Group model.
86 """
87
88 use_in_migrations = True
89
90 def get_by_natural_key(self, name):
91 return self.get(name=name)
92
93
94class Group(models.Model):
95 """
96 Groups are a generic way of categorizing users to apply permissions, or
97 some other label, to those users. A user can belong to any number of
98 groups.
99
100 A user in a group automatically has all the permissions granted to that
101 group. For example, if the group 'Site editors' has the permission
102 can_edit_home_page, any user in that group will have that permission.
103
104 Beyond permissions, groups are a convenient way to categorize users to
105 apply some label, or extended functionality, to them. For example, you
106 could create a group 'Special users', and you could write code that would
107 do special things to those users -- such as giving them access to a
108 members-only portion of your site, or sending them members-only email
109 messages.
110 """
111
112 name = models.CharField(_("name"), max_length=150, unique=True)
113 permissions = models.ManyToManyField(
114 Permission, verbose_name=_("permissions"), blank=True
115 )
116
117 objects = GroupManager()
118
119 class Meta:
120 verbose_name = _("group")
121 verbose_name_plural = _("groups")
122
123 def __str__(self):
124 return self.name
125
126 def natural_key(self):
127 return (self.name,)
128
129
130class UserManager(BaseUserManager):
131 use_in_migrations = True
132
133 def _create_user(self, username, email, password, **extra_fields):
134 """
135 Create and save a user with the given username, email, and password.
136 """
137 if not username:
138 raise ValueError("The given username must be set")
139 email = self.normalize_email(email)
140 username = self.model.normalize_username(username)
141 user = self.model(username=username, email=email, **extra_fields)
142 user.set_password(password)
143 user.save(using=self._db)
144 return user
145
146 def create_user(self, username, email=None, password=None, **extra_fields):
147 extra_fields.setdefault("is_staff", False)
148 extra_fields.setdefault("is_superuser", False)
149 return self._create_user(username, email, password, **extra_fields)
150
151 def create_superuser(self, username, email, password, **extra_fields):
152 extra_fields.setdefault("is_staff", True)
153 extra_fields.setdefault("is_superuser", True)
154
155 if extra_fields.get("is_staff") is not True:
156 raise ValueError("Superuser must have is_staff=True.")
157 if extra_fields.get("is_superuser") is not True:
158 raise ValueError("Superuser must have is_superuser=True.")
159
160 return self._create_user(username, email, password, **extra_fields)
161
162
163# A few helper functions for common logic between User and AnonymousUser.
164def _user_get_all_permissions(user, obj):
165 permissions = set()
166 for backend in auth.get_backends():
167 if hasattr(backend, "get_all_permissions"):
168 permissions.update(backend.get_all_permissions(user, obj))
169 return permissions
170
171
172def _user_has_perm(user, perm, obj):
173 """
174 A backend can raise `PermissionDenied` to short-circuit permission checking.
175 """
176 for backend in auth.get_backends():
177 if not hasattr(backend, "has_perm"):
178 continue
179 try:
180 if backend.has_perm(user, perm, obj):
181 return True
182 except PermissionDenied:
183 return False
184 return False
185
186
187def _user_has_module_perms(user, app_label):
188 """
189 A backend can raise `PermissionDenied` to short-circuit permission checking.
190 """
191 for backend in auth.get_backends():
192 if not hasattr(backend, "has_module_perms"):
193 continue
194 try:
195 if backend.has_module_perms(user, app_label):
196 return True
197 except PermissionDenied:
198 return False
199 return False
200
201
202class PermissionsMixin(models.Model):
203 """
204 Add the fields and methods necessary to support the Group and Permission
205 models using the ModelBackend.
206 """
207
208 is_superuser = models.BooleanField(
209 _("superuser status"),
210 default=False,
211 help_text=_(
212 "Designates that this user has all permissions without "
213 "explicitly assigning them."
214 ),
215 )
216 groups = models.ManyToManyField(
217 Group,
218 verbose_name=_("groups"),
219 blank=True,
220 help_text=_(
221 "The groups this user belongs to. A user will get all permissions "
222 "granted to each of their groups."
223 ),
224 related_name="user_set",
225 related_query_name="user",
226 )
227 user_permissions = models.ManyToManyField(
228 Permission,
229 verbose_name=_("user permissions"),
230 blank=True,
231 help_text=_("Specific permissions for this user."),
232 related_name="user_set",
233 related_query_name="user",
234 )
235
236 class Meta:
237 abstract = True
238
239 def get_group_permissions(self, obj=None):
240 """
241 Return a list of permission strings that this user has through their
242 groups. Query all available auth backends. If an object is passed in,
243 return only permissions matching this object.
244 """
245 permissions = set()
246 for backend in auth.get_backends():
247 if hasattr(backend, "get_group_permissions"):
248 permissions.update(backend.get_group_permissions(self, obj))
249 return permissions
250
251 def get_all_permissions(self, obj=None):
252 return _user_get_all_permissions(self, obj)
253
254 def has_perm(self, perm, obj=None):
255 """
256 Return True if the user has the specified permission. Query all
257 available auth backends, but return immediately if any backend returns
258 True. Thus, a user who has permission from a single auth backend is
259 assumed to have permission in general. If an object is provided, check
260 permissions for that object.
261 """
262 # Active superusers have all permissions.
263 if self.is_active and self.is_superuser:
264 return True
265
266 # Otherwise we need to check the backends.
267 return _user_has_perm(self, perm, obj)
268
269 def has_perms(self, perm_list, obj=None):
270 """
271 Return True if the user has each of the specified permissions. If
272 object is passed, check if the user has all required perms for it.
273 """
274 return all(self.has_perm(perm, obj) for perm in perm_list)
275
276 def has_module_perms(self, app_label):
277 """
278 Return True if the user has any permissions in the given app label.
279 Use similar logic as has_perm(), above.
280 """
281 # Active superusers have all permissions.
282 if self.is_active and self.is_superuser:
283 return True
284
285 return _user_has_module_perms(self, app_label)
286
287
288class AbstractUser(AbstractBaseUser, PermissionsMixin):
289 """
290 An abstract base class implementing a fully featured User model with
291 admin-compliant permissions.
292
293 Username and password are required. Other fields are optional.
294 """
295
296 username_validator = UnicodeUsernameValidator()
297
298 username = models.CharField(
299 _("username"),
300 max_length=150,
301 unique=True,
302 help_text=_(
303 "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
304 ),
305 validators=[username_validator],
306 error_messages={"unique": _("A user with that username already exists.")},
307 )
308 first_name = models.CharField(_("first name"), max_length=30, blank=True)
309 last_name = models.CharField(_("last name"), max_length=150, blank=True)
310 email = models.EmailField(_("email address"), blank=True)
311 is_staff = models.BooleanField(
312 _("staff status"),
313 default=False,
314 help_text=_("Designates whether the user can log into this admin site."),
315 )
316 is_active = models.BooleanField(
317 _("active"),
318 default=True,
319 help_text=_(
320 "Designates whether this user should be treated as active. "
321 "Unselect this instead of deleting accounts."
322 ),
323 )
324 date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
325
326 objects = UserManager()
327
328 EMAIL_FIELD = "email"
329 USERNAME_FIELD = "username"
330 REQUIRED_FIELDS = ["email"]
331
332 class Meta:
333 verbose_name = _("user")
334 verbose_name_plural = _("users")
335 abstract = True
336
337 def clean(self):
338 super().clean()
339 self.email = self.__class__.objects.normalize_email(self.email)
340
341 def get_full_name(self):
342 """
343 Return the first_name plus the last_name, with a space in between.
344 """
345 full_name = "%s %s" % (self.first_name, self.last_name)
346 return full_name.strip()
347
348 def get_short_name(self):
349 """Return the short name for the user."""
350 return self.first_name
351
352 def email_user(self, subject, message, from_email=None, **kwargs):
353 """Send an email to this user."""
354 send_mail(subject, message, from_email, [self.email], **kwargs)
355
356
357class User(AbstractUser):
358 """
359 Users within the Django authentication system are represented by this
360 model.
361
362 Username and password are required. Other fields are optional.
363 """
364
365 class Meta(AbstractUser.Meta):
366 swappable = "AUTH_USER_MODEL"
367
368
369class AnonymousUser:
370 id = None
371 pk = None
372 username = ""
373 is_staff = False
374 is_active = False
375 is_superuser = False
376 _groups = EmptyManager(Group)
377 _user_permissions = EmptyManager(Permission)
378
379 def __str__(self):
380 return "AnonymousUser"
381
382 def __eq__(self, other):
383 return isinstance(other, self.__class__)
384
385 def __hash__(self):
386 return 1 # instances always return the same hash value
387
388 def __int__(self):
389 raise TypeError(
390 "Cannot cast AnonymousUser to int. Are you trying to use it in place of User?"
391 )
392
393 def save(self):
394 raise NotImplementedError(
395 "Django doesn't provide a DB representation for AnonymousUser."
396 )
397
398 def delete(self):
399 raise NotImplementedError(
400 "Django doesn't provide a DB representation for AnonymousUser."
401 )
402
403 def set_password(self, raw_password):
404 raise NotImplementedError(
405 "Django doesn't provide a DB representation for AnonymousUser."
406 )
407
408 def check_password(self, raw_password):
409 raise NotImplementedError(
410 "Django doesn't provide a DB representation for AnonymousUser."
411 )
412
413 @property
414 def groups(self):
415 return self._groups
416
417 @property
418 def user_permissions(self):
419 return self._user_permissions
420
421 def get_group_permissions(self, obj=None):
422 return set()
423
424 def get_all_permissions(self, obj=None):
425 return _user_get_all_permissions(self, obj=obj)
426
427 def has_perm(self, perm, obj=None):
428 return _user_has_perm(self, perm, obj=obj)
429
430 def has_perms(self, perm_list, obj=None):
431 return all(self.has_perm(perm, obj) for perm in perm_list)
432
433 def has_module_perms(self, module):
434 return _user_has_module_perms(self, module)
435
436 @property
437 def is_anonymous(self):
438 return True
439
440 @property
441 def is_authenticated(self):
442 return False
443
444 def get_username(self):
445 return self.username
Module Python django/contrib/auth/base_user.py ¶
1"""
2This module allows importing AbstractBaseUser even when django.contrib.auth is
3not in INSTALLED_APPS.
4"""
5import unicodedata
6
7from django.contrib.auth import password_validation
8from django.contrib.auth.hashers import check_password
9from django.contrib.auth.hashers import is_password_usable
10from django.contrib.auth.hashers import make_password
11from django.db import models
12from django.utils.crypto import get_random_string
13from django.utils.crypto import salted_hmac
14from django.utils.translation import gettext_lazy as _
15
16
17class BaseUserManager(models.Manager):
18 @classmethod
19 def normalize_email(cls, email):
20 """
21 Normalize the email address by lowercasing the domain part of it.
22 """
23 email = email or ""
24 try:
25 email_name, domain_part = email.strip().rsplit("@", 1)
26 except ValueError:
27 pass
28 else:
29 email = email_name + "@" + domain_part.lower()
30 return email
31
32 def make_random_password(
33 self,
34 length=10,
35 allowed_chars="abcdefghjkmnpqrstuvwxyz" "ABCDEFGHJKLMNPQRSTUVWXYZ" "23456789",
36 ):
37 """
38 Generate a random password with the given length and given
39 allowed_chars. The default value of allowed_chars does not have "I" or
40 "O" or letters and digits that look similar -- just to avoid confusion.
41 """
42 return get_random_string(length, allowed_chars)
43
44 def get_by_natural_key(self, username):
45 return self.get(**{self.model.USERNAME_FIELD: username})
46
47
48class AbstractBaseUser(models.Model):
49 password = models.CharField(_("password"), max_length=128)
50 last_login = models.DateTimeField(_("last login"), blank=True, null=True)
51
52 is_active = True
53
54 REQUIRED_FIELDS = []
55
56 # Stores the raw password if set_password() is called so that it can
57 # be passed to password_changed() after the model is saved.
58 _password = None
59
60 class Meta:
61 abstract = True
62
63 def __str__(self):
64 return self.get_username()
65
66 def save(self, *args, **kwargs):
67 super().save(*args, **kwargs)
68 if self._password is not None:
69 password_validation.password_changed(self._password, self)
70 self._password = None
71
72 def get_username(self):
73 """Return the username for this User."""
74 return getattr(self, self.USERNAME_FIELD)
75
76 def clean(self):
77 setattr(self, self.USERNAME_FIELD, self.normalize_username(self.get_username()))
78
79 def natural_key(self):
80 return (self.get_username(),)
81
82 @property
83 def is_anonymous(self):
84 """
85 Always return False. This is a way of comparing User objects to
86 anonymous users.
87 """
88 return False
89
90 @property
91 def is_authenticated(self):
92 """
93 Always return True. This is a way to tell if the user has been
94 authenticated in templates.
95 """
96 return True
97
98 def set_password(self, raw_password):
99 self.password = make_password(raw_password)
100 self._password = raw_password
101
102 def check_password(self, raw_password):
103 """
104 Return a boolean of whether the raw_password was correct. Handles
105 hashing formats behind the scenes.
106 """
107
108 def setter(raw_password):
109 self.set_password(raw_password)
110 # Password hash upgrades shouldn't be considered password changes.
111 self._password = None
112 self.save(update_fields=["password"])
113
114 return check_password(raw_password, self.password, setter)
115
116 def set_unusable_password(self):
117 # Set a value that will never be a valid hash
118 self.password = make_password(None)
119
120 def has_usable_password(self):
121 """
122 Return False if set_unusable_password() has been called for this user.
123 """
124 return is_password_usable(self.password)
125
126 def get_session_auth_hash(self):
127 """
128 Return an HMAC of the password field.
129 """
130 key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"
131 return salted_hmac(key_salt, self.password).hexdigest()
132
133 @classmethod
134 def get_email_field_name(cls):
135 try:
136 return cls.EMAIL_FIELD
137 except AttributeError:
138 return "email"
139
140 @classmethod
141 def normalize_username(cls, username):
142 return (
143 unicodedata.normalize("NFKC", username)
144 if isinstance(username, str)
145 else username
146 )
Module Python django/contrib/auth/mixins.py ¶
1from django.conf import settings
2from django.contrib.auth import REDIRECT_FIELD_NAME
3from django.contrib.auth.views import redirect_to_login
4from django.core.exceptions import ImproperlyConfigured
5from django.core.exceptions import PermissionDenied
6
7
8class AccessMixin:
9 """
10 Abstract CBV mixin that gives access mixins the same customizable
11 functionality.
12 """
13
14 login_url = None
15 permission_denied_message = ""
16 raise_exception = False
17 redirect_field_name = REDIRECT_FIELD_NAME
18
19 def get_login_url(self):
20 """
21 Override this method to override the login_url attribute.
22 """
23 login_url = self.login_url or settings.LOGIN_URL
24 if not login_url:
25 raise ImproperlyConfigured(
26 "{0} is missing the login_url attribute. Define {0}.login_url, settings.LOGIN_URL, or override "
27 "{0}.get_login_url().".format(self.__class__.__name__)
28 )
29 return str(login_url)
30
31 def get_permission_denied_message(self):
32 """
33 Override this method to override the permission_denied_message attribute.
34 """
35 return self.permission_denied_message
36
37 def get_redirect_field_name(self):
38 """
39 Override this method to override the redirect_field_name attribute.
40 """
41 return self.redirect_field_name
42
43 def handle_no_permission(self):
44 if self.raise_exception or self.request.user.is_authenticated:
45 raise PermissionDenied(self.get_permission_denied_message())
46 return redirect_to_login(
47 self.request.get_full_path(),
48 self.get_login_url(),
49 self.get_redirect_field_name(),
50 )
51
52
53class LoginRequiredMixin(AccessMixin):
54 """Verify that the current user is authenticated."""
55
56 def dispatch(self, request, *args, **kwargs):
57 if not request.user.is_authenticated:
58 return self.handle_no_permission()
59 return super().dispatch(request, *args, **kwargs)
60
61
62class PermissionRequiredMixin(AccessMixin):
63 """Verify that the current user has all specified permissions."""
64
65 permission_required = None
66
67 def get_permission_required(self):
68 """
69 Override this method to override the permission_required attribute.
70 Must return an iterable.
71 """
72 if self.permission_required is None:
73 raise ImproperlyConfigured(
74 "{0} is missing the permission_required attribute. Define {0}.permission_required, or override "
75 "{0}.get_permission_required().".format(self.__class__.__name__)
76 )
77 if isinstance(self.permission_required, str):
78 perms = (self.permission_required,)
79 else:
80 perms = self.permission_required
81 return perms
82
83 def has_permission(self):
84 """
85 Override this method to customize the way permissions are checked.
86 """
87 perms = self.get_permission_required()
88 return self.request.user.has_perms(perms)
89
90 def dispatch(self, request, *args, **kwargs):
91 if not self.has_permission():
92 return self.handle_no_permission()
93 return super().dispatch(request, *args, **kwargs)
94
95
96class UserPassesTestMixin(AccessMixin):
97 """
98 Deny a request with a permission error if the test_func() method returns
99 False.
100 """
101
102 def test_func(self):
103 raise NotImplementedError(
104 "{0} is missing the implementation of the test_func() method.".format(
105 self.__class__.__name__
106 )
107 )
108
109 def get_test_func(self):
110 """
111 Override this method to use a different test_func method.
112 """
113 return self.test_func
114
115 def dispatch(self, request, *args, **kwargs):
116 user_test_result = self.get_test_func()()
117 if not user_test_result:
118 return self.handle_no_permission()
119 return super().dispatch(request, *args, **kwargs)
Module Python django/contrib/auth/urls.py ¶
1# The views used below are normally mapped in django.contrib.admin.urls.py
2# This URLs file is used to provide a reliable view deployment for test purposes.
3# It is also provided as a convenience to those who want to deploy these URLs
4# elsewhere.
5from django.contrib.auth import views
6from django.urls import path
7
8urlpatterns = [
9 path("login/", views.LoginView.as_view(), name="login"),
10 path("logout/", views.LogoutView.as_view(), name="logout"),
11 path(
12 "password_change/", views.PasswordChangeView.as_view(), name="password_change"
13 ),
14 path(
15 "password_change/done/",
16 views.PasswordChangeDoneView.as_view(),
17 name="password_change_done",
18 ),
19 path("password_reset/", views.PasswordResetView.as_view(), name="password_reset"),
20 path(
21 "password_reset/done/",
22 views.PasswordResetDoneView.as_view(),
23 name="password_reset_done",
24 ),
25 path(
26 "reset/<uidb64>/<token>/",
27 views.PasswordResetConfirmView.as_view(),
28 name="password_reset_confirm",
29 ),
30 path(
31 "reset/done/",
32 views.PasswordResetCompleteView.as_view(),
33 name="password_reset_complete",
34 ),
35]
Module Python django/contrib/auth/decorators.py ¶
1from functools import wraps
2from urllib.parse import urlparse
3
4from django.conf import settings
5from django.contrib.auth import REDIRECT_FIELD_NAME
6from django.core.exceptions import PermissionDenied
7from django.shortcuts import resolve_url
8
9
10def user_passes_test(
11 test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME
12):
13 """
14 Decorator for views that checks that the user passes the given test,
15 redirecting to the log-in page if necessary. The test should be a callable
16 that takes the user object and returns True if the user passes.
17 """
18
19 def decorator(view_func):
20 @wraps(view_func)
21 def _wrapped_view(request, *args, **kwargs):
22 if test_func(request.user):
23 return view_func(request, *args, **kwargs)
24 path = request.build_absolute_uri()
25 resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
26 # If the login url is the same scheme and net location then just
27 # use the path as the "next" url.
28 login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
29 current_scheme, current_netloc = urlparse(path)[:2]
30 if (not login_scheme or login_scheme == current_scheme) and (
31 not login_netloc or login_netloc == current_netloc
32 ):
33 path = request.get_full_path()
34 from django.contrib.auth.views import redirect_to_login
35
36 return redirect_to_login(path, resolved_login_url, redirect_field_name)
37
38 return _wrapped_view
39
40 return decorator
41
42
43def login_required(
44 function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None
45):
46 """
47 Decorator for views that checks that the user is logged in, redirecting
48 to the log-in page if necessary.
49 """
50 actual_decorator = user_passes_test(
51 lambda u: u.is_authenticated,
52 login_url=login_url,
53 redirect_field_name=redirect_field_name,
54 )
55 if function:
56 return actual_decorator(function)
57 return actual_decorator
58
59
60def permission_required(perm, login_url=None, raise_exception=False):
61 """
62 Decorator for views that checks whether a user has a particular permission
63 enabled, redirecting to the log-in page if necessary.
64 If the raise_exception parameter is given the PermissionDenied exception
65 is raised.
66 """
67
68 def check_perms(user):
69 if isinstance(perm, str):
70 perms = (perm,)
71 else:
72 perms = perm
73 # First check if the user has the permission (even anon users)
74 if user.has_perms(perms):
75 return True
76 # In case the 403 handler should be called raise the exception
77 if raise_exception:
78 raise PermissionDenied
79 # As the last resort, show the login form
80 return False
81
82 return user_passes_test(check_perms, login_url=login_url)
Module Python django/contrib/auth/context_processors.py ¶
1# PermWrapper and PermLookupDict proxy the permissions system into objects that
2# the template system can understand.
3
4
5class PermLookupDict:
6 def __init__(self, user, app_label):
7 self.user, self.app_label = user, app_label
8
9 def __repr__(self):
10 return str(self.user.get_all_permissions())
11
12 def __getitem__(self, perm_name):
13 return self.user.has_perm("%s.%s" % (self.app_label, perm_name))
14
15 def __iter__(self):
16 # To fix 'item in perms.someapp' and __getitem__ interaction we need to
17 # define __iter__. See #18979 for details.
18 raise TypeError("PermLookupDict is not iterable.")
19
20 def __bool__(self):
21 return self.user.has_module_perms(self.app_label)
22
23
24class PermWrapper:
25 def __init__(self, user):
26 self.user = user
27
28 def __getitem__(self, app_label):
29 return PermLookupDict(self.user, app_label)
30
31 def __iter__(self):
32 # I am large, I contain multitudes.
33 raise TypeError("PermWrapper is not iterable.")
34
35 def __contains__(self, perm_name):
36 """
37 Lookup by "someapp" or "someapp.someperm" in perms.
38 """
39 if "." not in perm_name:
40 # The name refers to module.
41 return bool(self[perm_name])
42 app_label, perm_name = perm_name.split(".", 1)
43 return self[app_label][perm_name]
44
45
46def auth(request):
47 """
48 Return context variables required by apps that use Django's authentication
49 system.
50
51 If there is no 'user' attribute in the request, use AnonymousUser (from
52 django.contrib.auth).
53 """
54 if hasattr(request, "user"):
55 user = request.user
56 else:
57 from django.contrib.auth.models import AnonymousUser
58
59 user = AnonymousUser()
60
61 return {"user": user, "perms": PermWrapper(user)}