3.5 Using Policies to Control Abilities
Not every user will have the ability to edit and delete pages. We'll use Laravel's Policy feature to determine who can and can't perform certain tasks.
1.Introduction1 lesson, 01:23
2.Getting Started4 lessons, 46:41
3.Managing Pages6 lessons, 1:12:31
4.User Management2 lessons, 27:37
5.Managing the Blog4 lessons, 41:51
6.Adding Extras2 lessons, 26:07
7.Implementing the Front-End3 lessons, 30:24
8.Homework Review1 lesson, 07:11
9.Conclusion1 lesson, 01:24
3.5 Using Policies to Control Abilities
In the previous lesson, we ended with a working pages controller. We just need to implement security, and we also need to add the ability to delete pages. Now as far as deleting pages is concerned, that is homework. I want you to think about that and implement that on your own. At the end of this course, there will be a lesson that goes over how I implemented that functionality. Now, let me go ahead and say that whether yours matches mine, it doesn't matter, there is no correct or incorrect approach. We are different people, we will probably approach the problem differently. But I want you to see how I implemented that. But here's a hint, in order to delete something, you have to issue a Post request, so just keep that in mind. Okay, so we want to secure our pages controller. Now, I am not logged in and we'll go to admin pages. As an unauthenticated user, I should not be able to access this. I should not be able to click on any one of these and if we edited this then it would be edited. Now as far as creating, there would be a slight little problem because we are not logged in, there is no user to specify as the author. But other than that, this is completely unprotected. So what we want to do is, first of all, protect this. Now we already have the code to do that. If you'll remember we wrote some middleware that we used, whenever we defined the admin route, we called it admin. So we can approach this in a couple of different ways. We could also use the middleware here, after calling resource, we could just paste in that call to middleware and admin. If we refresh, hello, we have to login, so that's one approach. Another approach is to do it inside of the controller, so let's do that. Let's go to app, admin pages controller and we're going to add a constructor. So public function_construct(), and we will essentially do the same thing. We will say this, and then we will call middleware and admin. Once again, it's going to be protected, it's going to send us to the login page. So let's login, but let's log in as Joe, because we haven't written anything as Joe. So Joe, is just an author, he's not an admin or an editor. And so, we wants to implement at least some granularity here, so that if Joe didn't write the page, then he doesn't have access to that page. So that means he won't have access to see it, he won't have access to edit it as well. But there's a problem here, so let's see what that is I need the semicolon there, and there we go. Okay, so the first thing that we can do is filter the data that he sees in the index view. So let's go ahead and do that, so as far as the admin and editor they will always be able to see everything. So we could do this, if (Auth::user() user hasAnyRole) and then we specify the roles. So that would be, admin and editor, then they would have the ability to see everything. So that would be that, otherwise, we just get the pages from the user. So we will say Auth::user() pages() get(); and then if we go back refresh, we just see Joe's page. Now I created this off screen, so you could go and create a new one. This could be Joe's, page 2, URL would be Joe's page 2, and this is Joe's page 2, okay, Submit. So we are only seeing Joe's stuff that's great, however, let's do this, we'll say pages /1/edit. And hello, we have access to one of the admins pages. And if we edited this, it would edit, so we want to protect this as well. So, first of all, I know that we are going to be doing this check at least one other time. So it kind of makes sense to put this functionality inside of a helper method in our user model, so let's do that. Let's go and let's add another method, public function isAdminOrEditor(). And we will simply return this and we will call hasAnyRole([ 'admin', 'editor']) and there we go. So let's make that quick little change, and then we will talk about securing our other actions. So we could do this in a similar way. We can add checks here. Now as far as creating any user will be able to create, so we're not going to worry about protecting that. The same is true for storing. But when it comes to editing, we could come in here and we could say, if (Auth::user() isAdminOrEditor), bla, bla, bla, yeah, we could do that. Or we could write something that's called a policy. A policy is a way of authorizing a user for a particular model, basically it's what it is. So, what we're going to do is go to the command line and we're going to say php artisan make:policy. And we're going to call this a PagePolicy and we also get specify the model as well, we don't have to. But since, we're going to be working with pages in this case, we want to specify our model here. So we will create that. And if we go to app, and then policies, we now have this policy class. Now, there are several methods that are given to us, but we can also define our own. These are just here to give us something to start with. Now, I'll tell you that as far as viewing, we're not going to do anything there. So let's just delete that, as far as creating, let's delete that as well. But we are concerned with updating. We are also concerned with deleting even though we haven't implemented anything yet, we are at least going to have some code here. So, what we want to do is check to see if the user is an admin or an editor. If they are, then we'll just return true immediately. So, let's do that, we'll say if and we have the user and the page passed here. So if the user isAdminOrEditor, and then we will return true. Otherwise, we need to check to see if the user created that page. So we'll say return $user and will get the id = $page, user_id. And that will be fun. And we essentially want to do the same thing for delete. Although you could make the argument that you wouldn't want anybody except admins or editors to delete pages. So, let's just leave it like this for now. Okay, so one thing that is common, well, there are several things common between these. But we are always checking to see if the user is an admin or an editor. So what we can do is define a method called before, so public function before. This is a method that's going to be called before any of these abilities are checked. And that makes it perfect for checking to see if the user is an admin or an editor. So we will have the user, will also have the ability past to the before method, the ability is going to be well, you'll see what that is. So an admin or an editor is always going to have the ability to do whatever is specified. So, we are simply going to return true in this case, that is going to simplify the rest of our methods. Because then all we have to do is check to see if the user created that post or that page rather. So this before method, is going to execute before any ability is checked if it returns true, then there is no other check involved with that ability. So then we need to register this policy before we can use it inside of our controller. So, we register that inside of a providers, and then the AuthServiceProvider. We need to add an item to our policies, so we're gonna say App\Page, and then we specify what that policy is App\Policies\PagePolicy. So now that we have registered our policy, we can use it inside of our controller. And we do it like this. So, as far as the edit method that is displaying the form, and the update, which is actually performing the update. We want to check to see if the user has the ability to update the provided page. So we will say if (Auth::user) and then there's a method called can(). So if the user can update the given page, then we want to return the view, just like that. Otherwise, we want to do something else. But in this case, what we can do is this, we have this can method. We also have this can't method. So if the user can't update the page, well then we want to do something. If we don't do anything, then all we are going to see as an empty string. So in this case, we can simply just redirect to our index. So we'll say routes('pages.index') and that will be fine. So that if the user does not have the ability to edit the post, then we just send them back to the index and we're good to go. Otherwise, the user gets to see the Edit form. And we will do the same thing inside of the update method, so let's just go ahead and add that. And we will do the same thing for destroy, even though we don't have anything implemented for destroying a page. So this ability is what is passed to the before method. So the ability in this case would be update, or it would be delete, and instead of a destroy, we need to change that to delete. So if we wanted to have our own custom ability, we will just have to write a public function ownCustomAbility(User $user,Page $page), and so on and so forth. And then we would just use our own custom ability with the can or the can't method. So, now let's go to, well, we need to fire up artisan once again. And then we will go to the browser, let's refresh this page here, Joe did not create this. So we should be automatically redirecting it back to the index, and we are. And if we try that with anything else, we have that second page, no Joe does not have that ability, so here we are, redirect it back. But if we go to our own page, then we can see that we can edit it, and we could also delete it if we had that implemented. But let's also just check the admin as well. So let's log in as admin, @admin.com, password and we need to add some links so that we don't have to keep doing this. So admin pages, so we can see all of our pages if we go to about we can of course see that. If we go to Joe's pages, we can see those and we would be able to edit them as well. So in this lesson, we secured our pages controller. We reused the admin middleware that we wrote a few lessons ago. And we also wrote a policy for our page model. And using that policy, we were able to check if the user was an admin or an editor, or if the user created that page. And then by using the can or can't methods inside of the controller, we were able to control whether or not the user could perform that ability.