0

I am trying to create an api endpoint that lists all the users in the database, but I only want staff members to be able to do this. By default when a secretary is created, the is_staff field in the users model is set to True. In the permission class, I am checking to see if this field is set to true and grant permissions accordingly.
Since I have many different types of users with slightly different attributes, I created a concrete base user model that is inherited by other user models. The SECRETARY role is assigned by the serializer when handling the secretary creation endpoint request.



class User(AbstractBaseUser):

    class Roles(models.TextChoices):
        """define the user roles"""

        ADMIN = "ADMIN", "Admin"
        PRINTER = "PRINTER", "Printer"
        SECRETARY = "SECRETARY", "Secretary"
        PHOTOGRAPHER = "PHOTOGRAPHER", "Photographer"
        EDITOR = "EDITOR", "Editor"
        CLIENT = "CLIENT", "Client"
        SYSTEM = "SYSTEM", "System"
        APPLICANT = "APPLICANT", "Applicant"

    id = models.CharField(
        max_length=8, unique=True, primary_key=True, default=uidgen, editable=False
    )
    first_name = models.CharField("First Name", max_length=30, null=False)
    last_name = models.CharField("Last Name", max_length=30, null=True, blank=True)
    email = models.EmailField(
        verbose_name="Email", max_length=255, unique=True, null=False
    )
    username = None
    phone_number = models.CharField(
        "Phone Number", max_length=20, null=False, unique=True
    )
    dob = models.DateField(verbose_name="Date Of Birth", blank=True, null=True)
    role = models.CharField(max_length=50, default=Roles.CLIENT)
    date_joined = models.DateTimeField(verbose_name="Date Joined", auto_now_add=True)
    last_login = models.DateTimeField(verbose_name="Last Login", auto_now=True)
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["phone_number"]
    is_staff = False
    is_superuser = False
    is_active = True
    is_contractor = False
    is_associate = False
    phone_is_verified = False
    email_is_verified = False

    objects = UserManager()

User manager:



class UserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError("The Email must be set")
        user = self.model(
            email=self.normalize_email(email),
        )
        if password:
            user.set_password(password)
        try:
            if user.role == User.Roles.SECRETARY:
                extra_fields.setdefault("is_staff", True)
            else:
                extra_fields.setdefault("is_staff", False)
        except AttributeError:
            user.role = User.Roles.ADMIN
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        extra_fields.setdefault("is_active", True)
        admin_user = self.create_user(email, password, **extra_fields)
        admin_user.role = User.Roles.ADMIN
        admin_user.save()
        return admin_user

The Secretary model:


class Secretary(User):
    bank_number = models.CharField(max_length=50, null=True)
    is_staff = True

    class Meta:
        verbose_name = "Secretary"
        verbose_name_plural = "Secretaries"

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

    objects = UserManager()

Permission class:


class UserListPermission(permissions.BasePermission):
    def has_permission(self, request, view):
        print(request.user.is_staff)
        if view.action == "list" and request.user.is_staff:
            return True
        return False

    def has_object_permission(self, request, view):
        if view.action == "list" and request.user.is_staff:
            return True
        return False

I created a secretary along with an access token and tried accessing a staff only api endpoint. I get a response:

{
    "detail": "You do not have permission to perform this action."
}

In the server shell output, the print statement spits out False. In the django shell:

>>> sec = Secretary.objects.get(email='dinoseca@yahoo.de')
>>> sec.role
'SECRETARY'
>>> sec.is_staff
True

When I remove is_staff = True from the Secretary model, the above shell snippet results in False

I feel like the mistake lies somewhere in my UserManager or my User model.

keystroke33
  • 159
  • 3
  • 11

1 Answers1

0

You have to save your user before returning in create_user method. self.model just creates an instance the save method must be called for the instance to be saved to the database.

def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError("The Email must be set")
        user = self.model(
            email=self.normalize_email(email),
        )
        if password:
            user.set_password(password)
        try:
            user.is_staff = True if user.role == User.Roles.SECRETARY else False
        except AttributeError:
            user.role = User.Roles.ADMIN
        user.save(using=self._db)
        return user

This might also help to clarify https://stackoverflow.com/a/51163172/11128969

kidus solomon
  • 23
  • 1
  • 7
  • this doesn't solve the problem. I forgot to mention in the post that I have tried adding `user.save()` before returning the user. – keystroke33 Dec 30 '21 at 11:57
  • Btw why you make the boolean fields as just a property on the model class ? you have to persist the values in the database. you are just getting the property you set statically on the class. so change the fields to a boolean field like this `is_staff = models.BooleanField(default=False)` – kidus solomon Dec 30 '21 at 12:14