0

I created a custom user model using AbstractBaseUser class. Code for the same is here.

class UserModel(AbstractBaseUser):
    user_type_choices = (
                            (constants.USER_TYPE_ADMIN, 'Admin'),
                            (constants.USER_TYPE_INSTITUTE, 'Institute'),
                            (constants.USER_TYPE_STUDENT, 'Student')
                        )
    sys_id = models.AutoField(primary_key=True, blank=True)        
    name = models.CharField(max_length=127, null=False, blank=False)    
    email = models.EmailField(max_length=127, unique=True, null=False, blank=False)
    mobile = models.CharField(max_length=10, unique=True, null=False, blank=False)  
    user_type = models.PositiveSmallIntegerField(choices=user_type_choices, null=False, blank=True, help_text="Admin(1)/Institute(2)/Student(3)")  
    access_valid_start = models.DateTimeField(null=True, blank=True)
    access_valid_end = models.DateTimeField(null=True, blank=True)
    created_when = models.DateTimeField(null=True, blank=True )
    created_by = models.BigIntegerField(null=True, blank=True)
    last_updated_when = models.DateTimeField(null=True, blank=True)
    last_updated_by = models.BigIntegerField(null=True, blank=True)
    notes = models.CharField(max_length=2048, null=True, blank=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=True)

    objects = MyUserManager()

    USERNAME_FIELD = "email"
    # REQUIRED_FIELDS must contain all required fields on your User model, 
    # but should not contain the USERNAME_FIELD or password as these fields will always be prompted for.
    REQUIRED_FIELDS = ['name', 'mobile', 'user_type']

    class Meta:
        app_label = "accounts"
        db_table = "users"

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.name

    def get_short_name(self):
        return self.name

    def is_access_valid(self):
        if self.access_valid_end > utility.now():
            return True
        else:
            return False


    def save(self, *args, **kwargs):    
        if not self.sys_id:
            self.created_when = utility.now()
        self.last_updated_when = utility.now()
        return super(UserModel, self).save(*args, **kwargs)

Code for its manager is as below.

class MyUserManager(BaseUserManager):
    use_in_migrations = True
    def create_user(self, email, name, mobile, user_type, password):      
        return create_superuser(self, email, name, mobile, user_type, password)

    # python manage.py createsuperuser
    def create_superuser(self, email, name, mobile, user_type, password):
        user = self.model(
                          email = email,
                          name = name,
                          mobile = mobile,
                          user_type = user_type,
                          access_valid_start = utility.now(),
                          access_valid_end = utility.get_access_end_date(),
                          created_when = utility.now(),
                          created_by = constants.COMMAND_LINE_USER_ID, 
                          last_updated_when = utility.now(),
                          last_updated_by = constants.COMMAND_LINE_USER_ID,
                          notes = 'This user is created from command line. createsuperuser utility.'
                          )
        user.set_password(password)
        user.save(using=self._db)
        return user

I also created the Authentication Backend.

class MyAuthBackend(object):
    def authenticate(self, email, password):
        try:
            user = UserModel.objects.get(email=email)
            if user.check_password(password):
                return user
            else:
                return None
        except UserModel.DoesNotExist:
            logging.getLogger("error_logger").error("user with login %s does not exists " % login)
            return None
        except Exception as e:
            logging.getLogger("error_logger").error("user with login %s does not exists " % login)
            return None

    def get_user(self, user_id):
        # user_id must be the primary key of table.
        try:
            user = UserModel.objects.get(sys_id=user_id)
            if user.is_active:
                return user
            return None
        except UserModel.DoesNotExist:
            logging.getLogger("error_logger").error("user with %(user_id)d not found")
            return None

I have included custom user model and backend, both in settings file.

AUTH_USER_MODEL = 'accounts.UserModel'
AUTHENTICATION_BACKENDS = ('accounts.backends.MyAuthBackend',)

I created a superuser from command line but when I tried to login from admin login url, following error was thrown.

Please enter the correct email and password for a staff account. Note that both fields may be case-sensitive.

As suggest in this SO answer, I used fallback authentication backend and then it started throwing this error : 'UserModel' object has no attribute 'has_module_perms'. Which means fallback backend worked here. But it required to add below functions in custom user model.

def has_perm(self, perm, obj=None):
        return self.is_superuser

    # this methods are require to login super user from admin panel
    def has_module_perms(self, app_label):
        return self.is_superuser

and added is_superuser field as well. Now it is working fine.

So I have below questions :

  1. Why I am not able to authenticate user using custom backend when trying to login from admin panel?
  2. why function has_perm, is_staff and is_superuser fields are mandatory for login from admin panel?
  3. Why has_perm function , is_staff and is_superuser fields are not required when logging in from my own login form?
Community
  • 1
  • 1
Anurag Rana
  • 1,429
  • 2
  • 24
  • 48

1 Answers1

0
  1. You did authenticate but you are not authorized. Django Admin is using is_staff flag to authorize you in as stated in documentation

  2. They are part of Django simple permission system which is by default used by Django admin as stated in documentation

  3. You are not using permissions system otherwise they would be required as they are part of permission system.

iklinac
  • 14,944
  • 4
  • 28
  • 30