|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- from django.apps import apps
- from django.contrib import auth
- from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
- from django.contrib.auth.hashers import make_password
- from django.contrib.contenttypes.models import ContentType
- from django.core.exceptions import PermissionDenied
- from django.core.mail import send_mail
- from django.db import models
- from django.db.models.manager import EmptyManager
- from django.utils import timezone
- from django.utils.itercompat import is_iterable
- from django.utils.translation import gettext_lazy as _
-
- from .validators import UnicodeUsernameValidator
-
-
- def update_last_login(sender, user, **kwargs):
- """
- A signal receiver which updates the last_login date for
- the user logging in.
- """
- user.last_login = timezone.now()
- user.save(update_fields=["last_login"])
-
-
- class PermissionManager(models.Manager):
- use_in_migrations = True
-
- def get_by_natural_key(self, codename, app_label, model):
- return self.get(
- codename=codename,
- content_type=ContentType.objects.db_manager(self.db).get_by_natural_key(
- app_label, model
- ),
- )
-
-
- class Permission(models.Model):
- """
- The permissions system provides a way to assign permissions to specific
- users and groups of users.
-
- The permission system is used by the Django admin site, but may also be
- useful in your own code. The Django admin site uses permissions as follows:
-
- - The "add" permission limits the user's ability to view the "add" form
- and add an object.
- - The "change" permission limits a user's ability to view the change
- list, view the "change" form and change an object.
- - The "delete" permission limits the ability to delete an object.
- - The "view" permission limits the ability to view an object.
-
- Permissions are set globally per type of object, not per specific object
- instance. It is possible to say "Mary may change news stories," but it's
- not currently possible to say "Mary may change news stories, but only the
- ones she created herself" or "Mary may only change news stories that have a
- certain status or publication date."
-
- The permissions listed above are automatically created for each model.
- """
-
- name = models.CharField(_("name"), max_length=255)
- content_type = models.ForeignKey(
- ContentType,
- models.CASCADE,
- verbose_name=_("content type"),
- )
- codename = models.CharField(_("codename"), max_length=100)
-
- objects = PermissionManager()
-
- class Meta:
- verbose_name = _("permission")
- verbose_name_plural = _("permissions")
- unique_together = [["content_type", "codename"]]
- ordering = ["content_type__app_label", "content_type__model", "codename"]
-
- def __str__(self):
- return "%s | %s" % (self.content_type, self.name)
-
- def natural_key(self):
- return (self.codename,) + self.content_type.natural_key()
-
- natural_key.dependencies = ["contenttypes.contenttype"]
-
-
- class GroupManager(models.Manager):
- """
- The manager for the auth's Group model.
- """
-
- use_in_migrations = True
-
- def get_by_natural_key(self, name):
- return self.get(name=name)
-
-
- class Group(models.Model):
- """
- Groups are a generic way of categorizing users to apply permissions, or
- some other label, to those users. A user can belong to any number of
- groups.
-
- A user in a group automatically has all the permissions granted to that
- group. For example, if the group 'Site editors' has the permission
- can_edit_home_page, any user in that group will have that permission.
-
- Beyond permissions, groups are a convenient way to categorize users to
- apply some label, or extended functionality, to them. For example, you
- could create a group 'Special users', and you could write code that would
- do special things to those users -- such as giving them access to a
- members-only portion of your site, or sending them members-only email
- messages.
- """
-
- name = models.CharField(_("name"), max_length=150, unique=True)
- permissions = models.ManyToManyField(
- Permission,
- verbose_name=_("permissions"),
- blank=True,
- )
-
- objects = GroupManager()
-
- class Meta:
- verbose_name = _("group")
- verbose_name_plural = _("groups")
-
- def __str__(self):
- return self.name
-
- def natural_key(self):
- return (self.name,)
-
-
- class UserManager(BaseUserManager):
- use_in_migrations = True
-
- def _create_user(self, username, email, password, **extra_fields):
- """
- Create and save a user with the given username, email, and password.
- """
- if not username:
- raise ValueError("The given username must be set")
- email = self.normalize_email(email)
- # Lookup the real model class from the global app registry so this
- # manager method can be used in migrations. This is fine because
- # managers are by definition working on the real model.
- GlobalUserModel = apps.get_model(
- self.model._meta.app_label, self.model._meta.object_name
- )
- username = GlobalUserModel.normalize_username(username)
- user = self.model(username=username, email=email, **extra_fields)
- user.password = make_password(password)
- user.save(using=self._db)
- return user
-
- def create_user(self, username, email=None, password=None, **extra_fields):
- extra_fields.setdefault("is_staff", False)
- extra_fields.setdefault("is_superuser", False)
- return self._create_user(username, email, password, **extra_fields)
-
- def create_superuser(self, username, email=None, password=None, **extra_fields):
- extra_fields.setdefault("is_staff", True)
- extra_fields.setdefault("is_superuser", True)
-
- if extra_fields.get("is_staff") is not True:
- raise ValueError("Superuser must have is_staff=True.")
- if extra_fields.get("is_superuser") is not True:
- raise ValueError("Superuser must have is_superuser=True.")
-
- return self._create_user(username, email, password, **extra_fields)
-
- def with_perm(
- self, perm, is_active=True, include_superusers=True, backend=None, obj=None
- ):
- if backend is None:
- backends = auth._get_backends(return_tuples=True)
- if len(backends) == 1:
- backend, _ = backends[0]
- else:
- raise ValueError(
- "You have multiple authentication backends configured and "
- "therefore must provide the `backend` argument."
- )
- elif not isinstance(backend, str):
- raise TypeError(
- "backend must be a dotted import path string (got %r)." % backend
- )
- else:
- backend = auth.load_backend(backend)
- if hasattr(backend, "with_perm"):
- return backend.with_perm(
- perm,
- is_active=is_active,
- include_superusers=include_superusers,
- obj=obj,
- )
- return self.none()
-
-
- # A few helper functions for common logic between User and AnonymousUser.
- def _user_get_permissions(user, obj, from_name):
- permissions = set()
- name = "get_%s_permissions" % from_name
- for backend in auth.get_backends():
- if hasattr(backend, name):
- permissions.update(getattr(backend, name)(user, obj))
- return permissions
-
-
- def _user_has_perm(user, perm, obj):
- """
- A backend can raise `PermissionDenied` to short-circuit permission checking.
- """
- for backend in auth.get_backends():
- if not hasattr(backend, "has_perm"):
- continue
- try:
- if backend.has_perm(user, perm, obj):
- return True
- except PermissionDenied:
- return False
- return False
-
-
- def _user_has_module_perms(user, app_label):
- """
- A backend can raise `PermissionDenied` to short-circuit permission checking.
- """
- for backend in auth.get_backends():
- if not hasattr(backend, "has_module_perms"):
- continue
- try:
- if backend.has_module_perms(user, app_label):
- return True
- except PermissionDenied:
- return False
- return False
-
-
- class PermissionsMixin(models.Model):
- """
- Add the fields and methods necessary to support the Group and Permission
- models using the ModelBackend.
- """
-
- is_superuser = models.BooleanField(
- _("superuser status"),
- default=False,
- help_text=_(
- "Designates that this user has all permissions without "
- "explicitly assigning them."
- ),
- )
- groups = models.ManyToManyField(
- Group,
- verbose_name=_("groups"),
- blank=True,
- help_text=_(
- "The groups this user belongs to. A user will get all permissions "
- "granted to each of their groups."
- ),
- related_name="user_set",
- related_query_name="user",
- )
- user_permissions = models.ManyToManyField(
- Permission,
- verbose_name=_("user permissions"),
- blank=True,
- help_text=_("Specific permissions for this user."),
- related_name="user_set",
- related_query_name="user",
- )
-
- class Meta:
- abstract = True
-
- def get_user_permissions(self, obj=None):
- """
- Return a list of permission strings that this user has directly.
- Query all available auth backends. If an object is passed in,
- return only permissions matching this object.
- """
- return _user_get_permissions(self, obj, "user")
-
- def get_group_permissions(self, obj=None):
- """
- Return a list of permission strings that this user has through their
- groups. Query all available auth backends. If an object is passed in,
- return only permissions matching this object.
- """
- return _user_get_permissions(self, obj, "group")
-
- def get_all_permissions(self, obj=None):
- return _user_get_permissions(self, obj, "all")
-
- def has_perm(self, perm, obj=None):
- """
- Return True if the user has the specified permission. Query all
- available auth backends, but return immediately if any backend returns
- True. Thus, a user who has permission from a single auth backend is
- assumed to have permission in general. If an object is provided, check
- permissions for that object.
- """
- # Active superusers have all permissions.
- if self.is_active and self.is_superuser:
- return True
-
- # Otherwise we need to check the backends.
- return _user_has_perm(self, perm, obj)
-
- def has_perms(self, perm_list, obj=None):
- """
- Return True if the user has each of the specified permissions. If
- object is passed, check if the user has all required perms for it.
- """
- if not is_iterable(perm_list) or isinstance(perm_list, str):
- raise ValueError("perm_list must be an iterable of permissions.")
- return all(self.has_perm(perm, obj) for perm in perm_list)
-
- def has_module_perms(self, app_label):
- """
- Return True if the user has any permissions in the given app label.
- Use similar logic as has_perm(), above.
- """
- # Active superusers have all permissions.
- if self.is_active and self.is_superuser:
- return True
-
- return _user_has_module_perms(self, app_label)
-
-
- class AbstractUser(AbstractBaseUser, PermissionsMixin):
- """
- An abstract base class implementing a fully featured User model with
- admin-compliant permissions.
-
- Username and password are required. Other fields are optional.
- """
-
- username_validator = UnicodeUsernameValidator()
-
- username = models.CharField(
- _("username"),
- max_length=150,
- unique=True,
- help_text=_(
- "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
- ),
- validators=[username_validator],
- error_messages={
- "unique": _("A user with that username already exists."),
- },
- )
- first_name = models.CharField(_("first name"), max_length=150, blank=True)
- last_name = models.CharField(_("last name"), max_length=150, blank=True)
- email = models.EmailField(_("email address"), blank=True)
- is_staff = models.BooleanField(
- _("staff status"),
- default=False,
- help_text=_("Designates whether the user can log into this admin site."),
- )
- is_active = models.BooleanField(
- _("active"),
- default=True,
- help_text=_(
- "Designates whether this user should be treated as active. "
- "Unselect this instead of deleting accounts."
- ),
- )
- date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
-
- objects = UserManager()
-
- EMAIL_FIELD = "email"
- USERNAME_FIELD = "username"
- REQUIRED_FIELDS = ["email"]
-
- class Meta:
- verbose_name = _("user")
- verbose_name_plural = _("users")
- abstract = True
-
- def clean(self):
- super().clean()
- self.email = self.__class__.objects.normalize_email(self.email)
-
- def get_full_name(self):
- """
- Return the first_name plus the last_name, with a space in between.
- """
- full_name = "%s %s" % (self.first_name, self.last_name)
- return full_name.strip()
-
- def get_short_name(self):
- """Return the short name for the user."""
- return self.first_name
-
- def email_user(self, subject, message, from_email=None, **kwargs):
- """Send an email to this user."""
- send_mail(subject, message, from_email, [self.email], **kwargs)
-
-
- class User(AbstractUser):
- """
- Users within the Django authentication system are represented by this
- model.
-
- Username and password are required. Other fields are optional.
- """
-
- class Meta(AbstractUser.Meta):
- swappable = "AUTH_USER_MODEL"
-
-
- class AnonymousUser:
- id = None
- pk = None
- username = ""
- is_staff = False
- is_active = False
- is_superuser = False
- _groups = EmptyManager(Group)
- _user_permissions = EmptyManager(Permission)
-
- def __str__(self):
- return "AnonymousUser"
-
- def __eq__(self, other):
- return isinstance(other, self.__class__)
-
- def __hash__(self):
- return 1 # instances always return the same hash value
-
- def __int__(self):
- raise TypeError(
- "Cannot cast AnonymousUser to int. Are you trying to use it in place of "
- "User?"
- )
-
- def save(self):
- raise NotImplementedError(
- "Django doesn't provide a DB representation for AnonymousUser."
- )
-
- def delete(self):
- raise NotImplementedError(
- "Django doesn't provide a DB representation for AnonymousUser."
- )
-
- def set_password(self, raw_password):
- raise NotImplementedError(
- "Django doesn't provide a DB representation for AnonymousUser."
- )
-
- def check_password(self, raw_password):
- raise NotImplementedError(
- "Django doesn't provide a DB representation for AnonymousUser."
- )
-
- @property
- def groups(self):
- return self._groups
-
- @property
- def user_permissions(self):
- return self._user_permissions
-
- def get_user_permissions(self, obj=None):
- return _user_get_permissions(self, obj, "user")
-
- def get_group_permissions(self, obj=None):
- return set()
-
- def get_all_permissions(self, obj=None):
- return _user_get_permissions(self, obj, "all")
-
- def has_perm(self, perm, obj=None):
- return _user_has_perm(self, perm, obj=obj)
-
- def has_perms(self, perm_list, obj=None):
- if not is_iterable(perm_list) or isinstance(perm_list, str):
- raise ValueError("perm_list must be an iterable of permissions.")
- return all(self.has_perm(perm, obj) for perm in perm_list)
-
- def has_module_perms(self, module):
- return _user_has_module_perms(self, module)
-
- @property
- def is_anonymous(self):
- return True
-
- @property
- def is_authenticated(self):
- return False
-
- def get_username(self):
- return self.username
|