toothie
toothie

Reputation: 1029

django authenticate() for custom user model

Below is my custom user model:

 class CUserManager(BaseUserManager):

        def _create_user(self, email, first_name, password,
                         is_staff, is_superuser, **extra_fields):
            """
        Creates and saves a User with the given email and password.
        """
        now = timezone.now()
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email,
                          first_name = first_name,
                          is_staff=is_staff, is_active=False,
                          is_superuser=is_superuser, last_login=now,
                          date_joined=now, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, first_name, password=None, **extra_fields):
        return self._create_user(email, first_name, password, False, False,
                                 **extra_fields)

    def create_superuser(self, email, first_name, password, **extra_fields):
        return self._create_user(email, first_name, password, True, True,
                                 **extra_fields)


class CUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(_('email address'), max_length=254, unique=True)
    first_name = models.CharField(_('first name'), max_length=30)
    last_name = models.CharField(_('last name'), max_length=30, 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=False,
        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)
    last_updated = models.DateTimeField(_('last updated'), default=timezone.now)

    objects = CUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name']

It creates a new user correctly. But when I try to authenticate the user from shell or from views, the authenticate() function doesn't work for users having is_active=False.

>>> from django.contrib.auth import get_user_model, auhtenticate
>>> u = get_user_model()
>>> authenticate(username='[email protected]', password='abc)

The above line returns nothing if the user is inactive but returns the user object otherwise. I don't understand why its returning nothing for inactive users.

Upvotes: 3

Views: 7657

Answers (3)

Bruce Wayne
Bruce Wayne

Reputation: 94

There are standard methods of doing it by customizing the auth backend. Some of them feel too cumbersome for simple applications that are to be done within deadlines. Here is a customized function that addresses this issue:

Code

from django.contrib.auth.hashers import check_password

from users.models import CustomUser


def authenticate(username=None, password=None):
    try:
        # Get the corresponding user.
        user = CustomUser.objects.get(username=username)
        #  If password, matches just return the user. Otherwise, return None.
        if check_password(password, user.password):
            return user
        return None
    except CustomUser.DoesNotExist:
        # No user was found.
        return None

Explanation

  • This returns a User object if the given user with matching username and password is found.
  • The username parameter can be switched with any other parameter as well - like email, et cetera, whatever the logic needs.
  • Custom Model Managers are not needed here.

Where and How to Use?

I have a special helper.py file within each app to have such functions or classes. So that's where this goes. Just import it and use authenticate() the usual way, except this would now work on the CustomUser model rather than the User model.

Upvotes: 0

user5662309
user5662309

Reputation:

Hi You can write Custom backend for this problem.

from django.contrib.auth.hashers import check_password 

from django.contrib.auth.models import User

from apps.staffs.models import Staff(Custom User)


class StaffBackend:

    # Create an authentication method
    # This is called by the standard Django login procedure
    def authenticate(self, username=None, password=None):

        try:
            # Try to find a user matching your username
            user = Staff.objects.get(username=username)

            #  Check the password is the reverse of the username
            if check_password(password, user.password):
                # Yes? return the Django user object
                return user
            else:
                # No? return None - triggers default login failed
                return None
        except Staff.DoesNotExist:
            # No user was found, return None - triggers default login failed
            return None

    # Required for your backend to work properly - unchanged in most scenarios
    def get_user(self, user_id):
        try:
            return Staff.objects.get(pk=user_id)
        except Staff.DoesNotExist:
            return None

Upvotes: 0

Sardorbek Imomaliev
Sardorbek Imomaliev

Reputation: 15390

It is happening because of how django's authentication works. By default it uses ModelBackend which checks for is_active https://docs.djangoproject.com/en/1.10/ref/contrib/auth/#django.contrib.auth.backends.ModelBackend.get_user_permissions

So you can create custom authentication backend which will ignore this option https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#writing-an-authentication-backend

Upvotes: 2

Related Questions