Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialAnders Prytz
26,193 PointsProblem with access for non superusers?
In the code the following solution is presented for the permission on DELETE request the none super users are not allowe to GET, POST etc after the last fix in the video. The following solution was to ensure that only superusers could delete courses:
class IsSuperUser(permissions.BasePermission):
def has_permission(self, request, view):
if request.user.is_superuser:
return True
else:
if request.method == 'DELETE':
return False
class CourseViewSet(viewsets.ModelViewSet):
permission_classes = (
IsSuperUser,
permissions.DjangoModelPermissions,
)
After this I could not do any GET requests with any other user (with permissions for this). It seems that the IsSuperUser is only running and that there are not fallback to the permissions.DjangoModelPermissions.
I there a good way to gain access for the other such that non superusers could send a GET request with this setup? Should there be more code in the IsSuperUser class for handeling other permissions or is it something I am missing to get the DjangoModelPermissions to work if there are no returns from the IsSuperUser?
2 Answers
Alex Koumparos
Python Development Techdegree Student 36,887 PointsHi Anders,
The way that the REST Framework's permissions work is that if any permissions check evaluates to False, permission will be denied, i.e., all the options are AND
ed together. So given the following tuple (assume the individual permissions objects work like their names would suggest):
permission_classes = (IsSuperUser, permissions.DjangoModelPermissions)
If the user is a superuser, they will pass the first test (its test returns True). Since superusers have all permissions according to DjangoModelPermissions, they will also pass the second test (returns True). Since there has been no test that returned False, the user will pass authorisation and be allowed to perform the action.
If the user is not a superuser, they will fail the first test (its test returns False). Since we have a False, and False AND any value = False, the permissions system will raise an exception and permission will be denied.
Lots of people have a mental model where the tuple should represent a list of 'fallback' options (options are OR
ed together). This is not how the comma separators apply in this system. However, you can use the |
operator to OR together permissions. Consider the following tuple:
permission_classes = (IsSuperUser|permissions.DjangoModelPermissions,)
Now, if the user is a superuser they pass the test. Since we have a True and any value OR True = True, permission is granted.
If the user is not a superuser the IsSuperUser will return False. But, if they do have permissions according to DjangoModelPermissions, then the DjangoModelPermissions test will return True. Since False OR True = True, permission will be granted. Only if the user is neither superuser nor approved according to DjangoModelPermissions will permission be denied.
Now that we know how to compose permissions: comma (or &
) for AND, |
for OR, let's consider what we want the behaviour to be:
- if the method is DELETE, only allow superuser (regardless of whether they are permitted in the DjangoModelPermissions);
- otherwise, apply DjangoModelPermissions
We can express this as:
(IsSuperuser
OR NOT DELETE
) AND DjangoModelPermissions
This becomes the following:
permission_classes = (SuperUserOrNotDelete, permissions.DjangoModelPermissions)
We can then write a permissions class to describe (IsSuperuser
OR NOT DELETE
):
class SuperUserOrNotDelete(permissions.BasePermission):
def has_permission(self, request, view):
if request.method != 'DELETE' or request.user.is_superuser:
return True
return False
Hope that clears everything up,
Cheers
Alex
Steve Wehba
831 PointsThere are several problems with this code. First, if the user is not a superuser and the method is not DELETE, has_permission
will return None
. This will evaluate to False
when tested in a conditional expression. It's also strange that the class is called IsSuperUser
when it clearly does more than test for superuser status — it also tests for the request method. I think the has_permission
method should just test for superuser like this:
def has_permission(self, request, view):
return request.user.is_superuser
If this works, then I'm not sure why the pre-defined IsAdminUser
permission class is not good enough.
If we really do need to check the method, then I suggest renaming the class to something like IsSuperUserOrSafeMethod
, and has_permission
should work like this:
def has_permission(self, request, view):
return request.user.is_useruser or request in self.SAFE_METHODS
Hope this helps.
peterj
8,746 Pointspeterj
8,746 PointsI'm also having the same issue! Would love to know of a fix that would allow non-superusers with valid token to send a GET request (among other things they are permissioned to do) .