I have a permissions.py file that holds a custom permission called CanViewUserRecord. I have assigned this to a viewset called UserRecordView. The permission isn't working though, whenever I call the endpoint attached to the viewset all of the data from the database is returned. Here is the custom permission code:
from accounts.models import Staff
class CanViewUserRecord(permissions.BasePermission):
edit_methods = ("PUT", "PATCH")
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
if obj.account_type == "staff":
# If user is the manager of staff
if obj.staff.manager == request.user.id:
return True
# If the user is the owner
if obj == request.user:
return True
# Allow admins to access
if request.user.account_type == "admin":
return True
return False
elif obj.account_type == "manager":
# If the user is the owner
if obj == request.user:
return True
# Allow Admins to access
if request.user.account_type == "admin":
return True
return False
elif obj.account_type == "admin":
# If the user is the owner
if obj == request.user:
return True
# Allow Admins to access
if request.user.account_type == "admin":
return True
return False
else:
return False
elif request.method in self.edit_methods:
if obj.account_type == "staff":
if (
request.user.account_type == "manager"
or request.user.account_type == "admin"
):
return True
else:
return False
elif obj.account_type == "manager":
return True
else:
return False
return False
The idea is to limit what users can see depending on the account_type of the user assigned to the token supplied to the API endpoint. Here is my viewset code:
class UserRecordView(views.APIView):
"""
API View to create or get a list of all the registered
users. GET request returns the registered users whereas
a POST request allows to create a new user.
"""
permission_classes = [CanViewUserRecord, permissions.IsAuthenticated]
def get(self, format=None):
users = Account.objects.all()
serializer = AccountSerializer(users, many=True)
return response.Response(serializer.data)
def post(self, request):
serializer = AccountSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.save()
return response.Response(serializer.data, status=status.HTTP_201_CREATED)
return response.Response(
{
"error": True,
"error_msg": serializer.error_messages,
},
status=status.HTTP_400_BAD_REQUEST,
)
So if I make a request with a token that is assigned to a user who is a "manager" I don't want them to be able to see records for a user who has the account_type "admin". However, I did a test where the permission just returned False and nothing else and the API request still returns data even when the permission is set to False. Any ideas on what I'm doing wrong? Here's the model for field reference:
class Account(auth_models.AbstractBaseUser):
username = models.CharField(max_length=40, unique=True, primary_key=True)
ACCOUNT_TYPE_CHOICES = (
("manager", "Manager"),
("staff", "Staff"),
("admin", "Admin"),
)
account_type = models.CharField(
choices=ACCOUNT_TYPE_CHOICES,
default="staff",
blank=False,
null=False,
max_length=10,
)
date_joined = models.DateTimeField(verbose_name="Date Joined",
auto_now_add=True)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["account_type"]
objects = AccountManager()
def __str__(self):
return self.username
def has_module_perms(self, app_label):
return True
class Staff(models.Model):
user = models.OneToOneField(Account, on_delete=models.CASCADE, primary_key=True)
first_name = models.CharField(max_length=50, null=False, blank=False)
last_name = models.CharField(max_length=50, null=False, blank=False)
date_of_birth = models.DateField(null=False, blank=False)
manager = models.ForeignKey(Manager, on_delete=models.CASCADE)
employment_start_date = models.DateField(null=False, blank=True)
employment_end_date = models.DateField(blank=True)
role = models.ForeignKey(
Role, on_delete=models.CASCADE, related_name="assigned_staff"
)
REQUIRED_FIELDS = [
"user",
"first_name",
"last_name",
"date_of_birth",
"manager",
"employment_start_date",
"role",
]
EDIT: I've added the has_permission function into the permission as suggested by JPG in the comments. The data that is being returned is still incorrect and is giving me to access it shouldn't have permission to. Here is the updated code:
class CanViewUserRecord(permissions.BasePermission):
edit_methods = ("PUT", "PATCH")
message = None
def has_permission(self, request, view):
if request.user.is_authenticated:
return True
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
if obj.account_type == "employee":
# If user is the employer
if obj.employee.employer == request.user.id:
return True
# If the user is the owner
if obj == request.user:
return True
# Allow Jool Admins to access
if request.user.account_type == "jool-admin":
return True
self.message = "Requestor does not have access to this employee record"
return False
elif obj.account_type == "provider":
# If the user is the owner
if obj == request.user:
return True
# Allow Jool Admins to access
if request.user.account_type == "jool-admin":
return True
self.message = "Requestor cannot view provider records"
return False
elif obj.account_type == "jool-admin":
# If the user is the owner
if obj == request.user:
return True
# Allow Jool Admins to access
if request.user.account_type == "jool-admin":
return True
self.message = "Requestor cannot view admin records"
return False
else:
return False
elif request.method in self.edit_methods:
if obj.account_type == "employee":
if (
request.user.account_type == "provider"
or request.user.account_type == "jool-admin"
):
return True
else:
return False
elif obj.account_type == "provider":
return True
else:
return False
return False