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, 45:12
3.Managing Pages6 lessons, 1:12:31
4.User Management2 lessons, 27:40
5.Managing the Blog3 lessons, 42:25
6.Adding Extras2 lessons, 25:48
7.Implementing the Front-End3 lessons, 32:10
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 PagesController, 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 you're own. At the end of this course,with 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 is 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 PagesController. 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 log in. So that's one approach. Another approach is to do it inside of the controller. So let's do that. Let's go to our Admin, PagesController, 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 log in, 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 want 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 a 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) and then user, and then 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 will be that. Otherwise, we just get the pages from the user. So we will say Auth::user. We will use our pages and 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 joes-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 admin's 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, we'll say isAdminOrEditor. And we will simply return this, and we will call hasAnyRole, pass in 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 could 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, blahblitty, blah, blah, blah, 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 is what it is. So what we're going to do is go to the command line. And we are going to say, php artisan make:policy. And we are going to call this a PagePolicy. And we also get to specify the model as well. We don't have to. But since we are 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 is admin or editor, 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 we'll get id == the $page->user_id, and that will be fine. 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 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, we'll also have the ability passed 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 will 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 our providers, and then the off-service provider. We need to add an item to our policies. So we're going to 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, and then Auth::user, and then there is 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 is an empty string. So in this case, we can simply just redirect to our index. So we'll say route and then 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 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 inside of destroy, we need to change that to delete. So if we wanted to have our own custom ability, we would just have to write a public function ownCustomAbility, and then that would be the $user and the Page and so on and so forth. And then we would just use our ownCustomAbility 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 redirected 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 redirected 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 email@example.com. Well, 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 PagesController. 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.