mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #24564 -- Moved AbstractBaseUser and BaseUserManager so they can be used without auth in INSTALLED_APPS
This commit is contained in:
		
							
								
								
									
										114
									
								
								django/contrib/auth/base_user.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								django/contrib/auth/base_user.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| """ | ||||
| This module allows importing AbstractBaseUser even when django.contrib.auth is | ||||
| not in INSTALLED_APPS. | ||||
| """ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from django.contrib.auth.hashers import ( | ||||
|     check_password, is_password_usable, make_password, | ||||
| ) | ||||
| from django.db import models | ||||
| from django.utils.crypto import get_random_string, salted_hmac | ||||
| from django.utils.encoding import python_2_unicode_compatible | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
|  | ||||
|  | ||||
| class BaseUserManager(models.Manager): | ||||
|  | ||||
|     @classmethod | ||||
|     def normalize_email(cls, email): | ||||
|         """ | ||||
|         Normalize the email address by lowercasing the domain part of the it. | ||||
|         """ | ||||
|         email = email or '' | ||||
|         try: | ||||
|             email_name, domain_part = email.strip().rsplit('@', 1) | ||||
|         except ValueError: | ||||
|             pass | ||||
|         else: | ||||
|             email = '@'.join([email_name, domain_part.lower()]) | ||||
|         return email | ||||
|  | ||||
|     def make_random_password(self, length=10, | ||||
|                              allowed_chars='abcdefghjkmnpqrstuvwxyz' | ||||
|                                            'ABCDEFGHJKLMNPQRSTUVWXYZ' | ||||
|                                            '23456789'): | ||||
|         """ | ||||
|         Generate a random password with the given length and given | ||||
|         allowed_chars. The default value of allowed_chars does not have "I" or | ||||
|         "O" or letters and digits that look similar -- just to avoid confusion. | ||||
|         """ | ||||
|         return get_random_string(length, allowed_chars) | ||||
|  | ||||
|     def get_by_natural_key(self, username): | ||||
|         return self.get(**{self.model.USERNAME_FIELD: username}) | ||||
|  | ||||
|  | ||||
| @python_2_unicode_compatible | ||||
| class AbstractBaseUser(models.Model): | ||||
|     password = models.CharField(_('password'), max_length=128) | ||||
|     last_login = models.DateTimeField(_('last login'), blank=True, null=True) | ||||
|  | ||||
|     is_active = True | ||||
|  | ||||
|     REQUIRED_FIELDS = [] | ||||
|  | ||||
|     class Meta: | ||||
|         abstract = True | ||||
|  | ||||
|     def get_username(self): | ||||
|         "Return the identifying username for this User" | ||||
|         return getattr(self, self.USERNAME_FIELD) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.get_username() | ||||
|  | ||||
|     def natural_key(self): | ||||
|         return (self.get_username(),) | ||||
|  | ||||
|     def is_anonymous(self): | ||||
|         """ | ||||
|         Always return False. This is a way of comparing User objects to | ||||
|         anonymous users. | ||||
|         """ | ||||
|         return False | ||||
|  | ||||
|     def is_authenticated(self): | ||||
|         """ | ||||
|         Always return True. This is a way to tell if the user has been | ||||
|         authenticated in templates. | ||||
|         """ | ||||
|         return True | ||||
|  | ||||
|     def set_password(self, raw_password): | ||||
|         self.password = make_password(raw_password) | ||||
|  | ||||
|     def check_password(self, raw_password): | ||||
|         """ | ||||
|         Return a boolean of whether the raw_password was correct. Handles | ||||
|         hashing formats behind the scenes. | ||||
|         """ | ||||
|         def setter(raw_password): | ||||
|             self.set_password(raw_password) | ||||
|             self.save(update_fields=["password"]) | ||||
|         return check_password(raw_password, self.password, setter) | ||||
|  | ||||
|     def set_unusable_password(self): | ||||
|         # Set a value that will never be a valid hash | ||||
|         self.password = make_password(None) | ||||
|  | ||||
|     def has_usable_password(self): | ||||
|         return is_password_usable(self.password) | ||||
|  | ||||
|     def get_full_name(self): | ||||
|         raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method') | ||||
|  | ||||
|     def get_short_name(self): | ||||
|         raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.') | ||||
|  | ||||
|     def get_session_auth_hash(self): | ||||
|         """ | ||||
|         Return an HMAC of the password field. | ||||
|         """ | ||||
|         key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" | ||||
|         return salted_hmac(key_salt, self.password).hexdigest() | ||||
| @@ -1,9 +1,7 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from django.contrib import auth | ||||
| from django.contrib.auth.hashers import ( | ||||
|     check_password, is_password_usable, make_password, | ||||
| ) | ||||
| from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager | ||||
| from django.contrib.auth.signals import user_logged_in | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.core import validators | ||||
| @@ -12,7 +10,6 @@ from django.core.mail import send_mail | ||||
| from django.db import models | ||||
| from django.db.models.manager import EmptyManager | ||||
| from django.utils import six, timezone | ||||
| from django.utils.crypto import get_random_string, salted_hmac | ||||
| from django.utils.encoding import python_2_unicode_compatible | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
|  | ||||
| @@ -129,39 +126,6 @@ class Group(models.Model): | ||||
|         return (self.name,) | ||||
|  | ||||
|  | ||||
| class BaseUserManager(models.Manager): | ||||
|  | ||||
|     @classmethod | ||||
|     def normalize_email(cls, email): | ||||
|         """ | ||||
|         Normalize the address by lowercasing the domain part of the email | ||||
|         address. | ||||
|         """ | ||||
|         email = email or '' | ||||
|         try: | ||||
|             email_name, domain_part = email.strip().rsplit('@', 1) | ||||
|         except ValueError: | ||||
|             pass | ||||
|         else: | ||||
|             email = '@'.join([email_name, domain_part.lower()]) | ||||
|         return email | ||||
|  | ||||
|     def make_random_password(self, length=10, | ||||
|                              allowed_chars='abcdefghjkmnpqrstuvwxyz' | ||||
|                                            'ABCDEFGHJKLMNPQRSTUVWXYZ' | ||||
|                                            '23456789'): | ||||
|         """ | ||||
|         Generates a random password with the given length and given | ||||
|         allowed_chars. Note that the default value of allowed_chars does not | ||||
|         have "I" or "O" or letters and digits that look similar -- just to | ||||
|         avoid confusion. | ||||
|         """ | ||||
|         return get_random_string(length, allowed_chars) | ||||
|  | ||||
|     def get_by_natural_key(self, username): | ||||
|         return self.get(**{self.model.USERNAME_FIELD: username}) | ||||
|  | ||||
|  | ||||
| class UserManager(BaseUserManager): | ||||
|     use_in_migrations = True | ||||
|  | ||||
| @@ -189,76 +153,6 @@ class UserManager(BaseUserManager): | ||||
|                                  **extra_fields) | ||||
|  | ||||
|  | ||||
| @python_2_unicode_compatible | ||||
| class AbstractBaseUser(models.Model): | ||||
|     password = models.CharField(_('password'), max_length=128) | ||||
|     last_login = models.DateTimeField(_('last login'), blank=True, null=True) | ||||
|  | ||||
|     is_active = True | ||||
|  | ||||
|     REQUIRED_FIELDS = [] | ||||
|  | ||||
|     class Meta: | ||||
|         abstract = True | ||||
|  | ||||
|     def get_username(self): | ||||
|         "Return the identifying username for this User" | ||||
|         return getattr(self, self.USERNAME_FIELD) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.get_username() | ||||
|  | ||||
|     def natural_key(self): | ||||
|         return (self.get_username(),) | ||||
|  | ||||
|     def is_anonymous(self): | ||||
|         """ | ||||
|         Always returns False. This is a way of comparing User objects to | ||||
|         anonymous users. | ||||
|         """ | ||||
|         return False | ||||
|  | ||||
|     def is_authenticated(self): | ||||
|         """ | ||||
|         Always return True. This is a way to tell if the user has been | ||||
|         authenticated in templates. | ||||
|         """ | ||||
|         return True | ||||
|  | ||||
|     def set_password(self, raw_password): | ||||
|         self.password = make_password(raw_password) | ||||
|  | ||||
|     def check_password(self, raw_password): | ||||
|         """ | ||||
|         Returns a boolean of whether the raw_password was correct. Handles | ||||
|         hashing formats behind the scenes. | ||||
|         """ | ||||
|         def setter(raw_password): | ||||
|             self.set_password(raw_password) | ||||
|             self.save(update_fields=["password"]) | ||||
|         return check_password(raw_password, self.password, setter) | ||||
|  | ||||
|     def set_unusable_password(self): | ||||
|         # Sets a value that will never be a valid hash | ||||
|         self.password = make_password(None) | ||||
|  | ||||
|     def has_usable_password(self): | ||||
|         return is_password_usable(self.password) | ||||
|  | ||||
|     def get_full_name(self): | ||||
|         raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method') | ||||
|  | ||||
|     def get_short_name(self): | ||||
|         raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.') | ||||
|  | ||||
|     def get_session_auth_hash(self): | ||||
|         """ | ||||
|         Returns an HMAC of the password field. | ||||
|         """ | ||||
|         key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" | ||||
|         return salted_hmac(key_salt, self.password).hexdigest() | ||||
|  | ||||
|  | ||||
| # A few helper functions for common logic between User and AnonymousUser. | ||||
| def _user_get_all_permissions(user, obj): | ||||
|     permissions = set() | ||||
|   | ||||
| @@ -59,6 +59,12 @@ Minor features | ||||
| * The ``BCryptSHA256PasswordHasher`` will now update passwords if its | ||||
|   ``rounds`` attribute is changed. | ||||
|  | ||||
| * ``AbstractBaseUser`` and ``BaseUserManager`` were moved to a new | ||||
|   ``django.contrib.auth.base_user`` module so that they can be imported without | ||||
|   including ``django.contrib.auth`` in :setting:`INSTALLED_APPS` (this raised | ||||
|   a deprecation warning in older versions and is no longer supported in | ||||
|   Django 1.9). | ||||
|  | ||||
| :mod:`django.contrib.gis` | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
|   | ||||
| @@ -586,6 +586,16 @@ password resets. You must then provide some key implementation details: | ||||
|         identifies the user in an informal way. It may also return the same | ||||
|         value as :meth:`django.contrib.auth.models.User.get_full_name()`. | ||||
|  | ||||
|     .. admonition:: Importing ``AbstractBaseUser`` | ||||
|  | ||||
|         .. versionadded:: 1.9 | ||||
|  | ||||
|         ``AbstractBaseUser`` and ``BaseUserManager`` are importable from | ||||
|         ``django.contrib.auth.base_user`` so that they can be imported without | ||||
|         including ``django.contrib.auth`` in :setting:`INSTALLED_APPS` (this | ||||
|         raised a deprecation warning in older versions and is no longer | ||||
|         supported in Django 1.9). | ||||
|  | ||||
| The following methods are available on any subclass of | ||||
| :class:`~django.contrib.auth.models.AbstractBaseUser`: | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user