00:00 In the previous lesson, I gave a quick review of the Django authentication module. In this lesson, I’m going to use the accounts created in the previous lesson to show you how DRF permissions work. In case you skipped the previous lesson, I’ll just quickly run through what I did.
This will be where I demonstrate the permissions. If you’re writing the code yourself while you’re following along, you’re going to need to change the
settings file to add
books to the
INSTALLED_APPS, change the
urls file to include
books.urls, and then create the files that I’m going to show you for your
urls. Like before, you’ll need to create your own
admin file or pick one from the supplementing materials if you wish to use the admin to create your data. You’ll need to run
migrate as necessary on the models, and then use the admin or a
loaddata command to get data in so that you can play with the REST content.
Here’s the model file for my newly created
books app. I’m creating an ORM class called
Book that has two fields,
title, the title of the book, and whether or not the book is restricted, which is a Boolean.
And now here’s the
views file. It’s a little longer than things from before. First off, let me scroll down a little bit to show you the home view, the
library() method. This method’s pretty simple.
04:14 Let me visit that. And here’s the result, the listing of books. Going back to the Library page, logging out, and then going straight to the books listing, and you’ll notice what comes back from the REST API is a permission denied.
Here I am as
indy, visiting the Books API. Uh oh. That’s not quite what I had planned. It turns out that DRF’s
IsAdminUser really means is staff, because the
admin account is both a superuser and staff it’s allowed in.
It has one method which is
.has_permission(), and inside of that, I’m going to look at the
request object, the
.user who’s associated with the
request object, and I’m going to check the
.is_superuser property and return that as the value for
.has_permission(). Now, if the user is a superuser, they’ll be allowed in, and if they’re not, they won’t. So in this case,
indy, who’s only staff, should be denied.
What I’m going to do here is check whether or not the object itself, which in this case is a
Book, is restricted. This is happening on line 17. If the
Book does not have the
.restricted flag, then anyone is able to see this, because I’m always returning
If it is,
indy is allowed to see the restricted books. Everyone else is not. The other change that I have to make is inside of
IsSuperUser, because I’m going to combine these two permissions. By default, inside of
BasePermission, permission is granted.
So if I chain
IsSuperUser and I don’t change the
IsSuperUser, it will always be granted, which is something you have to be very careful with. Personally, if I were designing the framework, I would make permission denied be the default rather than permission accepted, so you have to be very, very cautious when you play with this stuff inside of the DRF.
I have already shot myself in the foot several times with this particular feature, accidentally opening up permissions when I didn’t mean to. Let me show you the change. Defining
.has_object_permission() inside of
And setting the return value the same as
.has_permission(). So this makes sure that if the permission’s being checked at the global level—at the request level—you check whether or not it is superuser, as well as at the object level.
And because I’m logged in as
marion, I now get permission denied. So it’s working, just not the way you might expect. You’ve seen that the
.permission_classes only affects whether or not you can actually touch the object specifically—it doesn’t affect the listing.
11:41 This is a little counter-intuitive and unfortunately allows you to accidentally expose things through your API that you may not have intended. In order to have the behavior that I want here so that restricted books don’t show up in the listing, I have to modify not just the permission classes, but also the queryset.
Inside of the queryset, you can get at the
.request object and you can decide who the request is for. In this case, I’m checking whether or not the user is staff. If the user is staff, then all books go back no matter what, essentially ignoring the
restricted field. Otherwise—so, users who are not staff or not logged in—you will get a filtered version of the queryset, and that filter only shows the non-restricted books.
12:56 You’ve learned how to use permissions. More importantly, you’ve learned how to be very careful with permissions. First off, default permissions allow entry rather than deny them. That can be a problem if you mess things up. Secondly, permissions don’t apply filters, so if you want to make sure that only things with permissions show up inside of your listings, you have to do more than just set the permission.
13:19 You also have to change your queryset. There’s a lot of power here, and I always hesitate when I criticize somebody who’s written open-source software because more power to them, we’re all using it, and that’s fantastic. That being said, you’ve got to be a little careful here or you’ll end up with some toes missing.
Become a Member to join the conversation.